bedrock: Support global endpoints and new regional endpoints (#44103)

Closes #43598

Release Notes:

- bedrock: Added opt-in `allow_global` which enables global endpoints
- bedrock: Updated cross-region-inference endpoint and model list
- bedrock: Fixed Opus 4.5 access on Bedrock, now only accessible through the `allow_global` setting
This commit is contained in:
Shardul Vaidya
2025-12-04 06:14:31 -05:00
committed by GitHub
parent 9db0d66251
commit 0f0017dc8e
5 changed files with 186 additions and 61 deletions

View File

@@ -584,41 +584,100 @@ impl Model {
}
}
pub fn cross_region_inference_id(&self, region: &str) -> anyhow::Result<String> {
pub fn cross_region_inference_id(
&self,
region: &str,
allow_global: bool,
) -> anyhow::Result<String> {
// List derived from here:
// https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html#inference-profiles-support-system
let model_id = self.request_id();
let supports_global = matches!(
self,
Model::ClaudeOpus4_5
| Model::ClaudeOpus4_5Thinking
| Model::ClaudeHaiku4_5
| Model::ClaudeSonnet4
| Model::ClaudeSonnet4Thinking
| Model::ClaudeSonnet4_5
| Model::ClaudeSonnet4_5Thinking
);
let region_group = if region.starts_with("us-gov-") {
"us-gov"
} else if region.starts_with("us-") {
"us"
} else if region.starts_with("us-")
|| region.starts_with("ca-")
|| region.starts_with("sa-")
{
if allow_global && supports_global {
"global"
} else {
"us"
}
} else if region.starts_with("eu-") {
"eu"
if allow_global && supports_global {
"global"
} else {
"eu"
}
} else if region.starts_with("ap-") || region == "me-central-1" || region == "me-south-1" {
"apac"
} else if region.starts_with("ca-") || region.starts_with("sa-") {
// Canada and South America regions - default to US profiles
"us"
if allow_global && supports_global {
"global"
} else {
"apac"
}
} else {
anyhow::bail!("Unsupported Region {region}");
};
let model_id = self.request_id();
match (self, region_group, region) {
(Model::Custom { .. }, _, _) => Ok(self.request_id().into()),
match (self, region_group) {
// Custom models can't have CRI IDs
(Model::Custom { .. }, _) => Ok(self.request_id().into()),
(
Model::ClaudeOpus4_5
| Model::ClaudeOpus4_5Thinking
| Model::ClaudeHaiku4_5
| Model::ClaudeSonnet4
| Model::ClaudeSonnet4Thinking
| Model::ClaudeSonnet4_5
| Model::ClaudeSonnet4_5Thinking,
"global",
_,
) => Ok(format!("{}.{}", region_group, model_id)),
// Models with US Gov only
(Model::Claude3_5Sonnet, "us-gov") | (Model::Claude3Haiku, "us-gov") => {
Ok(format!("{}.{}", region_group, model_id))
(
Model::Claude3Haiku
| Model::Claude3_5Sonnet
| Model::Claude3_7Sonnet
| Model::Claude3_7SonnetThinking
| Model::ClaudeSonnet4_5
| Model::ClaudeSonnet4_5Thinking,
"us-gov",
_,
) => Ok(format!("{}.{}", region_group, model_id)),
(
Model::ClaudeHaiku4_5 | Model::ClaudeSonnet4_5 | Model::ClaudeSonnet4_5Thinking,
"apac",
"ap-southeast-2" | "ap-southeast-4",
) => Ok(format!("au.{}", model_id)),
(
Model::ClaudeHaiku4_5 | Model::ClaudeSonnet4_5 | Model::ClaudeSonnet4_5Thinking,
"apac",
"ap-northeast-1" | "ap-northeast-3",
) => Ok(format!("jp.{}", model_id)),
(Model::AmazonNovaLite, "us", r) if r.starts_with("ca-") => {
Ok(format!("ca.{}", model_id))
}
// Available everywhere
(Model::AmazonNovaLite | Model::AmazonNovaMicro | Model::AmazonNovaPro, _) => {
Ok(format!("{}.{}", region_group, model_id))
}
// Models in US
(
Model::AmazonNovaPremier
| Model::AmazonNovaLite
| Model::AmazonNovaMicro
| Model::AmazonNovaPro
| Model::Claude3_5Haiku
| Model::ClaudeHaiku4_5
| Model::Claude3_5Sonnet
@@ -655,16 +714,18 @@ impl Model {
| Model::PalmyraWriterX4
| Model::PalmyraWriterX5,
"us",
_,
) => Ok(format!("{}.{}", region_group, model_id)),
// Models available in EU
(
Model::Claude3_5Sonnet
Model::AmazonNovaLite
| Model::AmazonNovaMicro
| Model::AmazonNovaPro
| Model::Claude3_5Sonnet
| Model::ClaudeHaiku4_5
| Model::Claude3_7Sonnet
| Model::Claude3_7SonnetThinking
| Model::ClaudeSonnet4
| Model::ClaudeSonnet4Thinking
| Model::ClaudeSonnet4_5
| Model::ClaudeSonnet4_5Thinking
| Model::Claude3Haiku
@@ -673,26 +734,26 @@ impl Model {
| Model::MetaLlama323BInstructV1
| Model::MistralPixtralLarge2502V1,
"eu",
_,
) => Ok(format!("{}.{}", region_group, model_id)),
// Models available in APAC
(
Model::Claude3_5Sonnet
Model::AmazonNovaLite
| Model::AmazonNovaMicro
| Model::AmazonNovaPro
| Model::Claude3_5Sonnet
| Model::Claude3_5SonnetV2
| Model::ClaudeHaiku4_5
| Model::Claude3Haiku
| Model::Claude3Sonnet
| Model::Claude3_7Sonnet
| Model::Claude3_7SonnetThinking
| Model::ClaudeSonnet4
| Model::ClaudeSonnet4Thinking
| Model::ClaudeSonnet4_5
| Model::ClaudeSonnet4_5Thinking,
| Model::Claude3Haiku
| Model::Claude3Sonnet,
"apac",
_,
) => Ok(format!("{}.{}", region_group, model_id)),
// Any other combination is not supported
_ => Ok(self.request_id().into()),
_ => Ok(model_id.into()),
}
}
}
@@ -705,15 +766,15 @@ mod tests {
fn test_us_region_inference_ids() -> anyhow::Result<()> {
// Test US regions
assert_eq!(
Model::Claude3_5SonnetV2.cross_region_inference_id("us-east-1")?,
Model::Claude3_5SonnetV2.cross_region_inference_id("us-east-1", false)?,
"us.anthropic.claude-3-5-sonnet-20241022-v2:0"
);
assert_eq!(
Model::Claude3_5SonnetV2.cross_region_inference_id("us-west-2")?,
Model::Claude3_5SonnetV2.cross_region_inference_id("us-west-2", false)?,
"us.anthropic.claude-3-5-sonnet-20241022-v2:0"
);
assert_eq!(
Model::AmazonNovaPro.cross_region_inference_id("us-east-2")?,
Model::AmazonNovaPro.cross_region_inference_id("us-east-2", false)?,
"us.amazon.nova-pro-v1:0"
);
Ok(())
@@ -723,19 +784,19 @@ mod tests {
fn test_eu_region_inference_ids() -> anyhow::Result<()> {
// Test European regions
assert_eq!(
Model::ClaudeSonnet4.cross_region_inference_id("eu-west-1")?,
Model::ClaudeSonnet4.cross_region_inference_id("eu-west-1", false)?,
"eu.anthropic.claude-sonnet-4-20250514-v1:0"
);
assert_eq!(
Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1")?,
Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1", false)?,
"eu.anthropic.claude-sonnet-4-5-20250929-v1:0"
);
assert_eq!(
Model::Claude3Sonnet.cross_region_inference_id("eu-west-1")?,
Model::Claude3Sonnet.cross_region_inference_id("eu-west-1", false)?,
"eu.anthropic.claude-3-sonnet-20240229-v1:0"
);
assert_eq!(
Model::AmazonNovaMicro.cross_region_inference_id("eu-north-1")?,
Model::AmazonNovaMicro.cross_region_inference_id("eu-north-1", false)?,
"eu.amazon.nova-micro-v1:0"
);
Ok(())
@@ -745,15 +806,15 @@ mod tests {
fn test_apac_region_inference_ids() -> anyhow::Result<()> {
// Test Asia-Pacific regions
assert_eq!(
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-northeast-1")?,
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-northeast-1", false)?,
"apac.anthropic.claude-3-5-sonnet-20241022-v2:0"
);
assert_eq!(
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-southeast-2")?,
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-southeast-2", false)?,
"apac.anthropic.claude-3-5-sonnet-20241022-v2:0"
);
assert_eq!(
Model::AmazonNovaLite.cross_region_inference_id("ap-south-1")?,
Model::AmazonNovaLite.cross_region_inference_id("ap-south-1", false)?,
"apac.amazon.nova-lite-v1:0"
);
Ok(())
@@ -763,11 +824,11 @@ mod tests {
fn test_gov_region_inference_ids() -> anyhow::Result<()> {
// Test Government regions
assert_eq!(
Model::Claude3_5Sonnet.cross_region_inference_id("us-gov-east-1")?,
Model::Claude3_5Sonnet.cross_region_inference_id("us-gov-east-1", false)?,
"us-gov.anthropic.claude-3-5-sonnet-20240620-v1:0"
);
assert_eq!(
Model::Claude3Haiku.cross_region_inference_id("us-gov-west-1")?,
Model::Claude3Haiku.cross_region_inference_id("us-gov-west-1", false)?,
"us-gov.anthropic.claude-3-haiku-20240307-v1:0"
);
Ok(())
@@ -777,15 +838,15 @@ mod tests {
fn test_meta_models_inference_ids() -> anyhow::Result<()> {
// Test Meta models
assert_eq!(
Model::MetaLlama370BInstructV1.cross_region_inference_id("us-east-1")?,
Model::MetaLlama370BInstructV1.cross_region_inference_id("us-east-1", false)?,
"meta.llama3-70b-instruct-v1:0"
);
assert_eq!(
Model::MetaLlama3170BInstructV1.cross_region_inference_id("us-east-1")?,
Model::MetaLlama3170BInstructV1.cross_region_inference_id("us-east-1", false)?,
"us.meta.llama3-1-70b-instruct-v1:0"
);
assert_eq!(
Model::MetaLlama321BInstructV1.cross_region_inference_id("eu-west-1")?,
Model::MetaLlama321BInstructV1.cross_region_inference_id("eu-west-1", false)?,
"eu.meta.llama3-2-1b-instruct-v1:0"
);
Ok(())
@@ -796,11 +857,11 @@ mod tests {
// Mistral models don't follow the regional prefix pattern,
// so they should return their original IDs
assert_eq!(
Model::MistralMistralLarge2402V1.cross_region_inference_id("us-east-1")?,
Model::MistralMistralLarge2402V1.cross_region_inference_id("us-east-1", false)?,
"mistral.mistral-large-2402-v1:0"
);
assert_eq!(
Model::MistralMixtral8x7BInstructV0.cross_region_inference_id("eu-west-1")?,
Model::MistralMixtral8x7BInstructV0.cross_region_inference_id("eu-west-1", false)?,
"mistral.mixtral-8x7b-instruct-v0:1"
);
Ok(())
@@ -811,11 +872,11 @@ mod tests {
// AI21 models don't follow the regional prefix pattern,
// so they should return their original IDs
assert_eq!(
Model::AI21J2UltraV1.cross_region_inference_id("us-east-1")?,
Model::AI21J2UltraV1.cross_region_inference_id("us-east-1", false)?,
"ai21.j2-ultra-v1"
);
assert_eq!(
Model::AI21JambaInstructV1.cross_region_inference_id("eu-west-1")?,
Model::AI21JambaInstructV1.cross_region_inference_id("eu-west-1", false)?,
"ai21.jamba-instruct-v1:0"
);
Ok(())
@@ -826,11 +887,11 @@ mod tests {
// Cohere models don't follow the regional prefix pattern,
// so they should return their original IDs
assert_eq!(
Model::CohereCommandRV1.cross_region_inference_id("us-east-1")?,
Model::CohereCommandRV1.cross_region_inference_id("us-east-1", false)?,
"cohere.command-r-v1:0"
);
assert_eq!(
Model::CohereCommandTextV14_4k.cross_region_inference_id("ap-southeast-1")?,
Model::CohereCommandTextV14_4k.cross_region_inference_id("ap-southeast-1", false)?,
"cohere.command-text-v14:7:4k"
);
Ok(())
@@ -850,10 +911,17 @@ mod tests {
// Custom model should return its name unchanged
assert_eq!(
custom_model.cross_region_inference_id("us-east-1")?,
custom_model.cross_region_inference_id("us-east-1", false)?,
"custom.my-model-v1:0"
);
// Test that models without global support fall back to regional when allow_global is true
assert_eq!(
Model::AmazonNovaPro.cross_region_inference_id("us-east-1", true)?,
"us.amazon.nova-pro-v1:0",
"Nova Pro should fall back to regional profile even when allow_global is true"
);
Ok(())
}
@@ -892,3 +960,28 @@ mod tests {
);
}
}
#[test]
fn test_global_inference_ids() -> anyhow::Result<()> {
// Test global inference for models that support it when allow_global is true
assert_eq!(
Model::ClaudeSonnet4.cross_region_inference_id("us-east-1", true)?,
"global.anthropic.claude-sonnet-4-20250514-v1:0"
);
assert_eq!(
Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1", true)?,
"global.anthropic.claude-sonnet-4-5-20250929-v1:0"
);
assert_eq!(
Model::ClaudeHaiku4_5.cross_region_inference_id("ap-south-1", true)?,
"global.anthropic.claude-haiku-4-5-20251001-v1:0"
);
// Test that regional prefix is used when allow_global is false
assert_eq!(
Model::ClaudeSonnet4.cross_region_inference_id("us-east-1", false)?,
"us.anthropic.claude-sonnet-4-20250514-v1:0"
);
Ok(())
}

View File

@@ -71,6 +71,7 @@ pub struct AmazonBedrockSettings {
pub profile_name: Option<String>,
pub role_arn: Option<String>,
pub authentication_method: Option<BedrockAuthMethod>,
pub allow_global: Option<bool>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, EnumIter, IntoStaticStr, JsonSchema)]
@@ -239,6 +240,13 @@ impl State {
.or(settings_region)
.unwrap_or(String::from("us-east-1"))
}
fn get_allow_global(&self) -> bool {
self.settings
.as_ref()
.and_then(|s| s.allow_global)
.unwrap_or(false)
}
}
pub struct BedrockLanguageModelProvider {
@@ -545,11 +553,13 @@ impl LanguageModel for BedrockModel {
LanguageModelCompletionError,
>,
> {
let Ok(region) = cx.read_entity(&self.state, |state, _cx| state.get_region()) else {
let Ok((region, allow_global)) = cx.read_entity(&self.state, |state, _cx| {
(state.get_region(), state.get_allow_global())
}) else {
return async move { Err(anyhow::anyhow!("App State Dropped").into()) }.boxed();
};
let model_id = match self.model.cross_region_inference_id(&region) {
let model_id = match self.model.cross_region_inference_id(&region, allow_global) {
Ok(s) => s,
Err(e) => {
return async move { Err(e.into()) }.boxed();

View File

@@ -58,6 +58,7 @@ impl settings::Settings for AllLanguageModelSettings {
profile_name: bedrock.profile,
role_arn: None, // todo(was never a setting for this...)
authentication_method: bedrock.authentication_method.map(Into::into),
allow_global: bedrock.allow_global,
},
deepseek: DeepSeekSettings {
api_url: deepseek.api_url.unwrap(),

View File

@@ -61,6 +61,7 @@ pub struct AmazonBedrockSettingsContent {
pub region: Option<String>,
pub profile: Option<String>,
pub authentication_method: Option<BedrockAuthMethodContent>,
pub allow_global: Option<bool>,
}
#[with_fallible_options]

View File

@@ -89,12 +89,32 @@ To do this:
#### Cross-Region Inference
The Zed implementation of Amazon Bedrock uses [Cross-Region inference](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html) for all the models and region combinations that support it.
The Zed implementation of Amazon Bedrock uses [Cross-Region inference](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html) to improve availability and throughput.
With Cross-Region inference, you can distribute traffic across multiple AWS Regions, enabling higher throughput.
For example, if you use `Claude Sonnet 3.7 Thinking` from `us-east-1`, it may be processed across the US regions, namely: `us-east-1`, `us-east-2`, or `us-west-2`.
Cross-Region inference requests are kept within the AWS Regions that are part of the geography where the data originally resides.
For example, a request made within the US is kept within the AWS Regions in the US.
##### Regional vs Global Inference Profiles
Bedrock supports two types of cross-region inference profiles:
- **Regional profiles** (default): Route requests within a specific geography (US, EU, APAC). For example, `us-east-1` uses the `us.*` profile which routes across `us-east-1`, `us-east-2`, and `us-west-2`.
- **Global profiles**: Route requests across all commercial AWS Regions for maximum availability and performance.
By default, Zed uses **regional profiles** which keep your data within the same geography. You can opt into global profiles by adding `"allow_global": true` to your Bedrock configuration:
```json [settings]
{
"language_models": {
"bedrock": {
"authentication_method": "named_profile",
"region": "your-aws-region",
"profile": "your-profile-name",
"allow_global": true
}
}
}
```
**Note:** Only select newer models support global inference profiles. See the [AWS Bedrock supported models documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html#inference-profiles-support-system) for the current list of models that support global inference. If you encounter availability issues with a model in your region, enabling `allow_global` may resolve them.
Although the data remains stored only in the source Region, your input prompts and output results might move outside of your source Region during cross-Region inference.
All data will be transmitted encrypted across Amazon's secure network.