refactor: load AI providers from the database at startup (#25672)

Replace the env-based `BuildProviders` with a DB-backed loader. The database is now the single source of truth for runtime provider configuration; env config arrives via `SeedAIProvidersFromEnv` (run at boot) and `BuildProviders` reads it back as `aibridge.Provider` instances. `cli/server.go` and `enterprise/cli/server.go` both call the same path, so aibridged and aibridgeproxyd see the same provider set.

Per-provider `DumpDir` is replaced by a top-level `CODER_AI_GATEWAY_DUMP_DIR` base; each provider's effective dump path is `<base>/<provider name>`.
This commit is contained in:
Danny Kopping
2026-05-26 15:57:01 +02:00
committed by GitHub
parent dfd7ca3b98
commit 282ab7de34
19 changed files with 570 additions and 258 deletions
+51 -2
View File
@@ -10,8 +10,10 @@ import (
"cdr.dev/slog/v3"
"cdr.dev/slog/v3/sloggers/slogtest"
"github.com/coder/coder/v2/aibridge"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
"github.com/coder/serpent"
)
func TestReadAIProvidersFromEnv(t *testing.T) {
@@ -34,7 +36,6 @@ func TestReadAIProvidersFromEnv(t *testing.T) {
"CODER_AIBRIDGE_PROVIDER_0_NAME=anthropic-zdr",
"CODER_AIBRIDGE_PROVIDER_0_KEY=sk-ant-xxx",
"CODER_AIBRIDGE_PROVIDER_0_BASE_URL=https://api.anthropic.com/",
"CODER_AIBRIDGE_PROVIDER_0_DUMP_DIR=/tmp/aibridge-dump",
},
expected: []codersdk.AIProviderConfig{
{
@@ -42,7 +43,6 @@ func TestReadAIProvidersFromEnv(t *testing.T) {
Name: "anthropic-zdr",
Keys: []string{"sk-ant-xxx"},
BaseURL: "https://api.anthropic.com/",
DumpDir: "/tmp/aibridge-dump",
},
},
},
@@ -537,3 +537,52 @@ func TestValidateLegacyAIBridgeConfig(t *testing.T) {
})
}
}
func TestBuildAIProviderFromRowSetsAPIDumpDir(t *testing.T) {
t.Parallel()
const dumpDir = "/tmp/coder-aibridge-dumps"
tests := []struct {
name string
row database.AIProvider
}{
{
name: "OpenAI",
row: database.AIProvider{
Type: database.AiProviderTypeOpenai,
Name: "openai",
BaseUrl: "https://api.openai.com/",
},
},
{
name: "Anthropic",
row: database.AIProvider{
Type: database.AiProviderTypeAnthropic,
Name: "anthropic",
BaseUrl: "https://api.anthropic.com/",
},
},
{
name: "Copilot",
row: database.AIProvider{
Type: database.AiProviderTypeCopilot,
Name: "copilot",
BaseUrl: "https://api.githubcopilot.com/",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
provider, err := buildAIProviderFromRow(tt.row, nil, codersdk.AIBridgeConfig{
AllowBYOK: serpent.Bool(true),
APIDumpDir: serpent.String(dumpDir),
})
require.NoError(t, err)
assert.Equal(t, dumpDir, provider.APIDumpDir())
})
}
}