Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2979,23 +2979,34 @@ func parseExternalAuthProvidersFromEnv(prefix string, environ []string) ([]coder
return providers, nil
}

const (
aiGatewayProviderEnvPrefix = "CODER_AI_GATEWAY_PROVIDER_"
aiBridgeProviderEnvPrefix = "CODER_AIBRIDGE_PROVIDER_"
)

// ReadAIProvidersFromEnv parses CODER_AI_GATEWAY_PROVIDER_<N>_<KEY>
// environment variables into a slice of AIProviderConfig.
// Deprecated alias env vars with the CODER_AIBRIDGE_PROVIDER_<N>_<KEY>
// prefix are also accepted for compatibility. Prefixes are mutually exclusive.
//
// This follows the same indexed pattern as ReadExternalAuthProvidersFromEnv.
func ReadAIProvidersFromEnv(logger slog.Logger, environ []string) ([]codersdk.AIProviderConfig, error) {
providers, err := readAIProvidersForPrefix(logger, environ, "CODER_AIBRIDGE_PROVIDER_")
providers, err := readAIProvidersForPrefix(logger, environ, aiBridgeProviderEnvPrefix)
if err != nil {
return nil, err
}
gatewayProviders, err := readAIProvidersForPrefix(logger, environ, "CODER_AI_GATEWAY_PROVIDER_")
gatewayProviders, err := readAIProvidersForPrefix(logger, environ, aiGatewayProviderEnvPrefix)
if err != nil {
return nil, err
}
if len(providers) > 0 && len(gatewayProviders) > 0 {
return nil, xerrors.New("cannot mix CODER_AIBRIDGE_PROVIDER_* and CODER_AI_GATEWAY_PROVIDER_* environment variables, please consolidate onto CODER_AI_GATEWAY_PROVIDER_*")
return nil, xerrors.Errorf("cannot mix %s* and %s* environment variables, please consolidate onto %s*", aiBridgeProviderEnvPrefix, aiGatewayProviderEnvPrefix, aiGatewayProviderEnvPrefix)
}
var activePrefix string
if len(providers) > 0 {
activePrefix = aiBridgeProviderEnvPrefix
} else if len(gatewayProviders) > 0 {
activePrefix = aiGatewayProviderEnvPrefix
}
providers = append(providers, gatewayProviders...)

Expand Down Expand Up @@ -3077,9 +3088,27 @@ func ReadAIProvidersFromEnv(logger slog.Logger, environ []string) ([]codersdk.AI
names[p.Name] = i
}

warnIfAIProvidersConfiguredFromEnv(context.Background(), logger, activePrefix, providers)

return providers, nil
}

func warnIfAIProvidersConfiguredFromEnv(ctx context.Context, logger slog.Logger, prefix string, providers []codersdk.AIProviderConfig) {
if len(providers) == 0 {
return
}

if prefix == "" {
return
}

logger.Warn(ctx,
"ai provider environment variables are deprecated for provider management and only seed provider configuration at startup",
slog.F("env_prefix", prefix),
slog.F("replacement", "Manage AI Providers from the Coder UI or HTTP API."),
)
}

// readAIProvidersForPrefix parses provider env vars under a single
// indexed prefix (e.g. CODER_AI_GATEWAY_PROVIDER_) into a slice of
// AIProviderConfig. Per-field syntax errors and unknown keys are
Expand Down
64 changes: 64 additions & 0 deletions cli/server_aibridge_internal_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"context"
"database/sql"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -575,6 +576,58 @@ func TestValidateLegacyAIBridgeConfig(t *testing.T) {
}
}

func TestWarnIfAIProvidersConfiguredFromEnv(t *testing.T) {
t.Parallel()

t.Run("NoProviders", func(t *testing.T) {
t.Parallel()

sink := testutil.NewFakeSink(t)
warnIfAIProvidersConfiguredFromEnv(context.Background(), sink.Logger(), aiGatewayProviderEnvPrefix, nil)

require.Empty(t, sink.Entries())
})

t.Run("EmptyPrefix", func(t *testing.T) {
t.Parallel()

sink := testutil.NewFakeSink(t)
warnIfAIProvidersConfiguredFromEnv(context.Background(), sink.Logger(), "", []codersdk.AIProviderConfig{{Type: "openai", Name: "openai"}})

require.Empty(t, sink.Entries())
})

t.Run("AIGatewayPrefix", func(t *testing.T) {
t.Parallel()

sink := testutil.NewFakeSink(t)
warnIfAIProvidersConfiguredFromEnv(context.Background(), sink.Logger(), aiGatewayProviderEnvPrefix, []codersdk.AIProviderConfig{{Type: "openai", Name: "openai"}})

entries := sink.Entries(func(e slog.SinkEntry) bool {
return e.Message == "ai provider environment variables are deprecated for provider management and only seed provider configuration at startup"
})
require.Len(t, entries, 1)
require.Len(t, entries[0].Fields, 2)
assertFieldValue(t, entries[0].Fields, "env_prefix", aiGatewayProviderEnvPrefix)
assertFieldValue(t, entries[0].Fields, "replacement", "Manage AI Providers from the Coder UI or HTTP API.")
})

t.Run("AIBridgePrefix", func(t *testing.T) {
t.Parallel()

sink := testutil.NewFakeSink(t)
warnIfAIProvidersConfiguredFromEnv(context.Background(), sink.Logger(), aiBridgeProviderEnvPrefix, []codersdk.AIProviderConfig{{Type: "openai", Name: "openai"}})

entries := sink.Entries(func(e slog.SinkEntry) bool {
return e.Message == "ai provider environment variables are deprecated for provider management and only seed provider configuration at startup"
})
require.Len(t, entries, 1)
require.Len(t, entries[0].Fields, 2)
assertFieldValue(t, entries[0].Fields, "env_prefix", aiBridgeProviderEnvPrefix)
assertFieldValue(t, entries[0].Fields, "replacement", "Manage AI Providers from the Coder UI or HTTP API.")
})
}

func TestBuildAIProviderFromRowSetsAPIDumpDir(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -721,3 +774,14 @@ func mustMarshalSettings(s codersdk.AIProviderSettings) sql.NullString {
}
return sql.NullString{String: string(data), Valid: true}
}

func assertFieldValue(t *testing.T, fields slog.Map, name string, expected interface{}) {
t.Helper()
for _, f := range fields {
if f.Name == name {
assert.Equal(t, expected, f.Value)
return
}
}
t.Errorf("field %q not found", name)
}
60 changes: 43 additions & 17 deletions cli/testdata/coder_server_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -124,35 +124,55 @@ AI GATEWAY OPTIONS:
disabled, only centralized key authentication is permitted.

--ai-gateway-anthropic-base-url string, $CODER_AI_GATEWAY_ANTHROPIC_BASE_URL (default: https://api.anthropic.com/)
The base URL of the Anthropic API.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The base URL of the Anthropic
API.

--ai-gateway-anthropic-key string, $CODER_AI_GATEWAY_ANTHROPIC_KEY
The key to authenticate against the Anthropic API.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The key to authenticate
against the Anthropic API.

--ai-gateway-bedrock-access-key string, $CODER_AI_GATEWAY_BEDROCK_ACCESS_KEY
The access key to authenticate against the AWS Bedrock API.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The access key to authenticate
against the AWS Bedrock API.

--ai-gateway-bedrock-access-key-secret string, $CODER_AI_GATEWAY_BEDROCK_ACCESS_KEY_SECRET
The access key secret to use with the access key to authenticate
against the AWS Bedrock API.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The access key secret to use
with the access key to authenticate against the AWS Bedrock API.

--ai-gateway-bedrock-base-url string, $CODER_AI_GATEWAY_BEDROCK_BASE_URL
The base URL to use for the AWS Bedrock API. Use this setting to
specify an exact URL to use. Takes precedence over
CODER_AI_GATEWAY_BEDROCK_REGION.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The base URL to use for the
AWS Bedrock API. Use this setting to specify an exact URL to use.
Takes precedence over CODER_AI_GATEWAY_BEDROCK_REGION.

--ai-gateway-bedrock-model string, $CODER_AI_GATEWAY_BEDROCK_MODEL (default: global.anthropic.claude-sonnet-4-5-20250929-v1:0)
The model to use when making requests to the AWS Bedrock API.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The model to use when making
requests to the AWS Bedrock API.

--ai-gateway-bedrock-region string, $CODER_AI_GATEWAY_BEDROCK_REGION
The AWS Bedrock API region to use. Constructs a base URL to use for
the AWS Bedrock API in the form of
'https://bedrock-runtime.<region>.amazonaws.com'.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The AWS Bedrock API region to
use. Constructs a base URL to use for the AWS Bedrock API in the form
of 'https://bedrock-runtime.<region>.amazonaws.com'.

--ai-gateway-bedrock-small-fastmodel string, $CODER_AI_GATEWAY_BEDROCK_SMALL_FAST_MODEL (default: global.anthropic.claude-haiku-4-5-20251001-v1:0)
The small fast model to use when making requests to the AWS Bedrock
API. Claude Code uses Haiku-class models to perform background tasks.
See
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The small fast model to use
when making requests to the AWS Bedrock API. Claude Code uses
Haiku-class models to perform background tasks. See
https://docs.claude.com/en/docs/claude-code/settings#environment-variables.

--ai-gateway-circuit-breaker-enabled bool, $CODER_AI_GATEWAY_CIRCUIT_BREAKER_ENABLED (default: false)
Expand All @@ -171,10 +191,16 @@ AI GATEWAY OPTIONS:
to disable (unlimited).

--ai-gateway-openai-base-url string, $CODER_AI_GATEWAY_OPENAI_BASE_URL (default: https://api.openai.com/v1/)
The base URL of the OpenAI API.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The base URL of the OpenAI
API.

--ai-gateway-openai-key string, $CODER_AI_GATEWAY_OPENAI_KEY
The key to authenticate against the OpenAI API.
Deprecated: manage AI Providers from the Coder UI or HTTP API. If set,
this option seeds provider configuration at startup only exactly once.
It will not be used in service runtime. The key to authenticate
against the OpenAI API.

--ai-gateway-rate-limit int, $CODER_AI_GATEWAY_RATE_LIMIT (default: 0)
Maximum number of AI Gateway requests per second per replica. Set to 0
Expand Down
34 changes: 25 additions & 9 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -874,25 +874,41 @@ ai_gateway:
# Whether to start an in-memory AI Gateway instance.
# (default: true, type: bool)
enabled: true
# The base URL of the OpenAI API.
# Deprecated: manage AI Providers from the Coder UI or HTTP API. If set, this
# option seeds provider configuration at startup only exactly once. It will not be
# used in service runtime. The base URL of the OpenAI API.
# (default: https://api.openai.com/v1/, type: string)
openai_base_url: https://api.openai.com/v1/
# The base URL of the Anthropic API.
# Deprecated: manage AI Providers from the Coder UI or HTTP API. If set, this
# option seeds provider configuration at startup only exactly once. It will not be
# used in service runtime. The base URL of the Anthropic API.
# (default: https://api.anthropic.com/, type: string)
anthropic_base_url: https://api.anthropic.com/
# The base URL to use for the AWS Bedrock API. Use this setting to specify an
# exact URL to use. Takes precedence over CODER_AI_GATEWAY_BEDROCK_REGION.
# Deprecated: manage AI Providers from the Coder UI or HTTP API. If set, this
# option seeds provider configuration at startup only exactly once. It will not be
# used in service runtime. The base URL to use for the AWS Bedrock API. Use this
# setting to specify an exact URL to use. Takes precedence over
# CODER_AI_GATEWAY_BEDROCK_REGION.
# (default: <unset>, type: string)
bedrock_base_url: ""
# The AWS Bedrock API region to use. Constructs a base URL to use for the AWS
# Bedrock API in the form of 'https://bedrock-runtime.<region>.amazonaws.com'.
# Deprecated: manage AI Providers from the Coder UI or HTTP API. If set, this
# option seeds provider configuration at startup only exactly once. It will not be
# used in service runtime. The AWS Bedrock API region to use. Constructs a base
# URL to use for the AWS Bedrock API in the form of
# 'https://bedrock-runtime.<region>.amazonaws.com'.
# (default: <unset>, type: string)
bedrock_region: ""
# The model to use when making requests to the AWS Bedrock API.
# Deprecated: manage AI Providers from the Coder UI or HTTP API. If set, this
# option seeds provider configuration at startup only exactly once. It will not be
# used in service runtime. The model to use when making requests to the AWS
# Bedrock API.
# (default: global.anthropic.claude-sonnet-4-5-20250929-v1:0, type: string)
bedrock_model: global.anthropic.claude-sonnet-4-5-20250929-v1:0
# The small fast model to use when making requests to the AWS Bedrock API. Claude
# Code uses Haiku-class models to perform background tasks. See
# Deprecated: manage AI Providers from the Coder UI or HTTP API. If set, this
# option seeds provider configuration at startup only exactly once. It will not be
# used in service runtime. The small fast model to use when making requests to the
# AWS Bedrock API. Claude Code uses Haiku-class models to perform background
# tasks. See
# https://docs.claude.com/en/docs/claude-code/settings#environment-variables.
# (default: global.anthropic.claude-haiku-4-5-20251001-v1:0, type: string)
bedrock_small_fast_model: global.anthropic.claude-haiku-4-5-20251001-v1:0
Expand Down
Loading
Loading