Skip to content

Commit eac0d62

Browse files
fix: support AWS GovCloud and China region ARNs in Bedrock provider (#10157)
Co-authored-by: Roo Code <[email protected]>
1 parent affa5f2 commit eac0d62

File tree

2 files changed

+109
-3
lines changed

2 files changed

+109
-3
lines changed

src/api/providers/__tests__/bedrock.spec.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => {
3636

3737
import { AwsBedrockHandler } from "../bedrock"
3838
import { ConverseStreamCommand, BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime"
39-
import { BEDROCK_1M_CONTEXT_MODEL_IDS, BEDROCK_SERVICE_TIER_MODEL_IDS, bedrockModels, ApiProviderError } from "@roo-code/types"
39+
import {
40+
BEDROCK_1M_CONTEXT_MODEL_IDS,
41+
BEDROCK_SERVICE_TIER_MODEL_IDS,
42+
bedrockModels,
43+
ApiProviderError,
44+
} from "@roo-code/types"
4045

4146
import type { Anthropic } from "@anthropic-ai/sdk"
4247

@@ -371,6 +376,103 @@ describe("AwsBedrockHandler", () => {
371376
expect(result.modelId).toBe("ap.anthropic.claude-3-5-sonnet-20241022-v2:0") // Should be preserved as-is
372377
})
373378
})
379+
380+
describe("AWS GovCloud and China partition support", () => {
381+
it("should parse AWS GovCloud ARNs (arn:aws-us-gov:bedrock:...)", () => {
382+
const handler = new AwsBedrockHandler({
383+
apiModelId: "test",
384+
awsAccessKey: "test",
385+
awsSecretKey: "test",
386+
awsRegion: "us-gov-west-1",
387+
})
388+
389+
const parseArn = (handler as any).parseArn.bind(handler)
390+
391+
const result = parseArn(
392+
"arn:aws-us-gov:bedrock:us-gov-west-1:123456789012:inference-profile/us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0",
393+
)
394+
395+
expect(result.isValid).toBe(true)
396+
expect(result.region).toBe("us-gov-west-1")
397+
expect(result.modelType).toBe("inference-profile")
398+
})
399+
400+
it("should parse AWS China ARNs (arn:aws-cn:bedrock:...)", () => {
401+
const handler = new AwsBedrockHandler({
402+
apiModelId: "test",
403+
awsAccessKey: "test",
404+
awsSecretKey: "test",
405+
awsRegion: "cn-north-1",
406+
})
407+
408+
const parseArn = (handler as any).parseArn.bind(handler)
409+
410+
const result = parseArn(
411+
"arn:aws-cn:bedrock:cn-north-1:123456789012:inference-profile/anthropic.claude-3-sonnet-20240229-v1:0",
412+
)
413+
414+
expect(result.isValid).toBe(true)
415+
expect(result.region).toBe("cn-north-1")
416+
expect(result.modelType).toBe("inference-profile")
417+
})
418+
419+
it("should accept GovCloud custom ARN in handler constructor", () => {
420+
const handler = new AwsBedrockHandler({
421+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
422+
awsAccessKey: "test-access-key",
423+
awsSecretKey: "test-secret-key",
424+
awsRegion: "us-gov-west-1",
425+
awsCustomArn:
426+
"arn:aws-us-gov:bedrock:us-gov-west-1:123456789012:inference-profile/us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0",
427+
})
428+
429+
// Should not throw and should return valid model info
430+
const modelInfo = handler.getModel()
431+
expect(modelInfo.id).toBe(
432+
"arn:aws-us-gov:bedrock:us-gov-west-1:123456789012:inference-profile/us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0",
433+
)
434+
expect(modelInfo.info).toBeDefined()
435+
})
436+
437+
it("should accept China region custom ARN in handler constructor", () => {
438+
const handler = new AwsBedrockHandler({
439+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
440+
awsAccessKey: "test-access-key",
441+
awsSecretKey: "test-secret-key",
442+
awsRegion: "cn-north-1",
443+
awsCustomArn:
444+
"arn:aws-cn:bedrock:cn-north-1:123456789012:inference-profile/anthropic.claude-3-sonnet-20240229-v1:0",
445+
})
446+
447+
// Should not throw and should return valid model info
448+
const modelInfo = handler.getModel()
449+
expect(modelInfo.id).toBe(
450+
"arn:aws-cn:bedrock:cn-north-1:123456789012:inference-profile/anthropic.claude-3-sonnet-20240229-v1:0",
451+
)
452+
expect(modelInfo.info).toBeDefined()
453+
})
454+
455+
it("should detect region mismatch in GovCloud ARN", () => {
456+
const handler = new AwsBedrockHandler({
457+
apiModelId: "test",
458+
awsAccessKey: "test",
459+
awsSecretKey: "test",
460+
awsRegion: "us-east-1",
461+
})
462+
463+
const parseArn = (handler as any).parseArn.bind(handler)
464+
465+
// Region in ARN (us-gov-west-1) doesn't match provided region (us-east-1)
466+
const result = parseArn(
467+
"arn:aws-us-gov:bedrock:us-gov-west-1:123456789012:inference-profile/us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0",
468+
"us-east-1",
469+
)
470+
471+
expect(result.isValid).toBe(true)
472+
expect(result.region).toBe("us-gov-west-1")
473+
expect(result.errorMessage).toContain("Region mismatch")
474+
})
475+
})
374476
})
375477

376478
describe("image handling", () => {

src/api/providers/bedrock.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,22 +928,26 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
928928
* represent literal characters in the AWS ARN format, not filesystem paths. This regex will function consistently across Windows,
929929
* macOS, Linux, and any other operating system where JavaScript runs.
930930
*
931+
* Supports any AWS partition (aws, aws-us-gov, aws-cn, or future partitions).
932+
* The partition is not captured since we don't need to use it.
933+
*
931934
* This matches ARNs like:
932935
* - Foundation Model: arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-v2
936+
* - GovCloud Inference Profile: arn:aws-us-gov:bedrock:us-gov-west-1:123456789012:inference-profile/us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0
933937
* - Prompt Router: arn:aws:bedrock:us-west-2:123456789012:prompt-router/anthropic-claude
934938
* - Inference Profile: arn:aws:bedrock:us-west-2:123456789012:inference-profile/anthropic.claude-v2
935939
* - Cross Region Inference Profile: arn:aws:bedrock:us-west-2:123456789012:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0
936940
* - Custom Model (Provisioned Throughput): arn:aws:bedrock:us-west-2:123456789012:provisioned-model/my-custom-model
937941
* - Imported Model: arn:aws:bedrock:us-west-2:123456789012:imported-model/my-imported-model
938942
*
939943
* match[0] - The entire matched string
940-
* match[1] - The region (e.g., "us-east-1")
944+
* match[1] - The region (e.g., "us-east-1", "us-gov-west-1")
941945
* match[2] - The account ID (can be empty string for AWS-managed resources)
942946
* match[3] - The resource type (e.g., "foundation-model")
943947
* match[4] - The resource ID (e.g., "anthropic.claude-3-sonnet-20240229-v1:0")
944948
*/
945949

946-
const arnRegex = /^arn:aws:(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^\/]+)\/([\w\.\-:]+)|([^\/]+))$/
950+
const arnRegex = /^arn:[^:]+:(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^\/]+)\/([\w\.\-:]+)|([^\/]+))$/
947951
let match = arn.match(arnRegex)
948952

949953
if (match && match[1] && match[3] && match[4]) {

0 commit comments

Comments
 (0)