feat: use AI provider chat APIs (#25415)

This commit is contained in:
Michael Suchacz
2026-05-22 07:53:23 +02:00
committed by GitHub
parent 10efde3e6c
commit 06526a5822
41 changed files with 2195 additions and 1126 deletions
+7 -7
View File
@@ -350,13 +350,13 @@ func (p *Server) resolveAdvisorModelOverride(
return fallbackModel, fallbackCallConfig
}
// GetEnabledChatModelConfigByID joins on chat_providers.enabled = TRUE
// and chat_model_configs.enabled = TRUE, so it returns sql.ErrNoRows
// the moment an admin disables either the model config or its provider.
// Using the cached ModelConfigByID here would keep resolving an override
// whose provider was just disabled, and an env or central fallback key
// would let ModelFromConfig succeed, silently routing advisor prompts
// to a provider the admin expects to be off.
// GetEnabledChatModelConfigByID checks the model config and referenced
// provider enabled state, so it returns sql.ErrNoRows the moment an
// admin disables either one. Using the cached ModelConfigByID here
// would keep resolving an override whose provider was just disabled,
// and an available fallback key would let ModelFromConfig succeed,
// silently routing advisor prompts to a provider the admin expects to
// be off.
overrideConfig, err := p.db.GetEnabledChatModelConfigByID(
ctx,
advisorCfg.ModelConfigID,
+7 -97
View File
@@ -288,22 +288,7 @@ func TestSubagentChatExcludesWorkspaceProvisioningTools(t *testing.T) {
)
})
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai-compat",
APIKey: "test-api-key",
BaseURL: openAIURL,
})
require.NoError(t, err)
contextLimit := int64(4096)
isDefault := true
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai-compat",
Model: "gpt-4o-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
})
require.NoError(t, err)
coderdtest.CreateOpenAICompatChatModelConfig(t, expClient, openAIURL)
// Create a root chat whose first model call will spawn a subagent.
chat, err := expClient.CreateChat(ctx, codersdk.CreateChatRequest{
@@ -483,22 +468,7 @@ func TestPlanModeSubagentChatExcludesAskUserQuestion(t *testing.T) {
)
})
_, err = expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai-compat",
APIKey: "test-api-key",
BaseURL: openAIURL,
})
require.NoError(t, err)
contextLimit := int64(4096)
isDefault := true
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai-compat",
Model: "gpt-4o-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
})
require.NoError(t, err)
coderdtest.CreateOpenAICompatChatModelConfig(t, expClient, openAIURL)
chat, err := expClient.CreateChat(ctx, codersdk.CreateChatRequest{
OrganizationID: user.OrganizationID,
@@ -638,24 +608,9 @@ func TestExploreSubagentIsReadOnly(t *testing.T) {
)
})
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai-compat",
APIKey: "test-api-key",
BaseURL: openAIURL,
})
require.NoError(t, err)
coderdtest.CreateOpenAICompatChatModelConfig(t, expClient, openAIURL)
contextLimit := int64(4096)
isDefault := true
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai-compat",
Model: "gpt-4o-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
})
require.NoError(t, err)
_, err = expClient.CreateChat(ctx, codersdk.CreateChatRequest{
_, err := expClient.CreateChat(ctx, codersdk.CreateChatRequest{
OrganizationID: user.OrganizationID,
WorkspaceID: &workspace.ID,
Content: []codersdk.ChatInputPart{
@@ -4953,22 +4908,7 @@ func TestCreateWorkspaceTool_EndToEnd(t *testing.T) {
)
})
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai-compat",
APIKey: "test-api-key",
BaseURL: openAIURL,
})
require.NoError(t, err)
contextLimit := int64(4096)
isDefault := true
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai-compat",
Model: "gpt-4o-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
})
require.NoError(t, err)
coderdtest.CreateOpenAICompatChatModelConfig(t, expClient, openAIURL)
chat, err := expClient.CreateChat(ctx, codersdk.CreateChatRequest{
OrganizationID: user.OrganizationID,
@@ -5123,22 +5063,7 @@ func TestStartWorkspaceTool_EndToEnd(t *testing.T) {
)
})
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai-compat",
APIKey: "test-api-key",
BaseURL: openAIURL,
})
require.NoError(t, err)
contextLimit := int64(4096)
isDefault := true
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai-compat",
Model: "gpt-4o-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
})
require.NoError(t, err)
coderdtest.CreateOpenAICompatChatModelConfig(t, expClient, openAIURL)
// Create a chat with the stopped workspace pre-associated.
chat, err := expClient.CreateChat(ctx, codersdk.CreateChatRequest{
@@ -8586,22 +8511,7 @@ func TestAgentContextFilesAndSkillsLoadedIntoChat(t *testing.T) {
)
})
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai-compat",
APIKey: "test-api-key",
BaseURL: openAIURL,
})
require.NoError(t, err)
contextLimit := int64(4096)
isDefault := true
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai-compat",
Model: "gpt-4o-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
})
require.NoError(t, err)
coderdtest.CreateOpenAICompatChatModelConfig(t, expClient, openAIURL)
workspaceID := workspace.ID
chat, err := expClient.CreateChat(ctx, codersdk.CreateChatRequest{
+65 -27
View File
@@ -5,6 +5,7 @@ import (
"os"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/coderdtest"
@@ -13,6 +14,52 @@ import (
"github.com/coder/coder/v2/testutil"
)
func createIntegrationAIProvider(
ctx context.Context,
t testing.TB,
client *codersdk.ExperimentalClient,
providerType codersdk.AIProviderType,
apiKey string,
baseURL string,
) codersdk.AIProvider {
t.Helper()
if baseURL == "" {
baseURL = defaultIntegrationAIProviderBaseURL(providerType)
}
provider, err := client.CreateAIProvider(ctx, codersdk.CreateAIProviderRequest{
Type: providerType,
Name: string(providerType) + "-" + uuid.NewString(),
DisplayName: aiProviderDisplayName(providerType),
Enabled: true,
BaseURL: baseURL,
APIKeys: []string{apiKey},
})
require.NoError(t, err)
return provider
}
func defaultIntegrationAIProviderBaseURL(providerType codersdk.AIProviderType) string {
switch providerType {
case codersdk.AIProviderTypeAnthropic:
return "https://api.anthropic.com"
case codersdk.AIProviderTypeOpenAI:
return "https://api.openai.com/v1"
default:
return "https://api.example.com"
}
}
func aiProviderDisplayName(providerType codersdk.AIProviderType) string {
switch providerType {
case codersdk.AIProviderTypeAnthropic:
return "Anthropic"
case codersdk.AIProviderTypeOpenAI:
return "OpenAI"
default:
return string(providerType)
}
}
// TestAnthropicWebSearchRoundTrip is an integration test that verifies
// provider-executed tool results (web_search) survive the full
// persist → reconstruct → re-send cycle. It sends a query that
@@ -43,19 +90,16 @@ func TestAnthropicWebSearchRoundTrip(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client)
expClient := codersdk.NewExperimentalClient(client)
// Configure an Anthropic provider with the real API key.
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "anthropic",
APIKey: apiKey,
BaseURL: baseURL,
})
require.NoError(t, err)
provider := createIntegrationAIProvider(
ctx, t, expClient, codersdk.AIProviderTypeAnthropic, apiKey, baseURL,
)
// Create a model config that enables web_search.
contextLimit := int64(200000)
isDefault := true
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "anthropic",
_, err := expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: string(provider.Type),
AIProviderID: &provider.ID,
Model: "claude-sonnet-4-20250514",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
@@ -303,13 +347,9 @@ func TestOpenAIReasoningRoundTrip(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client)
expClient := codersdk.NewExperimentalClient(client)
// Configure an OpenAI provider with the real API key.
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai",
APIKey: apiKey,
BaseURL: baseURL,
})
require.NoError(t, err)
provider := createIntegrationAIProvider(
ctx, t, expClient, codersdk.AIProviderTypeOpenAI, apiKey, baseURL,
)
// Create a model config for a reasoning model with Store: true
// (the default). Using o4-mini because it always produces
@@ -317,8 +357,9 @@ func TestOpenAIReasoningRoundTrip(t *testing.T) {
contextLimit := int64(200000)
isDefault := true
reasoningSummary := "auto"
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai",
_, err := expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: string(provider.Type),
AIProviderID: &provider.ID,
Model: "o4-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,
@@ -457,21 +498,18 @@ func TestOpenAIReasoningRoundTripStoreFalse(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client)
expClient := codersdk.NewExperimentalClient(client)
// Configure an OpenAI provider with the real API key.
_, err := expClient.CreateChatProvider(ctx, codersdk.CreateChatProviderConfigRequest{
Provider: "openai",
APIKey: apiKey,
BaseURL: baseURL,
})
require.NoError(t, err)
provider := createIntegrationAIProvider(
ctx, t, expClient, codersdk.AIProviderTypeOpenAI, apiKey, baseURL,
)
// Create a model config for a reasoning model with Store: false.
// Using o4-mini because it always produces reasoning items.
contextLimit := int64(200000)
isDefault := true
reasoningSummary := "auto"
_, err = expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: "openai",
_, err := expClient.CreateChatModelConfig(ctx, codersdk.CreateChatModelConfigRequest{
Provider: string(provider.Type),
AIProviderID: &provider.ID,
Model: "o4-mini",
ContextLimit: &contextLimit,
IsDefault: &isDefault,