fix(bedrock): inference profile prefix resolution#189
fix(bedrock): inference profile prefix resolution#189meowgorithm wants to merge 1 commit intomainfrom
Conversation
Resolve Bedrock model IDs using AWS region-group prefixes with AWS_DEFAULT_REGION fallback, preserve already-prefixed IDs, and default to global for unmapped regions. Add tests to lock in mapping and prefixing behavior. 💘 Generated with Crush Assisted-by: GPT-5.3 Codex via Crush <crush@charm.land>
There was a problem hiding this comment.
Pull request overview
Resolves AWS Bedrock model ID prefixing by mapping AWS regions to Bedrock “region-group” inference profile prefixes, with an added AWS_DEFAULT_REGION fallback.
Changes:
- Updated Bedrock auth/config region selection to use
AWS_REGIONwithAWS_DEFAULT_REGIONfallback. - Reworked Bedrock model ID prefixing to use region-group prefixes (
us,eu,jp,au,global) instead of the prior 2-letter region-derived prefix. - Added unit tests covering prefixing behavior and region-to-prefix mapping.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| providers/anthropic/bedrock.go | Adds region resolution helper, region→prefix mapping, and prefix detection to correctly form Bedrock inference profile model IDs. |
| providers/anthropic/anthropic_test.go | Adds tests verifying prefixing behavior, env var precedence, and region→prefix mapping. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func awsRegion() string { | ||
| return cmp.Or(os.Getenv("AWS_REGION"), os.Getenv("AWS_DEFAULT_REGION")) | ||
| } |
There was a problem hiding this comment.
awsRegion() only checks AWS_REGION/AWS_DEFAULT_REGION. When Bedrock is used with default AWS auth, config.LoadDefaultConfig can resolve the region from shared config/IMDS even when both env vars are unset; in that case model prefixing will still default to us-east-1 ("us") and can generate the wrong inference-profile prefix. Consider passing the resolved AWS config region into the prefixing logic (or changing this helper to accept a region parameter) so the model ID prefix matches the actual region the SDK will use.
There was a problem hiding this comment.
I'm not 100% sure about this one. Adding the awsRegion() function seems to do 2 things:
- duplicate the logic of which region is supposed to be selected, which is done by aws'
config.LoadDefaultConfig - could eventually lead to a situation where if no AWS_REGION / AWS_DEFAULT_REGION is set we run into models with the wrong prefix, because we're prefixing the model before resolving the region by aws' default helper (check
resolveConfigLoadersin config.go in the config@v1.32.12)
Maybe we can take a safer path and just do the following:
- get the default aws config from its config library
- if an API key is present/ no-auth was passed, we update the config with an API key
- if not, we move on with the parsed config
- if an error happened during parsing - we fallback to the default region
- BONUS: no if-else :)
// keep the model prefix enrichment method, but also add the resolved region instead of using the awsRegion helper
func bedrockPrefixModelWithRegion(modelID, region string) string {
if hasBedrockInferenceProfilePrefix(modelID) {
return modelID
}
return bedrockRegionPrefix(cmp.Or(region, "us-east-1")) + "." + modelID
}
// in anthropic.go
if a.options.useBedrock {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
cfg = aws.Config{}
}
// we inline the logic of the bedrockBasicAuthConfig function (or just call it)
// if the user has no region configured, we fallback to us-east
cfg.Region = cmp.Or(cfg.Region, "us-east-1")
if a.options.skipAuth || a.options.apiKey != "" {
cfg.BearerAuthTokenProvider = bearer.StaticTokenProvider{
Token: bearer.Token{Value: a.options.apiKey},
}
}
// now we update the model prefix, as we're sure about the region
// and update the client options
modelID = bedrockPrefixModelWithRegion(modelID, cfg.Region)
clientOptions = append(clientOptions, bedrock.WithConfig(cfg))
if a.options.baseURL != "" {
clientOptions = append(clientOptions, option.WithBaseURL(a.options.baseURL)
}
}
return languageModel{
modelID: modelID,
provider: a.options.name,
options: a.options,
client: anthropic.NewClient(clientOptions...),
}, nil
}I hope that makes sense. We get the win of relying just on AWS "stdlib" to get the configuration and we still get to overlay the bedrock API key, if needed.
| require.Equal(t, "global", bedrockRegionPrefix("ap-south-1")) | ||
| require.Equal(t, "global", bedrockRegionPrefix("sa-east-1")) |
There was a problem hiding this comment.
| require.Equal(t, "global", bedrockRegionPrefix("ap-south-1")) | |
| require.Equal(t, "global", bedrockRegionPrefix("sa-east-1")) | |
| require.Equal(t, "apac", bedrockRegionPrefix("ap-south-1")) | |
| require.Equal(t, "apac", bedrockRegionPrefix("ap-northeast-2")) | |
| require.Equal(t, "global", bedrockRegionPrefix("ap-south-1")) | |
| require.Equal(t, "global", bedrockRegionPrefix("sa-east-1")) |
| case region == "ap-southeast-2": | ||
| return "au" |
There was a problem hiding this comment.
Let's add the missing case again, to make sure we support the apac region as well:
| case region == "ap-southeast-2": | |
| return "au" | |
| case region == "ap-southeast-2": | |
| return "au" | |
| case strings.HasPrefix(region, "ap-"): | |
| return "apac" |
| } | ||
|
|
||
| func hasBedrockInferenceProfilePrefix(modelID string) bool { | ||
| for _, prefix := range []string{"us.", "eu.", "jp.", "au.", "global."} { |
There was a problem hiding this comment.
And finally, the missing apac prefix should be added here as well:
| for _, prefix := range []string{"us.", "eu.", "jp.", "au.", "global."} { | |
| for _, prefix := range []string{"us.", "eu.", "jp.", "au.", "apac.", "global."} { |
| func awsRegion() string { | ||
| return cmp.Or(os.Getenv("AWS_REGION"), os.Getenv("AWS_DEFAULT_REGION")) | ||
| } |
There was a problem hiding this comment.
I'm not 100% sure about this one. Adding the awsRegion() function seems to do 2 things:
- duplicate the logic of which region is supposed to be selected, which is done by aws'
config.LoadDefaultConfig - could eventually lead to a situation where if no AWS_REGION / AWS_DEFAULT_REGION is set we run into models with the wrong prefix, because we're prefixing the model before resolving the region by aws' default helper (check
resolveConfigLoadersin config.go in the config@v1.32.12)
Maybe we can take a safer path and just do the following:
- get the default aws config from its config library
- if an API key is present/ no-auth was passed, we update the config with an API key
- if not, we move on with the parsed config
- if an error happened during parsing - we fallback to the default region
- BONUS: no if-else :)
// keep the model prefix enrichment method, but also add the resolved region instead of using the awsRegion helper
func bedrockPrefixModelWithRegion(modelID, region string) string {
if hasBedrockInferenceProfilePrefix(modelID) {
return modelID
}
return bedrockRegionPrefix(cmp.Or(region, "us-east-1")) + "." + modelID
}
// in anthropic.go
if a.options.useBedrock {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
cfg = aws.Config{}
}
// we inline the logic of the bedrockBasicAuthConfig function (or just call it)
// if the user has no region configured, we fallback to us-east
cfg.Region = cmp.Or(cfg.Region, "us-east-1")
if a.options.skipAuth || a.options.apiKey != "" {
cfg.BearerAuthTokenProvider = bearer.StaticTokenProvider{
Token: bearer.Token{Value: a.options.apiKey},
}
}
// now we update the model prefix, as we're sure about the region
// and update the client options
modelID = bedrockPrefixModelWithRegion(modelID, cfg.Region)
clientOptions = append(clientOptions, bedrock.WithConfig(cfg))
if a.options.baseURL != "" {
clientOptions = append(clientOptions, option.WithBaseURL(a.options.baseURL)
}
}
return languageModel{
modelID: modelID,
provider: a.options.name,
options: a.options,
client: anthropic.NewClient(clientOptions...),
}, nil
}I hope that makes sense. We get the win of relying just on AWS "stdlib" to get the configuration and we still get to overlay the bedrock API key, if needed.
This PR resolves Bedrock model IDs using
AWSregion-group prefixes withAWS_DEFAULT_REGIONfallback.Related: charmbracelet/catwalk#224