From ff9f2566a7a6d6cb68b79366d8aa970bc368ddc8 Mon Sep 17 00:00:00 2001 From: Aldous Date: Wed, 2 Sep 2026 11:37:30 -0400 Subject: [PATCH] fix(anthropic): don't double-prefix Bedrock cross-region inference profile model IDs --- providers/anthropic/bedrock.go | 30 +++++++++++--- providers/anthropic/bedrock_test.go | 62 +++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 providers/anthropic/bedrock_test.go diff --git a/providers/anthropic/bedrock.go b/providers/anthropic/bedrock.go index f036dde1e..b49499ced 100644 --- a/providers/anthropic/bedrock.go +++ b/providers/anthropic/bedrock.go @@ -22,14 +22,34 @@ func bedrockBasicAuthConfig(apiKey, region string) aws.Config { } } +// bedrockInferenceProfilePrefixes are the cross-region inference +// profile prefixes Bedrock model IDs may already carry. IDs starting +// with one of these are fully qualified and must not be prefixed +// again with the caller's region. +var bedrockInferenceProfilePrefixes = []string{ + "global.", + "us-gov.", + "us.", + "eu.", + "apac.", + "ap.", + "jp.", + "au.", + "ca.", + "sa.", + "il.", + "mx.", +} + func bedrockPrefixModelWithRegion(modelID, region string) string { + for _, prefix := range bedrockInferenceProfilePrefixes { + if strings.HasPrefix(modelID, prefix) { + return modelID + } + } region = resolveBedrockRegion(region) if len(region) < 2 { return modelID } - prefix := region[:2] + "." - if strings.HasPrefix(modelID, prefix) { - return modelID - } - return prefix + modelID + return region[:2] + "." + modelID } diff --git a/providers/anthropic/bedrock_test.go b/providers/anthropic/bedrock_test.go new file mode 100644 index 000000000..4fe7d189a --- /dev/null +++ b/providers/anthropic/bedrock_test.go @@ -0,0 +1,62 @@ +package anthropic + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBedrockPrefixModelWithRegion(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + modelID string + region string + want string + }{ + { + name: "bare model id gets region prefix", + modelID: "anthropic.claude-fable-5-1", + region: "us-east-1", + want: "us.anthropic.claude-fable-5-1", + }, + { + name: "matching geo prefix is preserved", + modelID: "us.anthropic.claude-fable-5-1", + region: "us-east-1", + want: "us.anthropic.claude-fable-5-1", + }, + { + name: "global inference profile is preserved", + modelID: "global.anthropic.claude-fable-5-1", + region: "us-east-1", + want: "global.anthropic.claude-fable-5-1", + }, + { + name: "global inference profile is preserved outside us", + modelID: "global.anthropic.claude-fable-5-1", + region: "eu-west-1", + want: "global.anthropic.claude-fable-5-1", + }, + { + name: "apac profile is not double prefixed", + modelID: "apac.anthropic.claude-fable-5-1", + region: "ap-southeast-2", + want: "apac.anthropic.claude-fable-5-1", + }, + { + name: "cross geo profile is preserved", + modelID: "eu.anthropic.claude-fable-5-1", + region: "us-east-1", + want: "eu.anthropic.claude-fable-5-1", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + require.Equal(t, test.want, bedrockPrefixModelWithRegion(test.modelID, test.region)) + }) + } +}