providersFromEnv normalizes the deployment-values AI Bridge config (legacy single-provider env vars and indexed CODER_AIBRIDGE_PROVIDER_ _* env vars) into the deduplicated set of providers we want present in the database. Conflicts between legacy and indexed providers under the same canonical name
(ctx context.Context, cfg codersdk.AIBridgeConfig, logger slog.Logger)
| 297 | // the database. Conflicts between legacy and indexed providers under |
| 298 | // the same canonical name are surfaced as errors. |
| 299 | func providersFromEnv(ctx context.Context, cfg codersdk.AIBridgeConfig, logger slog.Logger) ([]desiredAIProvider, error) { |
| 300 | out := make(map[string]desiredAIProvider) |
| 301 | legacyNames := make(map[string]bool) |
| 302 | |
| 303 | addLegacy := func(name string, p desiredAIProvider) { |
| 304 | out[name] = p |
| 305 | legacyNames[name] = true |
| 306 | } |
| 307 | |
| 308 | // Legacy OpenAI. |
| 309 | if cfg.LegacyOpenAI.Key.String() != "" { |
| 310 | dp := desiredAIProvider{ |
| 311 | Name: aibridge.ProviderOpenAI, |
| 312 | Type: database.AiProviderTypeOpenai, |
| 313 | BaseURL: cfg.LegacyOpenAI.BaseURL.String(), |
| 314 | Keys: []string{cfg.LegacyOpenAI.Key.String()}, |
| 315 | } |
| 316 | dp.Hash = computeProviderHash(dp.canonical()) |
| 317 | addLegacy(aibridge.ProviderOpenAI, dp) |
| 318 | } |
| 319 | |
| 320 | // Legacy Anthropic + Bedrock. Anthropic is enabled if either an |
| 321 | // Anthropic key OR any Bedrock setting is explicitly configured. |
| 322 | // Detection goes through AIProviderBedrockSettings.IsConfigured() |
| 323 | // so the legacy and indexed paths agree on what counts as a |
| 324 | // Bedrock provider. |
| 325 | bedrock := codersdk.NewAIProviderBedrockSettings( |
| 326 | cfg.LegacyBedrock.Region.String(), |
| 327 | cfg.LegacyBedrock.AccessKey.String(), |
| 328 | cfg.LegacyBedrock.AccessKeySecret.String(), |
| 329 | cfg.LegacyBedrock.Model.String(), |
| 330 | cfg.LegacyBedrock.SmallFastModel.String(), |
| 331 | ) |
| 332 | hasAnthropicKey := cfg.LegacyAnthropic.Key.String() != "" |
| 333 | hasLegacyBedrock := codersdk.IsBedrockConfigured(cfg.LegacyBedrock.BaseURL.String(), bedrock) |
| 334 | if hasAnthropicKey || hasLegacyBedrock { |
| 335 | dp := desiredAIProvider{ |
| 336 | Name: aibridge.ProviderAnthropic, |
| 337 | Type: database.AiProviderTypeAnthropic, |
| 338 | } |
| 339 | if hasLegacyBedrock { |
| 340 | dp.Type = database.AiProviderTypeBedrock |
| 341 | if hasAnthropicKey { |
| 342 | logger.Warn(ctx, "ignoring legacy Anthropic API key because Bedrock credentials are configured; Bedrock authenticates via access keys or credential chain", |
| 343 | slog.F("provider", aibridge.ProviderAnthropic), |
| 344 | ) |
| 345 | } |
| 346 | // Bedrock-only deployments use CODER_AIBRIDGE_BEDROCK_BASE_URL |
| 347 | // for custom VPC, FIPS, or proxy endpoints. |
| 348 | dp.BaseURL = cfg.LegacyBedrock.BaseURL.String() |
| 349 | dp.Bedrock = &bedrock |
| 350 | } else { |
| 351 | dp.BaseURL = cfg.LegacyAnthropic.BaseURL.String() |
| 352 | dp.Keys = []string{cfg.LegacyAnthropic.Key.String()} |
| 353 | } |
| 354 | dp.Hash = computeProviderHash(dp.canonical()) |
| 355 | addLegacy(aibridge.ProviderAnthropic, dp) |
| 356 | } |
no test coverage detected