mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: add ai_gateway options that alias aibridge options (#25061)
Adds options matching new AI Gateway naming. New options are added as alias for old options. Old options are still working. Old options have deprecated message. No conflict detection was added. Updated documentation so it mentions only new options. Added note about old options still working. > Various AI tools where used to create this PR
This commit is contained in:
+540
-83
@@ -1479,12 +1479,20 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
|
||||
YAML: "chat",
|
||||
Description: "Configure the background chat processing daemon.",
|
||||
}
|
||||
deploymentGroupAIGateway = serpent.Group{
|
||||
Name: "AI Gateway",
|
||||
YAML: "ai_gateway",
|
||||
}
|
||||
deploymentGroupAIGatewayProxy = serpent.Group{
|
||||
Name: "AI Gateway Proxy",
|
||||
YAML: "ai_gateway_proxy",
|
||||
}
|
||||
deploymentGroupAIBridge = serpent.Group{
|
||||
Name: "AI Bridge",
|
||||
Name: "AI Bridge (Deprecated)",
|
||||
YAML: "aibridge",
|
||||
}
|
||||
deploymentGroupAIBridgeProxy = serpent.Group{
|
||||
Name: "AI Bridge Proxy",
|
||||
Name: "AI Bridge Proxy (Deprecated)",
|
||||
YAML: "aibridgeproxy",
|
||||
}
|
||||
deploymentGroupRetention = serpent.Group{
|
||||
@@ -1689,6 +1697,369 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
|
||||
Hidden: false,
|
||||
Default: "coder",
|
||||
}
|
||||
|
||||
// AI Gateway options
|
||||
aiGatewayEnabled := serpent.Option{
|
||||
Name: "AI Gateway Enabled",
|
||||
Description: "Whether to start an in-memory AI Gateway instance.",
|
||||
Flag: "ai-gateway-enabled",
|
||||
Env: "CODER_AI_GATEWAY_ENABLED",
|
||||
Value: &c.AI.BridgeConfig.Enabled,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "enabled",
|
||||
}
|
||||
aiGatewayOpenAIBaseURL := serpent.Option{
|
||||
Name: "AI Gateway OpenAI Base URL",
|
||||
Description: "The base URL of the OpenAI API.",
|
||||
Flag: "ai-gateway-openai-base-url",
|
||||
Env: "CODER_AI_GATEWAY_OPENAI_BASE_URL",
|
||||
Value: &c.AI.BridgeConfig.LegacyOpenAI.BaseURL,
|
||||
Default: "https://api.openai.com/v1/",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "openai_base_url",
|
||||
}
|
||||
aiGatewayOpenAIKey := serpent.Option{
|
||||
Name: "AI Gateway OpenAI Key",
|
||||
Description: "The key to authenticate against the OpenAI API.",
|
||||
Flag: "ai-gateway-openai-key",
|
||||
Env: "CODER_AI_GATEWAY_OPENAI_KEY",
|
||||
Value: &c.AI.BridgeConfig.LegacyOpenAI.Key,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
}
|
||||
aiGatewayAnthropicBaseURL := serpent.Option{
|
||||
Name: "AI Gateway Anthropic Base URL",
|
||||
Description: "The base URL of the Anthropic API.",
|
||||
Flag: "ai-gateway-anthropic-base-url",
|
||||
Env: "CODER_AI_GATEWAY_ANTHROPIC_BASE_URL",
|
||||
Value: &c.AI.BridgeConfig.LegacyAnthropic.BaseURL,
|
||||
Default: "https://api.anthropic.com/",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "anthropic_base_url",
|
||||
}
|
||||
aiGatewayAnthropicKey := serpent.Option{
|
||||
Name: "AI Gateway Anthropic Key",
|
||||
Description: "The key to authenticate against the Anthropic API.",
|
||||
Flag: "ai-gateway-anthropic-key",
|
||||
Env: "CODER_AI_GATEWAY_ANTHROPIC_KEY",
|
||||
Value: &c.AI.BridgeConfig.LegacyAnthropic.Key,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
}
|
||||
aiGatewayBedrockBaseURL := serpent.Option{
|
||||
Name: "AI Gateway Bedrock Base URL",
|
||||
Description: "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.",
|
||||
Flag: "ai-gateway-bedrock-base-url",
|
||||
Env: "CODER_AI_GATEWAY_BEDROCK_BASE_URL",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.BaseURL,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "bedrock_base_url",
|
||||
}
|
||||
aiGatewayBedrockRegion := serpent.Option{
|
||||
Name: "AI Gateway Bedrock Region",
|
||||
Description: "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'.",
|
||||
Flag: "ai-gateway-bedrock-region",
|
||||
Env: "CODER_AI_GATEWAY_BEDROCK_REGION",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.Region,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "bedrock_region",
|
||||
}
|
||||
aiGatewayBedrockAccessKey := serpent.Option{
|
||||
Name: "AI Gateway Bedrock Access Key",
|
||||
Description: "The access key to authenticate against the AWS Bedrock API.",
|
||||
Flag: "ai-gateway-bedrock-access-key",
|
||||
Env: "CODER_AI_GATEWAY_BEDROCK_ACCESS_KEY",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.AccessKey,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
}
|
||||
aiGatewayBedrockAccessKeySecret := serpent.Option{
|
||||
Name: "AI Gateway Bedrock Access Key Secret",
|
||||
Description: "The access key secret to use with the access key to authenticate against the AWS Bedrock API.",
|
||||
Flag: "ai-gateway-bedrock-access-key-secret",
|
||||
Env: "CODER_AI_GATEWAY_BEDROCK_ACCESS_KEY_SECRET",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.AccessKeySecret,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
}
|
||||
aiGatewayBedrockModel := serpent.Option{
|
||||
Name: "AI Gateway Bedrock Model",
|
||||
Description: "The model to use when making requests to the AWS Bedrock API.",
|
||||
Flag: "ai-gateway-bedrock-model",
|
||||
Env: "CODER_AI_GATEWAY_BEDROCK_MODEL",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.Model,
|
||||
Default: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", // See https://docs.claude.com/en/api/claude-on-amazon-bedrock#accessing-bedrock.
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "bedrock_model",
|
||||
}
|
||||
aiGatewayBedrockSmallFastModel := serpent.Option{
|
||||
Name: "AI Gateway Bedrock Small Fast Model",
|
||||
Description: "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.",
|
||||
Flag: "ai-gateway-bedrock-small-fastmodel",
|
||||
Env: "CODER_AI_GATEWAY_BEDROCK_SMALL_FAST_MODEL",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.SmallFastModel,
|
||||
Default: "global.anthropic.claude-haiku-4-5-20251001-v1:0", // See https://docs.claude.com/en/api/claude-on-amazon-bedrock#accessing-bedrock.
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "bedrock_small_fast_model",
|
||||
}
|
||||
aiGatewayInjectCoderMCPTools := serpent.Option{
|
||||
Name: "AI Gateway Inject Coder MCP tools",
|
||||
Description: "Deprecated: Injected MCP in AI Gateway is deprecated and will be removed in a future release. Whether to inject Coder's MCP tools into intercepted AI Gateway requests (requires the \"oauth2\" and \"mcp-server-http\" experiments to be enabled).",
|
||||
Flag: "ai-gateway-inject-coder-mcp-tools",
|
||||
Env: "CODER_AI_GATEWAY_INJECT_CODER_MCP_TOOLS",
|
||||
Value: &c.AI.BridgeConfig.InjectCoderMCPTools,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "inject_coder_mcp_tools",
|
||||
Hidden: true,
|
||||
}
|
||||
aiGatewayRetention := serpent.Option{
|
||||
Name: "AI Gateway Data Retention Duration",
|
||||
Description: "Length of time to retain data such as interceptions and all related records (token, prompt, tool use).",
|
||||
Flag: "ai-gateway-retention",
|
||||
Env: "CODER_AI_GATEWAY_RETENTION",
|
||||
Value: &c.AI.BridgeConfig.Retention,
|
||||
Default: "60d",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "retention",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
|
||||
}
|
||||
aiGatewayMaxConcurrency := serpent.Option{
|
||||
Name: "AI Gateway Max Concurrency",
|
||||
Description: "Maximum number of concurrent AI Gateway requests per replica. Set to 0 to disable (unlimited).",
|
||||
Flag: "ai-gateway-max-concurrency",
|
||||
Env: "CODER_AI_GATEWAY_MAX_CONCURRENCY",
|
||||
Value: &c.AI.BridgeConfig.MaxConcurrency,
|
||||
Default: "0",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "max_concurrency",
|
||||
}
|
||||
aiGatewayRateLimit := serpent.Option{
|
||||
Name: "AI Gateway Rate Limit",
|
||||
Description: "Maximum number of AI Gateway requests per second per replica. Set to 0 to disable (unlimited).",
|
||||
Flag: "ai-gateway-rate-limit",
|
||||
Env: "CODER_AI_GATEWAY_RATE_LIMIT",
|
||||
Value: &c.AI.BridgeConfig.RateLimit,
|
||||
Default: "0",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "rate_limit",
|
||||
}
|
||||
aiGatewayStructuredLogging := serpent.Option{
|
||||
Name: "AI Gateway Structured Logging",
|
||||
Description: "Emit structured logs for AI Gateway interception records. Use this for exporting these records to external SIEM or observability systems.",
|
||||
Flag: "ai-gateway-structured-logging",
|
||||
Env: "CODER_AI_GATEWAY_STRUCTURED_LOGGING",
|
||||
Value: &c.AI.BridgeConfig.StructuredLogging,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "structured_logging",
|
||||
}
|
||||
aiGatewaySendActorHeaders := serpent.Option{
|
||||
Name: "AI Gateway Send Actor Headers",
|
||||
Description: "Once enabled, extra headers will be added to upstream requests to identify the user (actor) making requests to AI Gateway. " +
|
||||
"This is only needed if you are using a proxy between AI Gateway and an upstream AI provider. " +
|
||||
"This will send X-Ai-Bridge-Actor-Id (the ID of the user making the request) and X-Ai-Bridge-Actor-Metadata-Username (their username).",
|
||||
Flag: "ai-gateway-send-actor-headers",
|
||||
Env: "CODER_AI_GATEWAY_SEND_ACTOR_HEADERS",
|
||||
Value: &c.AI.BridgeConfig.SendActorHeaders,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "send_actor_headers",
|
||||
}
|
||||
aiGatewayAllowBYOK := serpent.Option{
|
||||
Name: "AI Gateway Allow BYOK",
|
||||
Description: "Allow users to provide their own LLM API keys or subscriptions. When disabled, only centralized key authentication is permitted.",
|
||||
Flag: "ai-gateway-allow-byok",
|
||||
Env: "CODER_AI_GATEWAY_ALLOW_BYOK",
|
||||
Value: &c.AI.BridgeConfig.AllowBYOK,
|
||||
Default: "true",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "allow_byok",
|
||||
}
|
||||
|
||||
// validateCircuitBreakerPercent is shared by AI Gateway circuit breaker options
|
||||
validateCircuitBreakerPercent := func(value *serpent.Int64) error {
|
||||
if value.Value() <= 0 || value.Value() > 100 {
|
||||
return xerrors.New("must be between 1 and 100")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
aiGatewayCircuitBreakerEnabled := serpent.Option{
|
||||
Name: "AI Gateway Circuit Breaker Enabled",
|
||||
Description: "Enable the circuit breaker to protect against cascading failures from upstream AI provider overload (503, 529).",
|
||||
Flag: "ai-gateway-circuit-breaker-enabled",
|
||||
Env: "CODER_AI_GATEWAY_CIRCUIT_BREAKER_ENABLED",
|
||||
Value: &c.AI.BridgeConfig.CircuitBreakerEnabled,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "circuit_breaker_enabled",
|
||||
}
|
||||
aiGatewayCircuitBreakerFailureThreshold := serpent.Option{
|
||||
Name: "AI Gateway Circuit Breaker Failure Threshold",
|
||||
Description: "Number of consecutive failures that triggers the circuit breaker to open.",
|
||||
Flag: "ai-gateway-circuit-breaker-failure-threshold",
|
||||
Env: "CODER_AI_GATEWAY_CIRCUIT_BREAKER_FAILURE_THRESHOLD",
|
||||
Value: serpent.Validate(&c.AI.BridgeConfig.CircuitBreakerFailureThreshold, validateCircuitBreakerPercent),
|
||||
Default: "5",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "circuit_breaker_failure_threshold",
|
||||
}
|
||||
aiGatewayCircuitBreakerInterval := serpent.Option{
|
||||
Name: "AI Gateway Circuit Breaker Interval",
|
||||
Description: "Cyclic period of the closed state for clearing internal failure counts.",
|
||||
Flag: "ai-gateway-circuit-breaker-interval",
|
||||
Env: "CODER_AI_GATEWAY_CIRCUIT_BREAKER_INTERVAL",
|
||||
Value: &c.AI.BridgeConfig.CircuitBreakerInterval,
|
||||
Default: "10s",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "circuit_breaker_interval",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
|
||||
}
|
||||
aiGatewayCircuitBreakerTimeout := serpent.Option{
|
||||
Name: "AI Gateway Circuit Breaker Timeout",
|
||||
Description: "How long the circuit breaker stays open before transitioning to half-open state.",
|
||||
Flag: "ai-gateway-circuit-breaker-timeout",
|
||||
Env: "CODER_AI_GATEWAY_CIRCUIT_BREAKER_TIMEOUT",
|
||||
Value: &c.AI.BridgeConfig.CircuitBreakerTimeout,
|
||||
Default: "30s",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "circuit_breaker_timeout",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
|
||||
}
|
||||
aiGatewayCircuitBreakerMaxRequests := serpent.Option{
|
||||
Name: "AI Gateway Circuit Breaker Max Requests",
|
||||
Description: "Maximum number of requests allowed in half-open state before deciding to close or re-open the circuit.",
|
||||
Flag: "ai-gateway-circuit-breaker-max-requests",
|
||||
Env: "CODER_AI_GATEWAY_CIRCUIT_BREAKER_MAX_REQUESTS",
|
||||
Value: serpent.Validate(&c.AI.BridgeConfig.CircuitBreakerMaxRequests, validateCircuitBreakerPercent),
|
||||
Default: "3",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "circuit_breaker_max_requests",
|
||||
}
|
||||
aiGatewayProxyEnabled := serpent.Option{
|
||||
Name: "AI Gateway Proxy Enabled",
|
||||
Description: "Enable the AI Gateway MITM Proxy for intercepting and decrypting AI provider requests.",
|
||||
Flag: "ai-gateway-proxy-enabled",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_ENABLED",
|
||||
Value: &c.AI.BridgeProxyConfig.Enabled,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "enabled",
|
||||
}
|
||||
aiGatewayProxyListenAddr := serpent.Option{
|
||||
Name: "AI Gateway Proxy Listen Address",
|
||||
Description: "The address the AI Gateway Proxy will listen on.",
|
||||
Flag: "ai-gateway-proxy-listen-addr",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_LISTEN_ADDR",
|
||||
Value: &c.AI.BridgeProxyConfig.ListenAddr,
|
||||
Default: ":8888",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "listen_addr",
|
||||
}
|
||||
aiGatewayProxyTLSCertFile := serpent.Option{
|
||||
Name: "AI Gateway Proxy TLS Certificate File",
|
||||
Description: "Path to the TLS certificate file for the AI Gateway Proxy listener. Must be set together with AI Gateway Proxy TLS Key File.",
|
||||
Flag: "ai-gateway-proxy-tls-cert-file",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_TLS_CERT_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.TLSCertFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "tls_cert_file",
|
||||
}
|
||||
aiGatewayProxyTLSKeyFile := serpent.Option{
|
||||
Name: "AI Gateway Proxy TLS Key File",
|
||||
Description: "Path to the TLS private key file for the AI Gateway Proxy listener. Must be set together with AI Gateway Proxy TLS Certificate File.",
|
||||
Flag: "ai-gateway-proxy-tls-key-file",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_TLS_KEY_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.TLSKeyFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "tls_key_file",
|
||||
}
|
||||
aiGatewayProxyMITMCertFile := serpent.Option{
|
||||
Name: "AI Gateway Proxy MITM CA Certificate File",
|
||||
Description: "Path to the CA certificate file used to intercept (MITM) HTTPS traffic from AI clients. This CA must be trusted by AI clients for the proxy to decrypt their requests.",
|
||||
Flag: "ai-gateway-proxy-cert-file",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_CERT_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.MITMCertFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "cert_file",
|
||||
}
|
||||
aiGatewayProxyMITMKeyFile := serpent.Option{
|
||||
Name: "AI Gateway Proxy MITM CA Key File",
|
||||
Description: "Path to the CA private key file used to intercept (MITM) HTTPS traffic from AI clients.",
|
||||
Flag: "ai-gateway-proxy-key-file",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_KEY_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.MITMKeyFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "key_file",
|
||||
}
|
||||
aiGatewayProxyDomainAllowlist := serpent.Option{
|
||||
Name: "AI Gateway Proxy Domain Allowlist",
|
||||
Description: "Deprecated: This value is now derived automatically from the configured AI Gateway providers' base URLs. Setting this value has no effect. This option will be removed in a future release.",
|
||||
Flag: "ai-gateway-proxy-domain-allowlist",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_DOMAIN_ALLOWLIST",
|
||||
Value: &c.AI.BridgeProxyConfig.DomainAllowlist,
|
||||
Default: "",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "domain_allowlist",
|
||||
}
|
||||
aiGatewayProxyUpstreamProxy := serpent.Option{
|
||||
Name: "AI Gateway Proxy Upstream Proxy",
|
||||
Description: "URL of an upstream HTTP proxy to chain tunneled (non-allowlisted) requests through. Format: http://[user:pass@]host:port or https://[user:pass@]host:port.",
|
||||
Flag: "ai-gateway-proxy-upstream",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_UPSTREAM",
|
||||
Value: &c.AI.BridgeProxyConfig.UpstreamProxy,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "upstream_proxy",
|
||||
}
|
||||
aiGatewayProxyUpstreamProxyCA := serpent.Option{
|
||||
Name: "AI Gateway Proxy Upstream Proxy CA",
|
||||
Description: "Path to a PEM-encoded CA certificate to trust for the upstream proxy's TLS connection. Only needed for HTTPS upstream proxies with certificates not trusted by the system. If not provided, the system certificate pool is used.",
|
||||
Flag: "ai-gateway-proxy-upstream-ca",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_UPSTREAM_CA",
|
||||
Value: &c.AI.BridgeProxyConfig.UpstreamProxyCA,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "upstream_proxy_ca",
|
||||
}
|
||||
aiGatewayProxyAllowedPrivateCIDRs := serpent.Option{
|
||||
Name: "AI Gateway Proxy Allowed Private CIDRs",
|
||||
Description: "Comma-separated list of CIDR ranges that are permitted even though they fall within blocked private/reserved IP ranges. By default all private ranges are blocked to prevent SSRF attacks. Use this to allow access to specific internal networks.",
|
||||
Flag: "ai-gateway-proxy-allowed-private-cidrs",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_ALLOWED_PRIVATE_CIDRS",
|
||||
Value: &c.AI.BridgeProxyConfig.AllowedPrivateCIDRs,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "allowed_private_cidrs",
|
||||
}
|
||||
aiGatewayProxyAPIDumpDir := serpent.Option{
|
||||
Name: "AI Gateway Proxy API Dump Directory",
|
||||
Description: "Directory for dumping MITM request/response pairs to disk for debugging. When set, each proxied request produces .req.txt and .resp.txt files organized by provider. Sensitive headers are redacted. Leave empty to disable.",
|
||||
Flag: "ai-gateway-proxy-dump-dir",
|
||||
Env: "CODER_AI_GATEWAY_PROXY_DUMP_DIR",
|
||||
Value: &c.AI.BridgeProxyConfig.APIDumpDir,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIGatewayProxy,
|
||||
YAML: "api_dump_dir",
|
||||
}
|
||||
opts := serpent.OptionSet{
|
||||
{
|
||||
Name: "Access URL",
|
||||
@@ -3677,122 +4048,155 @@ Write out the current server config as YAML to stdout.`,
|
||||
Group: &deploymentGroupChat,
|
||||
YAML: "debugLoggingEnabled",
|
||||
},
|
||||
// AI Bridge Options
|
||||
// AI Bridge Options (deprecated in favor of AI Gateway options)
|
||||
{
|
||||
Name: "AI Bridge Enabled",
|
||||
Description: "Whether to start an in-memory aibridged instance.",
|
||||
Description: "Deprecated: use --ai-gateway-enabled or CODER_AI_GATEWAY_ENABLED instead. Whether to start an in-memory aibridged instance.",
|
||||
Flag: "aibridge-enabled",
|
||||
Env: "CODER_AIBRIDGE_ENABLED",
|
||||
Value: &c.AI.BridgeConfig.Enabled,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "enabled",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayEnabled},
|
||||
},
|
||||
aiGatewayEnabled,
|
||||
{
|
||||
Name: "AI Bridge OpenAI Base URL",
|
||||
Description: "The base URL of the OpenAI API.",
|
||||
Description: "Deprecated: use --ai-gateway-openai-base-url or CODER_AI_GATEWAY_OPENAI_BASE_URL instead. The base URL of the OpenAI API.",
|
||||
Flag: "aibridge-openai-base-url",
|
||||
Env: "CODER_AIBRIDGE_OPENAI_BASE_URL",
|
||||
Value: &c.AI.BridgeConfig.LegacyOpenAI.BaseURL,
|
||||
Default: "https://api.openai.com/v1/",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "openai_base_url",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayOpenAIBaseURL},
|
||||
},
|
||||
aiGatewayOpenAIBaseURL,
|
||||
{
|
||||
Name: "AI Bridge OpenAI Key",
|
||||
Description: "The key to authenticate against the OpenAI API.",
|
||||
Description: "Deprecated: use --ai-gateway-openai-key or CODER_AI_GATEWAY_OPENAI_KEY instead. The key to authenticate against the OpenAI API.",
|
||||
Flag: "aibridge-openai-key",
|
||||
Env: "CODER_AIBRIDGE_OPENAI_KEY",
|
||||
Value: &c.AI.BridgeConfig.LegacyOpenAI.Key,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayOpenAIKey},
|
||||
},
|
||||
aiGatewayOpenAIKey,
|
||||
{
|
||||
Name: "AI Bridge Anthropic Base URL",
|
||||
Description: "The base URL of the Anthropic API.",
|
||||
Description: "Deprecated: use --ai-gateway-anthropic-base-url or CODER_AI_GATEWAY_ANTHROPIC_BASE_URL instead. The base URL of the Anthropic API.",
|
||||
Flag: "aibridge-anthropic-base-url",
|
||||
Env: "CODER_AIBRIDGE_ANTHROPIC_BASE_URL",
|
||||
Value: &c.AI.BridgeConfig.LegacyAnthropic.BaseURL,
|
||||
Default: "https://api.anthropic.com/",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "anthropic_base_url",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayAnthropicBaseURL},
|
||||
},
|
||||
aiGatewayAnthropicBaseURL,
|
||||
{
|
||||
Name: "AI Bridge Anthropic Key",
|
||||
Description: "The key to authenticate against the Anthropic API.",
|
||||
Description: "Deprecated: use --ai-gateway-anthropic-key or CODER_AI_GATEWAY_ANTHROPIC_KEY instead. The key to authenticate against the Anthropic API.",
|
||||
Flag: "aibridge-anthropic-key",
|
||||
Env: "CODER_AIBRIDGE_ANTHROPIC_KEY",
|
||||
Value: &c.AI.BridgeConfig.LegacyAnthropic.Key,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayAnthropicKey},
|
||||
},
|
||||
aiGatewayAnthropicKey,
|
||||
{
|
||||
Name: "AI Bridge Bedrock Base URL",
|
||||
Description: "The base URL to use for the AWS Bedrock API. Use this setting to specify an exact URL to use. Takes precedence " +
|
||||
Description: "Deprecated: use --ai-gateway-bedrock-base-url or CODER_AI_GATEWAY_BEDROCK_BASE_URL instead. The base URL to use for the AWS Bedrock API. Use this setting to specify an exact URL to use. Takes precedence " +
|
||||
"over CODER_AIBRIDGE_BEDROCK_REGION.",
|
||||
Flag: "aibridge-bedrock-base-url",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_BASE_URL",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.BaseURL,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "bedrock_base_url",
|
||||
Flag: "aibridge-bedrock-base-url",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_BASE_URL",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.BaseURL,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "bedrock_base_url",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayBedrockBaseURL},
|
||||
},
|
||||
aiGatewayBedrockBaseURL,
|
||||
{
|
||||
Name: "AI Bridge Bedrock Region",
|
||||
Description: "The AWS Bedrock API region to use. Constructs a base URL to use for the AWS Bedrock API in the form of " +
|
||||
Description: "Deprecated: use --ai-gateway-bedrock-region or CODER_AI_GATEWAY_BEDROCK_REGION instead. 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'.",
|
||||
Flag: "aibridge-bedrock-region",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_REGION",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.Region,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "bedrock_region",
|
||||
Flag: "aibridge-bedrock-region",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_REGION",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.Region,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "bedrock_region",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayBedrockRegion},
|
||||
},
|
||||
aiGatewayBedrockRegion,
|
||||
{
|
||||
Name: "AI Bridge Bedrock Access Key",
|
||||
Description: "The access key to authenticate against the AWS Bedrock API.",
|
||||
Description: "Deprecated: use --ai-gateway-bedrock-access-key or CODER_AI_GATEWAY_BEDROCK_ACCESS_KEY instead. The access key to authenticate against the AWS Bedrock API.",
|
||||
Flag: "aibridge-bedrock-access-key",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_ACCESS_KEY",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.AccessKey,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayBedrockAccessKey},
|
||||
},
|
||||
aiGatewayBedrockAccessKey,
|
||||
{
|
||||
Name: "AI Bridge Bedrock Access Key Secret",
|
||||
Description: "The access key secret to use with the access key to authenticate against the AWS Bedrock API.",
|
||||
Description: "Deprecated: use --ai-gateway-bedrock-access-key-secret or CODER_AI_GATEWAY_BEDROCK_ACCESS_KEY_SECRET instead. The access key secret to use with the access key to authenticate against the AWS Bedrock API.",
|
||||
Flag: "aibridge-bedrock-access-key-secret",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_ACCESS_KEY_SECRET",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.AccessKeySecret,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayBedrockAccessKeySecret},
|
||||
},
|
||||
aiGatewayBedrockAccessKeySecret,
|
||||
{
|
||||
Name: "AI Bridge Bedrock Model",
|
||||
Description: "The model to use when making requests to the AWS Bedrock API.",
|
||||
Description: "Deprecated: use --ai-gateway-bedrock-model or CODER_AI_GATEWAY_BEDROCK_MODEL instead. The model to use when making requests to the AWS Bedrock API.",
|
||||
Flag: "aibridge-bedrock-model",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_MODEL",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.Model,
|
||||
Default: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", // See https://docs.claude.com/en/api/claude-on-amazon-bedrock#accessing-bedrock.
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "bedrock_model",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayBedrockModel},
|
||||
},
|
||||
aiGatewayBedrockModel,
|
||||
{
|
||||
Name: "AI Bridge Bedrock Small Fast Model",
|
||||
Description: "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.",
|
||||
Description: "Deprecated: use --ai-gateway-bedrock-small-fastmodel or CODER_AI_GATEWAY_BEDROCK_SMALL_FAST_MODEL instead. 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.",
|
||||
Flag: "aibridge-bedrock-small-fastmodel",
|
||||
Env: "CODER_AIBRIDGE_BEDROCK_SMALL_FAST_MODEL",
|
||||
Value: &c.AI.BridgeConfig.LegacyBedrock.SmallFastModel,
|
||||
Default: "global.anthropic.claude-haiku-4-5-20251001-v1:0", // See https://docs.claude.com/en/api/claude-on-amazon-bedrock#accessing-bedrock.
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "bedrock_small_fast_model",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayBedrockSmallFastModel},
|
||||
},
|
||||
aiGatewayBedrockSmallFastModel,
|
||||
{
|
||||
Name: "AI Bridge Inject Coder MCP tools",
|
||||
Description: "Deprecated: Injected MCP in AI Bridge is deprecated and will be removed in a future release. Whether to inject Coder's MCP tools into intercepted AI Bridge requests (requires the \"oauth2\" and \"mcp-server-http\" experiments to be enabled).",
|
||||
Description: "Deprecated: Injected MCP in AI Gateway is deprecated and will be removed in a future release. This option is an alias for --ai-gateway-inject-coder-mcp-tools.",
|
||||
Flag: "aibridge-inject-coder-mcp-tools",
|
||||
Env: "CODER_AIBRIDGE_INJECT_CODER_MCP_TOOLS",
|
||||
Value: &c.AI.BridgeConfig.InjectCoderMCPTools,
|
||||
@@ -3800,10 +4204,12 @@ Write out the current server config as YAML to stdout.`,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "inject_coder_mcp_tools",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayInjectCoderMCPTools},
|
||||
},
|
||||
aiGatewayInjectCoderMCPTools,
|
||||
{
|
||||
Name: "AI Bridge Data Retention Duration",
|
||||
Description: "Length of time to retain data such as interceptions and all related records (token, prompt, tool use).",
|
||||
Description: "Deprecated: use --ai-gateway-retention or CODER_AI_GATEWAY_RETENTION instead. Length of time to retain data such as interceptions and all related records (token, prompt, tool use).",
|
||||
Flag: "aibridge-retention",
|
||||
Env: "CODER_AIBRIDGE_RETENTION",
|
||||
Value: &c.AI.BridgeConfig.Retention,
|
||||
@@ -3811,88 +4217,106 @@ Write out the current server config as YAML to stdout.`,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "retention",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayRetention},
|
||||
},
|
||||
aiGatewayRetention,
|
||||
{
|
||||
Name: "AI Bridge Max Concurrency",
|
||||
Description: "Maximum number of concurrent AI Bridge requests per replica. Set to 0 to disable (unlimited).",
|
||||
Description: "Deprecated: use --ai-gateway-max-concurrency or CODER_AI_GATEWAY_MAX_CONCURRENCY instead. Maximum number of concurrent AI Bridge requests per replica. Set to 0 to disable (unlimited).",
|
||||
Flag: "aibridge-max-concurrency",
|
||||
Env: "CODER_AIBRIDGE_MAX_CONCURRENCY",
|
||||
Value: &c.AI.BridgeConfig.MaxConcurrency,
|
||||
Default: "0",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "max_concurrency",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayMaxConcurrency},
|
||||
},
|
||||
aiGatewayMaxConcurrency,
|
||||
{
|
||||
Name: "AI Bridge Rate Limit",
|
||||
Description: "Maximum number of AI Bridge requests per second per replica. Set to 0 to disable (unlimited).",
|
||||
Description: "Deprecated: use --ai-gateway-rate-limit or CODER_AI_GATEWAY_RATE_LIMIT instead. Maximum number of AI Bridge requests per second per replica. Set to 0 to disable (unlimited).",
|
||||
Flag: "aibridge-rate-limit",
|
||||
Env: "CODER_AIBRIDGE_RATE_LIMIT",
|
||||
Value: &c.AI.BridgeConfig.RateLimit,
|
||||
Default: "0",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "rate_limit",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayRateLimit},
|
||||
},
|
||||
aiGatewayRateLimit,
|
||||
{
|
||||
Name: "AI Bridge Structured Logging",
|
||||
Description: "Emit structured logs for AI Bridge interception records. Use this for exporting these records to external SIEM or observability systems.",
|
||||
Description: "Deprecated: use --ai-gateway-structured-logging or CODER_AI_GATEWAY_STRUCTURED_LOGGING instead. Emit structured logs for AI Bridge interception records. Use this for exporting these records to external SIEM or observability systems.",
|
||||
Flag: "aibridge-structured-logging",
|
||||
Env: "CODER_AIBRIDGE_STRUCTURED_LOGGING",
|
||||
Value: &c.AI.BridgeConfig.StructuredLogging,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "structured_logging",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayStructuredLogging},
|
||||
},
|
||||
aiGatewayStructuredLogging,
|
||||
{
|
||||
Name: "AI Bridge Send Actor Headers",
|
||||
Description: "Once enabled, extra headers will be added to upstream requests to identify the user (actor) making requests to AI Bridge. " +
|
||||
Description: "Deprecated: use --ai-gateway-send-actor-headers or CODER_AI_GATEWAY_SEND_ACTOR_HEADERS instead. Once enabled, extra headers will be added to upstream requests to identify the user (actor) making requests to AI Bridge. " +
|
||||
"This is only needed if you are using a proxy between AI Bridge and an upstream AI provider. " +
|
||||
"This will send X-Ai-Bridge-Actor-Id (the ID of the user making the request) and X-Ai-Bridge-Actor-Metadata-Username (their username).",
|
||||
Flag: "aibridge-send-actor-headers",
|
||||
Env: "CODER_AIBRIDGE_SEND_ACTOR_HEADERS",
|
||||
Value: &c.AI.BridgeConfig.SendActorHeaders,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "send_actor_headers",
|
||||
Flag: "aibridge-send-actor-headers",
|
||||
Env: "CODER_AIBRIDGE_SEND_ACTOR_HEADERS",
|
||||
Value: &c.AI.BridgeConfig.SendActorHeaders,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "send_actor_headers",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewaySendActorHeaders},
|
||||
},
|
||||
aiGatewaySendActorHeaders,
|
||||
{
|
||||
Name: "AI Bridge Allow BYOK",
|
||||
Description: "Allow users to provide their own LLM API keys or subscriptions. When disabled, only centralized key authentication is permitted.",
|
||||
Description: "Deprecated: use --ai-gateway-allow-byok or CODER_AI_GATEWAY_ALLOW_BYOK instead. Allow users to provide their own LLM API keys or subscriptions. When disabled, only centralized key authentication is permitted.",
|
||||
Flag: "aibridge-allow-byok",
|
||||
Env: "CODER_AIBRIDGE_ALLOW_BYOK",
|
||||
Value: &c.AI.BridgeConfig.AllowBYOK,
|
||||
Default: "true",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "allow_byok",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayAllowBYOK},
|
||||
},
|
||||
aiGatewayAllowBYOK,
|
||||
{
|
||||
Name: "AI Bridge Circuit Breaker Enabled",
|
||||
Description: "Enable the circuit breaker to protect against cascading failures from upstream AI provider overload (503, 529).",
|
||||
Description: "Deprecated: use --ai-gateway-circuit-breaker-enabled or CODER_AI_GATEWAY_CIRCUIT_BREAKER_ENABLED instead. Enable the circuit breaker to protect against cascading failures from upstream AI provider overload (503, 529).",
|
||||
Flag: "aibridge-circuit-breaker-enabled",
|
||||
Env: "CODER_AIBRIDGE_CIRCUIT_BREAKER_ENABLED",
|
||||
Value: &c.AI.BridgeConfig.CircuitBreakerEnabled,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "circuit_breaker_enabled",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayCircuitBreakerEnabled},
|
||||
},
|
||||
aiGatewayCircuitBreakerEnabled,
|
||||
{
|
||||
Name: "AI Bridge Circuit Breaker Failure Threshold",
|
||||
Description: "Number of consecutive failures that triggers the circuit breaker to open.",
|
||||
Description: "Deprecated: use --ai-gateway-circuit-breaker-failure-threshold or CODER_AI_GATEWAY_CIRCUIT_BREAKER_FAILURE_THRESHOLD instead. Number of consecutive failures that triggers the circuit breaker to open.",
|
||||
Flag: "aibridge-circuit-breaker-failure-threshold",
|
||||
Env: "CODER_AIBRIDGE_CIRCUIT_BREAKER_FAILURE_THRESHOLD",
|
||||
Value: serpent.Validate(&c.AI.BridgeConfig.CircuitBreakerFailureThreshold, func(value *serpent.Int64) error {
|
||||
if value.Value() <= 0 || value.Value() > 100 {
|
||||
return xerrors.New("must be between 1 and 100")
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
Default: "5",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "circuit_breaker_failure_threshold",
|
||||
Value: serpent.Validate(&c.AI.BridgeConfig.CircuitBreakerFailureThreshold, validateCircuitBreakerPercent),
|
||||
Default: "5",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "circuit_breaker_failure_threshold",
|
||||
UseInstead: serpent.OptionSet{aiGatewayCircuitBreakerFailureThreshold},
|
||||
},
|
||||
aiGatewayCircuitBreakerFailureThreshold,
|
||||
{
|
||||
Name: "AI Bridge Circuit Breaker Interval",
|
||||
Description: "Cyclic period of the closed state for clearing internal failure counts.",
|
||||
Description: "Deprecated: use --ai-gateway-circuit-breaker-interval or CODER_AI_GATEWAY_CIRCUIT_BREAKER_INTERVAL instead. Cyclic period of the closed state for clearing internal failure counts.",
|
||||
Flag: "aibridge-circuit-breaker-interval",
|
||||
Env: "CODER_AIBRIDGE_CIRCUIT_BREAKER_INTERVAL",
|
||||
Value: &c.AI.BridgeConfig.CircuitBreakerInterval,
|
||||
@@ -3901,10 +4325,12 @@ Write out the current server config as YAML to stdout.`,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "circuit_breaker_interval",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
|
||||
UseInstead: serpent.OptionSet{aiGatewayCircuitBreakerInterval},
|
||||
},
|
||||
aiGatewayCircuitBreakerInterval,
|
||||
{
|
||||
Name: "AI Bridge Circuit Breaker Timeout",
|
||||
Description: "How long the circuit breaker stays open before transitioning to half-open state.",
|
||||
Description: "Deprecated: use --ai-gateway-circuit-breaker-timeout or CODER_AI_GATEWAY_CIRCUIT_BREAKER_TIMEOUT instead. How long the circuit breaker stays open before transitioning to half-open state.",
|
||||
Flag: "aibridge-circuit-breaker-timeout",
|
||||
Env: "CODER_AIBRIDGE_CIRCUIT_BREAKER_TIMEOUT",
|
||||
Value: &c.AI.BridgeConfig.CircuitBreakerTimeout,
|
||||
@@ -3913,24 +4339,22 @@ Write out the current server config as YAML to stdout.`,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "circuit_breaker_timeout",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
|
||||
UseInstead: serpent.OptionSet{aiGatewayCircuitBreakerTimeout},
|
||||
},
|
||||
aiGatewayCircuitBreakerTimeout,
|
||||
{
|
||||
Name: "AI Bridge Circuit Breaker Max Requests",
|
||||
Description: "Maximum number of requests allowed in half-open state before deciding to close or re-open the circuit.",
|
||||
Description: "Deprecated: use --ai-gateway-circuit-breaker-max-requests or CODER_AI_GATEWAY_CIRCUIT_BREAKER_MAX_REQUESTS instead. Maximum number of requests allowed in half-open state before deciding to close or re-open the circuit.",
|
||||
Flag: "aibridge-circuit-breaker-max-requests",
|
||||
Env: "CODER_AIBRIDGE_CIRCUIT_BREAKER_MAX_REQUESTS",
|
||||
Value: serpent.Validate(&c.AI.BridgeConfig.CircuitBreakerMaxRequests, func(value *serpent.Int64) error {
|
||||
if value.Value() <= 0 || value.Value() > 100 {
|
||||
return xerrors.New("must be between 1 and 100")
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
Default: "3",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "circuit_breaker_max_requests",
|
||||
Value: serpent.Validate(&c.AI.BridgeConfig.CircuitBreakerMaxRequests, validateCircuitBreakerPercent),
|
||||
Default: "3",
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "circuit_breaker_max_requests",
|
||||
UseInstead: serpent.OptionSet{aiGatewayCircuitBreakerMaxRequests},
|
||||
},
|
||||
|
||||
aiGatewayCircuitBreakerMaxRequests,
|
||||
{
|
||||
Name: "AI Budget Policy",
|
||||
Description: "Determines the effective group when a user belongs to multiple groups with AI budgets. \"highest\" selects the group with the largest spend limit, and is currently the only supported value.",
|
||||
@@ -3938,7 +4362,7 @@ Write out the current server config as YAML to stdout.`,
|
||||
Env: "CODER_AI_BUDGET_POLICY",
|
||||
Value: serpent.EnumOf(&c.AI.BridgeConfig.BudgetPolicy, AIBudgetPolicies...),
|
||||
Default: string(AIBudgetPolicyHighest),
|
||||
Group: &deploymentGroupAIBridge,
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "budget_policy",
|
||||
},
|
||||
{
|
||||
@@ -3948,71 +4372,89 @@ Write out the current server config as YAML to stdout.`,
|
||||
Env: "CODER_AI_BUDGET_PERIOD",
|
||||
Value: serpent.EnumOf(&c.AI.BridgeConfig.BudgetPeriod, AIBudgetPeriods...),
|
||||
Default: string(AIBudgetPeriodMonth),
|
||||
Group: &deploymentGroupAIBridge,
|
||||
Group: &deploymentGroupAIGateway,
|
||||
YAML: "budget_period",
|
||||
},
|
||||
|
||||
// AI Bridge Proxy Options
|
||||
// AI Gateway Proxy Options
|
||||
{
|
||||
Name: "AI Bridge Proxy Enabled",
|
||||
Description: "Enable the AI Bridge MITM Proxy for intercepting and decrypting AI provider requests.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-enabled or CODER_AI_GATEWAY_PROXY_ENABLED instead. Enable the AI Bridge MITM Proxy for intercepting and decrypting AI provider requests.",
|
||||
Flag: "aibridge-proxy-enabled",
|
||||
Env: "CODER_AIBRIDGE_PROXY_ENABLED",
|
||||
Value: &c.AI.BridgeProxyConfig.Enabled,
|
||||
Default: "false",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "enabled",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyEnabled},
|
||||
},
|
||||
aiGatewayProxyEnabled,
|
||||
{
|
||||
Name: "AI Bridge Proxy Listen Address",
|
||||
Description: "The address the AI Bridge Proxy will listen on.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-listen-addr or CODER_AI_GATEWAY_PROXY_LISTEN_ADDR instead. The address the AI Bridge Proxy will listen on.",
|
||||
Flag: "aibridge-proxy-listen-addr",
|
||||
Env: "CODER_AIBRIDGE_PROXY_LISTEN_ADDR",
|
||||
Value: &c.AI.BridgeProxyConfig.ListenAddr,
|
||||
Default: ":8888",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "listen_addr",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyListenAddr},
|
||||
},
|
||||
aiGatewayProxyListenAddr,
|
||||
{
|
||||
Name: "AI Bridge Proxy TLS Certificate File",
|
||||
Description: "Path to the TLS certificate file for the AI Bridge Proxy listener. Must be set together with AI Bridge Proxy TLS Key File.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-tls-cert-file or CODER_AI_GATEWAY_PROXY_TLS_CERT_FILE instead. Path to the TLS certificate file for the AI Bridge Proxy listener. Must be set together with AI Bridge Proxy TLS Key File.",
|
||||
Flag: "aibridge-proxy-tls-cert-file",
|
||||
Env: "CODER_AIBRIDGE_PROXY_TLS_CERT_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.TLSCertFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "tls_cert_file",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyTLSCertFile},
|
||||
},
|
||||
aiGatewayProxyTLSCertFile,
|
||||
{
|
||||
Name: "AI Bridge Proxy TLS Key File",
|
||||
Description: "Path to the TLS private key file for the AI Bridge Proxy listener. Must be set together with AI Bridge Proxy TLS Certificate File.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-tls-key-file or CODER_AI_GATEWAY_PROXY_TLS_KEY_FILE instead. Path to the TLS private key file for the AI Bridge Proxy listener. Must be set together with AI Bridge Proxy TLS Certificate File.",
|
||||
Flag: "aibridge-proxy-tls-key-file",
|
||||
Env: "CODER_AIBRIDGE_PROXY_TLS_KEY_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.TLSKeyFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "tls_key_file",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyTLSKeyFile},
|
||||
},
|
||||
aiGatewayProxyTLSKeyFile,
|
||||
{
|
||||
Name: "AI Bridge Proxy MITM CA Certificate File",
|
||||
Description: "Path to the CA certificate file used to intercept (MITM) HTTPS traffic from AI clients. This CA must be trusted by AI clients for the proxy to decrypt their requests.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-cert-file or CODER_AI_GATEWAY_PROXY_CERT_FILE instead. Path to the CA certificate file used to intercept (MITM) HTTPS traffic from AI clients. This CA must be trusted by AI clients for the proxy to decrypt their requests.",
|
||||
Flag: "aibridge-proxy-cert-file",
|
||||
Env: "CODER_AIBRIDGE_PROXY_CERT_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.MITMCertFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "cert_file",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyMITMCertFile},
|
||||
},
|
||||
aiGatewayProxyMITMCertFile,
|
||||
{
|
||||
Name: "AI Bridge Proxy MITM CA Key File",
|
||||
Description: "Path to the CA private key file used to intercept (MITM) HTTPS traffic from AI clients.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-key-file or CODER_AI_GATEWAY_PROXY_KEY_FILE instead. Path to the CA private key file used to intercept (MITM) HTTPS traffic from AI clients.",
|
||||
Flag: "aibridge-proxy-key-file",
|
||||
Env: "CODER_AIBRIDGE_PROXY_KEY_FILE",
|
||||
Value: &c.AI.BridgeProxyConfig.MITMKeyFile,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "key_file",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyMITMKeyFile},
|
||||
},
|
||||
aiGatewayProxyMITMKeyFile,
|
||||
{
|
||||
Name: "AI Bridge Proxy Domain Allowlist",
|
||||
Description: "Deprecated: This value is now derived automatically from the configured AI providers' base URLs. Setting this value has no effect. This option will be removed in a future release.",
|
||||
@@ -4023,47 +4465,61 @@ Write out the current server config as YAML to stdout.`,
|
||||
Hidden: true,
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "domain_allowlist",
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyDomainAllowlist},
|
||||
},
|
||||
aiGatewayProxyDomainAllowlist,
|
||||
{
|
||||
Name: "AI Bridge Proxy Upstream Proxy",
|
||||
Description: "URL of an upstream HTTP proxy to chain tunneled (non-allowlisted) requests through. Format: http://[user:pass@]host:port or https://[user:pass@]host:port.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-upstream or CODER_AI_GATEWAY_PROXY_UPSTREAM instead. URL of an upstream HTTP proxy to chain tunneled (non-allowlisted) requests through. Format: http://[user:pass@]host:port or https://[user:pass@]host:port.",
|
||||
Flag: "aibridge-proxy-upstream",
|
||||
Env: "CODER_AIBRIDGE_PROXY_UPSTREAM",
|
||||
Value: &c.AI.BridgeProxyConfig.UpstreamProxy,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "upstream_proxy",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyUpstreamProxy},
|
||||
},
|
||||
aiGatewayProxyUpstreamProxy,
|
||||
{
|
||||
Name: "AI Bridge Proxy Upstream Proxy CA",
|
||||
Description: "Path to a PEM-encoded CA certificate to trust for the upstream proxy's TLS connection. Only needed for HTTPS upstream proxies with certificates not trusted by the system. If not provided, the system certificate pool is used.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-upstream-ca or CODER_AI_GATEWAY_PROXY_UPSTREAM_CA instead. Path to a PEM-encoded CA certificate to trust for the upstream proxy's TLS connection. Only needed for HTTPS upstream proxies with certificates not trusted by the system. If not provided, the system certificate pool is used.",
|
||||
Flag: "aibridge-proxy-upstream-ca",
|
||||
Env: "CODER_AIBRIDGE_PROXY_UPSTREAM_CA",
|
||||
Value: &c.AI.BridgeProxyConfig.UpstreamProxyCA,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "upstream_proxy_ca",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyUpstreamProxyCA},
|
||||
},
|
||||
aiGatewayProxyUpstreamProxyCA,
|
||||
{
|
||||
Name: "AI Bridge Proxy Allowed Private CIDRs",
|
||||
Description: "Comma-separated list of CIDR ranges that are permitted even though they fall within blocked private/reserved IP ranges. By default all private ranges are blocked to prevent SSRF attacks. Use this to allow access to specific internal networks.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-allowed-private-cidrs or CODER_AI_GATEWAY_PROXY_ALLOWED_PRIVATE_CIDRS instead. Comma-separated list of CIDR ranges that are permitted even though they fall within blocked private/reserved IP ranges. By default all private ranges are blocked to prevent SSRF attacks. Use this to allow access to specific internal networks.",
|
||||
Flag: "aibridge-proxy-allowed-private-cidrs",
|
||||
Env: "CODER_AIBRIDGE_PROXY_ALLOWED_PRIVATE_CIDRS",
|
||||
Value: &c.AI.BridgeProxyConfig.AllowedPrivateCIDRs,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "allowed_private_cidrs",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyAllowedPrivateCIDRs},
|
||||
},
|
||||
aiGatewayProxyAllowedPrivateCIDRs,
|
||||
{
|
||||
Name: "AI Bridge Proxy API Dump Directory",
|
||||
Description: "Directory for dumping MITM request/response pairs to disk for debugging. When set, each proxied request produces .req.txt and .resp.txt files organized by provider. Sensitive headers are redacted. Leave empty to disable.",
|
||||
Description: "Deprecated: use --ai-gateway-proxy-dump-dir or CODER_AI_GATEWAY_PROXY_DUMP_DIR instead. Directory for dumping MITM request/response pairs to disk for debugging. When set, each proxied request produces .req.txt and .resp.txt files organized by provider. Sensitive headers are redacted. Leave empty to disable.",
|
||||
Flag: "aibridge-proxy-dump-dir",
|
||||
Env: "CODER_AIBRIDGE_PROXY_DUMP_DIR",
|
||||
Value: &c.AI.BridgeProxyConfig.APIDumpDir,
|
||||
Default: "",
|
||||
Group: &deploymentGroupAIBridgeProxy,
|
||||
YAML: "api_dump_dir",
|
||||
Hidden: true,
|
||||
UseInstead: serpent.OptionSet{aiGatewayProxyAPIDumpDir},
|
||||
},
|
||||
aiGatewayProxyAPIDumpDir,
|
||||
|
||||
// Retention settings
|
||||
{
|
||||
@@ -4149,13 +4605,13 @@ Write out the current server config as YAML to stdout.`,
|
||||
|
||||
type AIBridgeConfig struct {
|
||||
Enabled serpent.Bool `json:"enabled" typescript:",notnull"`
|
||||
// Deprecated: Use Providers with indexed CODER_AIBRIDGE_PROVIDER_<N>_* env vars instead.
|
||||
// Deprecated: Use Providers with indexed CODER_AI_GATEWAY_PROVIDER_<N>_* env vars instead.
|
||||
LegacyOpenAI AIBridgeOpenAIConfig `json:"openai" typescript:",notnull"`
|
||||
// Deprecated: Use Providers with indexed CODER_AIBRIDGE_PROVIDER_<N>_* env vars instead.
|
||||
// Deprecated: Use Providers with indexed CODER_AI_GATEWAY_PROVIDER_<N>_* env vars instead.
|
||||
LegacyAnthropic AIBridgeAnthropicConfig `json:"anthropic" typescript:",notnull"`
|
||||
// Deprecated: Use Providers with indexed CODER_AIBRIDGE_PROVIDER_<N>_* env vars instead.
|
||||
// Deprecated: Use Providers with indexed CODER_AI_GATEWAY_PROVIDER_<N>_* env vars instead.
|
||||
LegacyBedrock AIBridgeBedrockConfig `json:"bedrock" typescript:",notnull"`
|
||||
// Providers holds provider instances populated from CODER_AIBRIDGE_PROVIDER_<N>_<KEY>
|
||||
// Providers holds provider instances populated from CODER_AI_GATEWAY_PROVIDER_<N>_<KEY>
|
||||
// env vars and/or the deprecated LegacyOpenAI/LegacyAnthropic/LegacyBedrock fields above.
|
||||
Providers []AIProviderConfig `json:"providers,omitempty"`
|
||||
// Deprecated: Injected MCP in AI Bridge is deprecated and will be removed in a future release.
|
||||
@@ -4198,7 +4654,8 @@ type AIBridgeBedrockConfig struct {
|
||||
}
|
||||
|
||||
// AIProviderConfig represents a single AI provider instance,
|
||||
// parsed from CODER_AIBRIDGE_PROVIDER_<N>_<KEY> environment variables.
|
||||
// parsed from CODER_AI_GATEWAY_PROVIDER_<N>_<KEY> environment variables.
|
||||
// CODER_AIBRIDGE_PROVIDER_<N>_<KEY> is also accepted as a deprecated alias.
|
||||
// This follows the same indexed pattern as ExternalAuthConfig.
|
||||
type AIProviderConfig struct {
|
||||
// Type is the provider type: "openai", "anthropic", or "copilot".
|
||||
|
||||
+152
-4
@@ -87,16 +87,16 @@ func TestDeploymentValues_HighlyConfigurable(t *testing.T) {
|
||||
},
|
||||
// We don't want these to be configurable via YAML because they are secrets.
|
||||
// However, we do want to allow them to be shown in documentation.
|
||||
"AI Bridge OpenAI Key": {
|
||||
"AI Gateway OpenAI Key": {
|
||||
yaml: true,
|
||||
},
|
||||
"AI Bridge Anthropic Key": {
|
||||
"AI Gateway Anthropic Key": {
|
||||
yaml: true,
|
||||
},
|
||||
"AI Bridge Bedrock Access Key": {
|
||||
"AI Gateway Bedrock Access Key": {
|
||||
yaml: true,
|
||||
},
|
||||
"AI Bridge Bedrock Access Key Secret": {
|
||||
"AI Gateway Bedrock Access Key Secret": {
|
||||
yaml: true,
|
||||
},
|
||||
}
|
||||
@@ -307,6 +307,154 @@ func must[T any](value T, err error) T {
|
||||
return value
|
||||
}
|
||||
|
||||
func TestAIGatewayCompatibilityAliases(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
options := (&codersdk.DeploymentValues{}).Options()
|
||||
byFlag := map[string]serpent.Option{}
|
||||
for _, opt := range options {
|
||||
if opt.Flag != "" {
|
||||
byFlag[opt.Flag] = opt
|
||||
}
|
||||
}
|
||||
|
||||
type alias struct {
|
||||
old serpent.Option
|
||||
new serpent.Option
|
||||
}
|
||||
var aliases []alias
|
||||
for _, opt := range options {
|
||||
if !strings.HasPrefix(opt.Flag, "aibridge-") {
|
||||
continue
|
||||
}
|
||||
require.True(t, strings.HasPrefix(opt.Description, "Deprecated:"), "aibridge option %s should have a 'Deprecated:' description", opt.Flag)
|
||||
require.Len(t, opt.UseInstead, 1, "aibridge option %s should point to a single replacement", opt.Flag)
|
||||
|
||||
newOpt, ok := byFlag[opt.UseInstead[0].Flag]
|
||||
require.True(t, ok, "aibridge option %s points to unknown flag %s", opt.Flag, opt.UseInstead[0].Flag)
|
||||
require.NotEqual(t, opt.Flag, newOpt.Flag, "flag %s shares its flag with the new alias option", opt.Flag)
|
||||
require.NotEqual(t, opt.Env, newOpt.Env, "flag %s shares its env with the new alias option", opt.Flag)
|
||||
if oldYAML := opt.YAMLPath(); oldYAML != "" {
|
||||
require.NotEqual(t, oldYAML, newOpt.YAMLPath(), "flag %s shares its YAML path with the new alias option", opt.Flag)
|
||||
} else {
|
||||
require.Empty(t, newOpt.YAMLPath(), "flag %s has no YAML path but the new alias option %s does", opt.Flag, newOpt.Flag)
|
||||
}
|
||||
aliases = append(aliases, alias{old: opt, new: newOpt})
|
||||
}
|
||||
// Update this count when adding or removing aibridge alias options.
|
||||
require.Len(t, aliases, 34, "unexpected number of aibridge alias options")
|
||||
|
||||
sampleVal := func(opt serpent.Option) any {
|
||||
switch opt.Value.Type() {
|
||||
case "bool":
|
||||
return opt.Default != "true"
|
||||
case "int":
|
||||
return 7
|
||||
case "duration":
|
||||
return "2h"
|
||||
case "string-array":
|
||||
return []string{"10.0.0.0/8", "172.16.0.0/12"}
|
||||
default:
|
||||
return "alias-value"
|
||||
}
|
||||
}
|
||||
sampleArg := func(opt serpent.Option) string {
|
||||
v := sampleVal(opt)
|
||||
if arr, ok := v.([]string); ok {
|
||||
return strings.Join(arr, ",")
|
||||
}
|
||||
return fmt.Sprint(v)
|
||||
}
|
||||
|
||||
aiConfFromOpts := func(t *testing.T, apply func(opts serpent.OptionSet) error) codersdk.AIConfig {
|
||||
t.Helper()
|
||||
dv := &codersdk.DeploymentValues{}
|
||||
opts := dv.Options()
|
||||
require.NoError(t, opts.SetDefaults())
|
||||
require.NoError(t, apply(opts))
|
||||
return dv.AI
|
||||
}
|
||||
|
||||
t.Run("FlagParity", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var oldArgs, newArgs []string
|
||||
for _, a := range aliases {
|
||||
value := sampleArg(a.old)
|
||||
oldArgs = append(oldArgs, "--"+a.old.Flag, value)
|
||||
newArgs = append(newArgs, "--"+a.new.Flag, value)
|
||||
}
|
||||
oldAI := aiConfFromOpts(t, func(opts serpent.OptionSet) error {
|
||||
return opts.FlagSet().Parse(oldArgs)
|
||||
})
|
||||
newAI := aiConfFromOpts(t, func(opts serpent.OptionSet) error {
|
||||
return opts.FlagSet().Parse(newArgs)
|
||||
})
|
||||
require.Equal(t, newAI, oldAI)
|
||||
})
|
||||
|
||||
t.Run("EnvParity", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var oldEnv, newEnv []serpent.EnvVar
|
||||
for _, a := range aliases {
|
||||
value := sampleArg(a.old)
|
||||
oldEnv = append(oldEnv, serpent.EnvVar{Name: a.old.Env, Value: value})
|
||||
newEnv = append(newEnv, serpent.EnvVar{Name: a.new.Env, Value: value})
|
||||
}
|
||||
oldAI := aiConfFromOpts(t, func(opts serpent.OptionSet) error {
|
||||
return opts.ParseEnv(oldEnv)
|
||||
})
|
||||
newAI := aiConfFromOpts(t, func(opts serpent.OptionSet) error {
|
||||
return opts.ParseEnv(newEnv)
|
||||
})
|
||||
require.Equal(t, newAI, oldAI)
|
||||
})
|
||||
|
||||
t.Run("YAMLParity", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
setPath := func(doc map[string]any, path string, value any) {
|
||||
parts := strings.Split(path, ".")
|
||||
for _, field := range parts[:len(parts)-1] {
|
||||
next, ok := doc[field].(map[string]any)
|
||||
if !ok {
|
||||
next = map[string]any{}
|
||||
doc[field] = next
|
||||
}
|
||||
doc = next
|
||||
}
|
||||
doc[parts[len(parts)-1]] = value
|
||||
}
|
||||
|
||||
oldYAML := map[string]any{}
|
||||
newYAML := map[string]any{}
|
||||
for _, a := range aliases {
|
||||
oldPath := a.old.YAMLPath()
|
||||
newPath := a.new.YAMLPath()
|
||||
if oldPath == "" {
|
||||
require.Empty(t, newPath)
|
||||
continue
|
||||
}
|
||||
require.NotEmpty(t, newPath, "new flag %s has no YAML path", a.old.Flag)
|
||||
|
||||
value := sampleVal(a.old)
|
||||
setPath(oldYAML, oldPath, value)
|
||||
setPath(newYAML, newPath, value)
|
||||
}
|
||||
|
||||
parse := func(doc map[string]any) codersdk.AIConfig {
|
||||
var node yaml.Node
|
||||
require.NoError(t, node.Encode(doc))
|
||||
return aiConfFromOpts(t, func(opts serpent.OptionSet) error {
|
||||
return opts.UnmarshalYAML(&node)
|
||||
})
|
||||
}
|
||||
|
||||
require.Equal(t, parse(newYAML), parse(oldYAML))
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeploymentValues_Validate_RefreshLifetime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user