feat: add AWS Bedrock support (#20507)

Depends on https://github.com/coder/aibridge/pull/44

Closes https://github.com/coder/aibridge/issues/28

---------

Signed-off-by: Danny Kopping <danny@coder.com>
This commit is contained in:
Danny Kopping
2025-10-28 03:38:14 +00:00
committed by GitHub
parent 4f7b279fd8
commit d18441debe
12 changed files with 228 additions and 14 deletions
+20 -2
View File
@@ -726,7 +726,25 @@ aibridge:
openai_key: ""
# The base URL of the Anthropic API.
# (default: https://api.anthropic.com/, type: string)
base_url: https://api.anthropic.com/
anthropic_base_url: https://api.anthropic.com/
# The key to authenticate against the Anthropic API.
# (default: <unset>, type: string)
key: ""
anthropic_key: ""
# The AWS Bedrock API region.
# (default: <unset>, type: string)
bedrock_region: ""
# The access key to authenticate against the AWS Bedrock API.
# (default: <unset>, type: string)
bedrock_access_key: ""
# The access key secret to use with the access key to authenticate against the AWS
# Bedrock API.
# (default: <unset>, type: string)
bedrock_access_key_secret: ""
# 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
# 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
+23
View File
@@ -11668,12 +11668,35 @@ const docTemplate = `{
}
}
},
"codersdk.AIBridgeBedrockConfig": {
"type": "object",
"properties": {
"access_key": {
"type": "string"
},
"access_key_secret": {
"type": "string"
},
"model": {
"type": "string"
},
"region": {
"type": "string"
},
"small_fast_model": {
"type": "string"
}
}
},
"codersdk.AIBridgeConfig": {
"type": "object",
"properties": {
"anthropic": {
"$ref": "#/definitions/codersdk.AIBridgeAnthropicConfig"
},
"bedrock": {
"$ref": "#/definitions/codersdk.AIBridgeBedrockConfig"
},
"enabled": {
"type": "boolean"
},
+23
View File
@@ -10364,12 +10364,35 @@
}
}
},
"codersdk.AIBridgeBedrockConfig": {
"type": "object",
"properties": {
"access_key": {
"type": "string"
},
"access_key_secret": {
"type": "string"
},
"model": {
"type": "string"
},
"region": {
"type": "string"
},
"small_fast_model": {
"type": "string"
}
}
},
"codersdk.AIBridgeConfig": {
"type": "object",
"properties": {
"anthropic": {
"$ref": "#/definitions/codersdk.AIBridgeAnthropicConfig"
},
"bedrock": {
"$ref": "#/definitions/codersdk.AIBridgeBedrockConfig"
},
"enabled": {
"type": "boolean"
},
+67 -3
View File
@@ -3280,18 +3280,73 @@ Write out the current server config as YAML to stdout.`,
Value: &c.AI.BridgeConfig.Anthropic.BaseURL,
Default: "https://api.anthropic.com/",
Group: &deploymentGroupAIBridge,
YAML: "base_url",
YAML: "anthropic_base_url",
Hidden: true,
},
{
Name: "AIBridge Anthropic KEY",
Name: "AIBridge Anthropic Key",
Description: "The key to authenticate against the Anthropic API.",
Flag: "aibridge-anthropic-key",
Env: "CODER_AIBRIDGE_ANTHROPIC_KEY",
Value: &c.AI.BridgeConfig.Anthropic.Key,
Default: "",
Group: &deploymentGroupAIBridge,
YAML: "key",
YAML: "anthropic_key",
Hidden: true,
},
{
Name: "AIBridge Bedrock Region",
Description: "The AWS Bedrock API region.",
Flag: "aibridge-bedrock-region",
Env: "CODER_AIBRIDGE_BEDROCK_REGION",
Value: &c.AI.BridgeConfig.Bedrock.Region,
Default: "",
Group: &deploymentGroupAIBridge,
YAML: "bedrock_region",
Hidden: true,
},
{
Name: "AIBridge Bedrock Access Key",
Description: "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.Bedrock.AccessKey,
Default: "",
Group: &deploymentGroupAIBridge,
YAML: "bedrock_access_key",
Hidden: true,
},
{
Name: "AIBridge Bedrock Access Key Secret",
Description: "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.Bedrock.AccessKeySecret,
Default: "",
Group: &deploymentGroupAIBridge,
YAML: "bedrock_access_key_secret",
Hidden: true,
},
{
Name: "AIBridge Bedrock Model",
Description: "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.Bedrock.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,
},
{
Name: "AIBridge 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: "aibridge-bedrock-small-fastmodel",
Env: "CODER_AIBRIDGE_BEDROCK_SMALL_FAST_MODEL",
Value: &c.AI.BridgeConfig.Bedrock.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,
},
{
@@ -3316,6 +3371,7 @@ type AIBridgeConfig struct {
Enabled serpent.Bool `json:"enabled" typescript:",notnull"`
OpenAI AIBridgeOpenAIConfig `json:"openai" typescript:",notnull"`
Anthropic AIBridgeAnthropicConfig `json:"anthropic" typescript:",notnull"`
Bedrock AIBridgeBedrockConfig `json:"bedrock" typescript:",notnull"`
}
type AIBridgeOpenAIConfig struct {
@@ -3328,6 +3384,14 @@ type AIBridgeAnthropicConfig struct {
Key serpent.String `json:"key" typescript:",notnull"`
}
type AIBridgeBedrockConfig struct {
Region serpent.String `json:"region" typescript:",notnull"`
AccessKey serpent.String `json:"access_key" typescript:",notnull"`
AccessKeySecret serpent.String `json:"access_key_secret" typescript:",notnull"`
Model serpent.String `json:"model" typescript:",notnull"`
SmallFastModel serpent.String `json:"small_fast_model" typescript:",notnull"`
}
type AIConfig struct {
BridgeConfig AIBridgeConfig `json:"bridge,omitempty"`
}
+7
View File
@@ -167,6 +167,13 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
"base_url": "string",
"key": "string"
},
"bedrock": {
"access_key": "string",
"access_key_secret": "string",
"model": "string",
"region": "string",
"small_fast_model": "string"
},
"enabled": true,
"openai": {
"base_url": "string",
+51
View File
@@ -351,6 +351,28 @@
| `base_url` | string | false | | |
| `key` | string | false | | |
## codersdk.AIBridgeBedrockConfig
```json
{
"access_key": "string",
"access_key_secret": "string",
"model": "string",
"region": "string",
"small_fast_model": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|---------------------|--------|----------|--------------|-------------|
| `access_key` | string | false | | |
| `access_key_secret` | string | false | | |
| `model` | string | false | | |
| `region` | string | false | | |
| `small_fast_model` | string | false | | |
## codersdk.AIBridgeConfig
```json
@@ -359,6 +381,13 @@
"base_url": "string",
"key": "string"
},
"bedrock": {
"access_key": "string",
"access_key_secret": "string",
"model": "string",
"region": "string",
"small_fast_model": "string"
},
"enabled": true,
"openai": {
"base_url": "string",
@@ -372,6 +401,7 @@
| Name | Type | Required | Restrictions | Description |
|-------------|----------------------------------------------------------------------|----------|--------------|-------------|
| `anthropic` | [codersdk.AIBridgeAnthropicConfig](#codersdkaibridgeanthropicconfig) | false | | |
| `bedrock` | [codersdk.AIBridgeBedrockConfig](#codersdkaibridgebedrockconfig) | false | | |
| `enabled` | boolean | false | | |
| `openai` | [codersdk.AIBridgeOpenAIConfig](#codersdkaibridgeopenaiconfig) | false | | |
@@ -654,6 +684,13 @@
"base_url": "string",
"key": "string"
},
"bedrock": {
"access_key": "string",
"access_key_secret": "string",
"model": "string",
"region": "string",
"small_fast_model": "string"
},
"enabled": true,
"openai": {
"base_url": "string",
@@ -2803,6 +2840,13 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
"base_url": "string",
"key": "string"
},
"bedrock": {
"access_key": "string",
"access_key_secret": "string",
"model": "string",
"region": "string",
"small_fast_model": "string"
},
"enabled": true,
"openai": {
"base_url": "string",
@@ -3310,6 +3354,13 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
"base_url": "string",
"key": "string"
},
"bedrock": {
"access_key": "string",
"access_key_secret": "string",
"model": "string",
"region": "string",
"small_fast_model": "string"
},
"enabled": true,
"openai": {
"base_url": "string",
+18 -3
View File
@@ -8,6 +8,7 @@ import (
"golang.org/x/xerrors"
"github.com/coder/aibridge"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd"
"github.com/coder/coder/v2/enterprise/x/aibridged"
)
@@ -20,14 +21,14 @@ func newAIBridgeDaemon(coderAPI *coderd.API) (*aibridged.Server, error) {
// Setup supported providers.
providers := []aibridge.Provider{
aibridge.NewOpenAIProvider(aibridge.ProviderConfig{
aibridge.NewOpenAIProvider(aibridge.OpenAIConfig{
BaseURL: coderAPI.DeploymentValues.AI.BridgeConfig.OpenAI.BaseURL.String(),
Key: coderAPI.DeploymentValues.AI.BridgeConfig.OpenAI.Key.String(),
}),
aibridge.NewAnthropicProvider(aibridge.ProviderConfig{
aibridge.NewAnthropicProvider(aibridge.AnthropicConfig{
BaseURL: coderAPI.DeploymentValues.AI.BridgeConfig.Anthropic.BaseURL.String(),
Key: coderAPI.DeploymentValues.AI.BridgeConfig.Anthropic.Key.String(),
}),
}, getBedrockConfig(coderAPI.DeploymentValues.AI.BridgeConfig.Bedrock)),
}
// Create pool for reusable stateful [aibridge.RequestBridge] instances (one per user).
@@ -45,3 +46,17 @@ func newAIBridgeDaemon(coderAPI *coderd.API) (*aibridged.Server, error) {
}
return srv, nil
}
func getBedrockConfig(cfg codersdk.AIBridgeBedrockConfig) *aibridge.AWSBedrockConfig {
if cfg.Region.String() == "" && cfg.AccessKey.String() == "" && cfg.AccessKeySecret.String() == "" {
return nil
}
return &aibridge.AWSBedrockConfig{
Region: cfg.Region.String(),
AccessKey: cfg.AccessKey.String(),
AccessKeySecret: cfg.AccessKeySecret.String(),
Model: cfg.Model.String(),
SmallFastModel: cfg.SmallFastModel.String(),
}
}
@@ -164,7 +164,7 @@ func TestIntegration(t *testing.T) {
require.NoError(t, err)
logger := testutil.Logger(t)
providers := []aibridge.Provider{aibridge.NewOpenAIProvider(aibridge.ProviderConfig{BaseURL: mockOpenAI.URL})}
providers := []aibridge.Provider{aibridge.NewOpenAIProvider(aibridge.OpenAIConfig{BaseURL: mockOpenAI.URL})}
pool, err := aibridged.NewCachedBridgePool(aibridged.DefaultPoolOptions, providers, logger)
require.NoError(t, err)
+2 -2
View File
@@ -288,8 +288,8 @@ func TestRouting(t *testing.T) {
client := mock.NewMockDRPCClient(ctrl)
providers := []aibridge.Provider{
aibridge.NewOpenAIProvider(aibridge.ProviderConfig{BaseURL: openaiSrv.URL}),
aibridge.NewAnthropicProvider(aibridge.ProviderConfig{BaseURL: antSrv.URL}),
aibridge.NewOpenAIProvider(aibridge.OpenAIConfig{BaseURL: openaiSrv.URL}),
aibridge.NewAnthropicProvider(aibridge.AnthropicConfig{BaseURL: antSrv.URL}, nil),
}
pool, err := aibridged.NewCachedBridgePool(aibridged.DefaultPoolOptions, providers, logger)
require.NoError(t, err)
+2 -1
View File
@@ -476,7 +476,7 @@ require (
github.com/anthropics/anthropic-sdk-go v1.13.0
github.com/brianvoe/gofakeit/v7 v7.8.0
github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225
github.com/coder/aibridge v0.1.5
github.com/coder/aibridge v0.1.6
github.com/coder/aisdk-go v0.0.9
github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945
github.com/coder/preview v1.0.4
@@ -509,6 +509,7 @@ require (
github.com/aquasecurity/trivy v0.61.1-0.20250407075540-f1329c7ea1aa // indirect
github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/buger/jsonparser v1.1.1 // indirect
+4 -2
View File
@@ -764,6 +764,8 @@ github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11/go.mod h1:dd+Lkp6YmMryke+qxW/VnKyhMBDTYP41Q2Bb+6gNZgY=
github.com/aws/aws-sdk-go-v2/config v1.31.3 h1:RIb3yr/+PZ18YYNe6MDiG/3jVoJrPmdoCARwNkMGvco=
github.com/aws/aws-sdk-go-v2/config v1.31.3/go.mod h1:jjgx1n7x0FAKl6TnakqrpkHWWKcX3xfWtdnIJs5K9CE=
github.com/aws/aws-sdk-go-v2/credentials v1.18.7 h1:zqg4OMrKj+t5HlswDApgvAHjxKtlduKS7KicXB+7RLg=
@@ -915,8 +917,8 @@ github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 h1:tRIViZ5JRmzdOEo5wUWngaGEFBG8OaE1o2GIHN5ujJ8=
github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225/go.mod h1:rNLVpYgEVeu1Zk29K64z6Od8RBP9DwqCu9OfCzh8MR4=
github.com/coder/aibridge v0.1.5 h1:uSrltfLZWF2qOaq9RDzJW/26Ow1wMFwcwObBM0WikME=
github.com/coder/aibridge v0.1.5/go.mod h1:Q5MCfKMcKYmYl4qH1Zd0rltmPaUBPKFvIPs2k9q6qeY=
github.com/coder/aibridge v0.1.6 h1:Ax9hfAQeg+vLeE9M/IMmARuBuGyCuFdMw8bpy72/fD8=
github.com/coder/aibridge v0.1.6/go.mod h1:7GhrLbzf6uM3sCA7OPaDzvq9QNrCjNuzMy+WgipYwfQ=
github.com/coder/aisdk-go v0.0.9 h1:Vzo/k2qwVGLTR10ESDeP2Ecek1SdPfZlEjtTfMveiVo=
github.com/coder/aisdk-go v0.0.9/go.mod h1:KF6/Vkono0FJJOtWtveh5j7yfNrSctVTpwgweYWSp5M=
github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945 h1:hDUf02kTX8EGR3+5B+v5KdYvORs4YNfDPci0zCs+pC0=
+10
View File
@@ -16,11 +16,21 @@ export interface AIBridgeAnthropicConfig {
readonly key: string;
}
// From codersdk/deployment.go
export interface AIBridgeBedrockConfig {
readonly region: string;
readonly access_key: string;
readonly access_key_secret: string;
readonly model: string;
readonly small_fast_model: string;
}
// From codersdk/deployment.go
export interface AIBridgeConfig {
readonly enabled: boolean;
readonly openai: AIBridgeOpenAIConfig;
readonly anthropic: AIBridgeAnthropicConfig;
readonly bedrock: AIBridgeBedrockConfig;
}
// From codersdk/aibridge.go