feat: add ai_providers table, queries, dbauthz, audit, RBAC (#24892)

This commit is contained in:
Danny Kopping
2026-05-14 16:10:46 +02:00
committed by GitHub
parent acf57b3b35
commit 841b777ccd
43 changed files with 1960 additions and 232 deletions
+3 -3
View File
@@ -44,7 +44,7 @@ func newAIBridgeDaemon(coderAPI *coderd.API, providers []aibridge.Provider) (*ai
return srv, nil
}
// buildProviders constructs the list of aibridge providers from config.
// buildProviders constructs the list of AI providers from config.
// It merges legacy single-provider env vars and indexed provider configs:
// 1. Legacy providers (from CODER_AIBRIDGE_OPENAI_KEY, etc.) are added first.
// If a legacy name conflicts with an indexed provider, startup fails with
@@ -171,9 +171,9 @@ func buildProviders(cfg codersdk.AIBridgeConfig) ([]aibridge.Provider, error) {
}
// bedrockConfigFromProvider converts Bedrock fields from an indexed
// AIBridgeProviderConfig into an aibridge AWSBedrockConfig.
// AIProviderConfig into an aibridge AWSBedrockConfig.
// Returns nil if no Bedrock fields are set.
func bedrockConfigFromProvider(p codersdk.AIBridgeProviderConfig) *aibridge.AWSBedrockConfig {
func bedrockConfigFromProvider(p codersdk.AIProviderConfig) *aibridge.AWSBedrockConfig {
if p.BedrockRegion == "" && p.BedrockBaseURL == "" && len(p.BedrockAccessKeys) == 0 && len(p.BedrockAccessKeySecrets) == 0 {
return nil
}
+10 -10
View File
@@ -42,7 +42,7 @@ func TestBuildProviders(t *testing.T) {
t.Run("IndexedOnly", func(t *testing.T) {
t.Parallel()
cfg := codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{
Type: aibridge.ProviderAnthropic,
Name: "anthropic-zdr",
@@ -71,7 +71,7 @@ func TestBuildProviders(t *testing.T) {
t.Run("LegacyOpenAIConflictsWithIndexed", func(t *testing.T) {
t.Parallel()
cfg := codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderOpenAI, Name: aibridge.ProviderOpenAI, Keys: []string{"sk-indexed"}},
},
}
@@ -85,7 +85,7 @@ func TestBuildProviders(t *testing.T) {
t.Run("LegacyAnthropicConflictsWithIndexed", func(t *testing.T) {
t.Parallel()
cfg := codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderAnthropic, Name: aibridge.ProviderAnthropic, Keys: []string{"sk-indexed"}},
},
}
@@ -99,7 +99,7 @@ func TestBuildProviders(t *testing.T) {
t.Run("MixedLegacyAndIndexed", func(t *testing.T) {
t.Parallel()
cfg := codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderAnthropic, Name: "anthropic-zdr", Keys: []string{"sk-zdr"}},
},
}
@@ -151,7 +151,7 @@ func TestBuildProviders(t *testing.T) {
t.Run("UnknownType", func(t *testing.T) {
t.Parallel()
cfg := codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: "gemini", Name: "gemini-pro"},
},
}
@@ -166,7 +166,7 @@ func TestBuildProviders(t *testing.T) {
// Copilot providers can target any of the three GitHub
// Copilot API hosts via an explicit BASE_URL.
cfg := codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderCopilot, Name: aibridge.ProviderCopilot, DumpDir: "/tmp/copilot-dump"},
{Type: aibridge.ProviderCopilot, Name: agplaibridge.ProviderCopilotBusiness, BaseURL: "https://" + agplaibridge.HostCopilotBusiness},
{Type: aibridge.ProviderCopilot, Name: agplaibridge.ProviderCopilotEnterprise, BaseURL: "https://" + agplaibridge.HostCopilotEnterprise},
@@ -190,7 +190,7 @@ func TestBuildProviders(t *testing.T) {
// ChatGPT is an OpenAI-compatible provider with a custom
// base URL. Admins configure it as an indexed openai provider.
cfg := codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderOpenAI, Name: agplaibridge.ProviderChatGPT, BaseURL: agplaibridge.BaseURLChatGPT},
},
}
@@ -219,7 +219,7 @@ func TestDomainsFromProviders(t *testing.T) {
t.Parallel()
providers, err := buildProviders(codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderOpenAI, Name: "openai", Keys: []string{"k"}},
{Type: aibridge.ProviderAnthropic, Name: "anthropic", Keys: []string{"k"}},
{Type: aibridge.ProviderOpenAI, Name: "custom", Keys: []string{"k"}, BaseURL: "https://custom-llm.example.com:8443/api"},
@@ -243,7 +243,7 @@ func TestDomainsFromProviders(t *testing.T) {
t.Parallel()
providers, err := buildProviders(codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderOpenAI, Name: "first", Keys: []string{"k"}, BaseURL: "https://api.example.com/v1"},
{Type: aibridge.ProviderOpenAI, Name: "second", Keys: []string{"k"}, BaseURL: "https://api.example.com/v2"},
},
@@ -268,7 +268,7 @@ func TestDomainsFromProviders(t *testing.T) {
t.Parallel()
providers, err := buildProviders(codersdk.AIBridgeConfig{
Providers: []codersdk.AIBridgeProviderConfig{
Providers: []codersdk.AIProviderConfig{
{Type: aibridge.ProviderOpenAI, Name: "provider", Keys: []string{"k"}, BaseURL: "https://API.Example.COM/v1"},
},
})
+1 -1
View File
@@ -168,7 +168,7 @@ func (r *RootCmd) Server(_ func()) *serpent.Command {
if bridgeEnabled || proxyEnabled {
providers, err := buildProviders(options.DeploymentValues.AI.BridgeConfig)
if err != nil {
return nil, nil, xerrors.Errorf("build aibridge providers: %w", err)
return nil, nil, xerrors.Errorf("build AI providers: %w", err)
}
// In-memory aibridge daemon.