diff --git a/crates/bedrock/src/models.rs b/crates/bedrock/src/models.rs index cd035be819..9b8a259640 100644 --- a/crates/bedrock/src/models.rs +++ b/crates/bedrock/src/models.rs @@ -834,6 +834,12 @@ pub enum MantleProtocol { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, EnumIter)] pub enum MantleModel { + #[serde(rename = "gpt-5.6-sol")] + Gpt5_6Sol, + #[serde(rename = "gpt-5.6-terra")] + Gpt5_6Terra, + #[serde(rename = "gpt-5.6-luna")] + Gpt5_6Luna, #[serde(rename = "gpt-5.5")] Gpt5_5, #[serde(rename = "gpt-5.4")] @@ -857,6 +863,9 @@ impl MantleModel { /// The model id Zed uses internally (also used as the `name` in settings). pub fn id(&self) -> &str { match self { + Self::Gpt5_6Sol => "gpt-5.6-sol", + Self::Gpt5_6Terra => "gpt-5.6-terra", + Self::Gpt5_6Luna => "gpt-5.6-luna", Self::Gpt5_5 => "gpt-5.5", Self::Gpt5_4 => "gpt-5.4", Self::Grok4_3 => "grok-4.3", @@ -867,6 +876,9 @@ impl MantleModel { /// The model id as expected in Bedrock Mantle request bodies, e.g. `openai.gpt-5.5`. pub fn request_id(&self) -> &str { match self { + Self::Gpt5_6Sol => "openai.gpt-5.6-sol", + Self::Gpt5_6Terra => "openai.gpt-5.6-terra", + Self::Gpt5_6Luna => "openai.gpt-5.6-luna", Self::Gpt5_5 => "openai.gpt-5.5", Self::Gpt5_4 => "openai.gpt-5.4", Self::Grok4_3 => "xai.grok-4.3", @@ -876,6 +888,9 @@ impl MantleModel { pub fn display_name(&self) -> &str { match self { + Self::Gpt5_6Sol => "GPT-5.6 Sol", + Self::Gpt5_6Terra => "GPT-5.6 Terra", + Self::Gpt5_6Luna => "GPT-5.6 Luna", Self::Gpt5_5 => "GPT-5.5", Self::Gpt5_4 => "GPT-5.4", Self::Grok4_3 => "Grok 4.3", @@ -888,14 +903,23 @@ impl MantleModel { /// Which OpenAI-compatible API this model must be called through. pub fn protocol(&self) -> MantleProtocol { match self { - Self::Gpt5_5 | Self::Gpt5_4 | Self::Grok4_3 => MantleProtocol::Responses, + Self::Gpt5_6Sol + | Self::Gpt5_6Terra + | Self::Gpt5_6Luna + | Self::Gpt5_5 + | Self::Gpt5_4 + | Self::Grok4_3 => MantleProtocol::Responses, Self::Custom { protocol, .. } => *protocol, } } pub fn max_token_count(&self) -> u64 { match self { - Self::Gpt5_5 | Self::Gpt5_4 => 272_000, + Self::Gpt5_6Sol + | Self::Gpt5_6Terra + | Self::Gpt5_6Luna + | Self::Gpt5_5 + | Self::Gpt5_4 => 272_000, Self::Grok4_3 => 1_000_000, Self::Custom { max_tokens, .. } => *max_tokens, } @@ -903,8 +927,12 @@ impl MantleModel { pub fn max_output_tokens(&self) -> u64 { match self { - // AWS doesn't document a hard cap for GPT-5.5/5.4 on Mantle. - Self::Gpt5_5 | Self::Gpt5_4 => 128_000, + // AWS doesn't document a hard cap for the GPT-5.x models on Mantle. + Self::Gpt5_6Sol + | Self::Gpt5_6Terra + | Self::Gpt5_6Luna + | Self::Gpt5_5 + | Self::Gpt5_4 => 128_000, Self::Grok4_3 => 131_072, Self::Custom { max_output_tokens, .. @@ -914,14 +942,24 @@ impl MantleModel { pub fn supports_tools(&self) -> bool { match self { - Self::Gpt5_5 | Self::Gpt5_4 | Self::Grok4_3 => true, + Self::Gpt5_6Sol + | Self::Gpt5_6Terra + | Self::Gpt5_6Luna + | Self::Gpt5_5 + | Self::Gpt5_4 + | Self::Grok4_3 => true, Self::Custom { supports_tools, .. } => *supports_tools, } } pub fn supports_images(&self) -> bool { match self { - Self::Gpt5_5 | Self::Gpt5_4 | Self::Grok4_3 => true, + Self::Gpt5_6Sol + | Self::Gpt5_6Terra + | Self::Gpt5_6Luna + | Self::Gpt5_5 + | Self::Gpt5_4 + | Self::Grok4_3 => true, Self::Custom { supports_images, .. } => *supports_images, @@ -930,7 +968,12 @@ impl MantleModel { pub fn supports_thinking(&self) -> bool { match self { - Self::Gpt5_5 | Self::Gpt5_4 | Self::Grok4_3 => true, + Self::Gpt5_6Sol + | Self::Gpt5_6Terra + | Self::Gpt5_6Luna + | Self::Gpt5_5 + | Self::Gpt5_4 + | Self::Grok4_3 => true, Self::Custom { supports_thinking, .. } => *supports_thinking, @@ -944,11 +987,84 @@ mod tests { #[test] fn test_builtin_mantle_models_use_responses_protocol() { + assert_eq!(MantleModel::Gpt5_6Sol.protocol(), MantleProtocol::Responses); + assert_eq!( + MantleModel::Gpt5_6Terra.protocol(), + MantleProtocol::Responses + ); + assert_eq!( + MantleModel::Gpt5_6Luna.protocol(), + MantleProtocol::Responses + ); assert_eq!(MantleModel::Gpt5_5.protocol(), MantleProtocol::Responses); assert_eq!(MantleModel::Gpt5_4.protocol(), MantleProtocol::Responses); assert_eq!(MantleModel::Grok4_3.protocol(), MantleProtocol::Responses); } + #[test] + fn test_gpt_5_6_mantle_model_metadata() { + // Values mirror the AWS Bedrock model cards for GPT-5.6 Sol/Terra/Luna, + // which are served only through the `bedrock-mantle` Responses API. + for (model, id, request_id, display_name) in [ + ( + MantleModel::Gpt5_6Sol, + "gpt-5.6-sol", + "openai.gpt-5.6-sol", + "GPT-5.6 Sol", + ), + ( + MantleModel::Gpt5_6Terra, + "gpt-5.6-terra", + "openai.gpt-5.6-terra", + "GPT-5.6 Terra", + ), + ( + MantleModel::Gpt5_6Luna, + "gpt-5.6-luna", + "openai.gpt-5.6-luna", + "GPT-5.6 Luna", + ), + ] { + assert_eq!(model.id(), id); + assert_eq!(model.request_id(), request_id); + assert_eq!(model.display_name(), display_name); + assert_eq!(model.max_token_count(), 272_000); + assert!(model.supports_tools()); + assert!(model.supports_images()); + assert!(model.supports_thinking()); + } + } + + #[test] + fn test_builtin_mantle_models_have_unique_ids_and_sane_token_limits() { + use std::collections::HashSet; + use strum::IntoEnumIterator; + + let mut ids = HashSet::new(); + let mut request_ids = HashSet::new(); + for model in + MantleModel::iter().filter(|model| !matches!(model, MantleModel::Custom { .. })) + { + assert!( + ids.insert(model.id().to_string()), + "duplicate MantleModel id: {}", + model.id() + ); + assert!( + request_ids.insert(model.request_id().to_string()), + "duplicate MantleModel request_id: {}", + model.request_id() + ); + assert!( + model.max_output_tokens() <= model.max_token_count(), + "{} has max_output_tokens ({}) greater than max_token_count ({})", + model.id(), + model.max_output_tokens(), + model.max_token_count() + ); + } + } + #[test] fn test_us_region_inference_ids() -> anyhow::Result<()> { assert_eq!( diff --git a/crates/language_models/src/provider/bedrock.rs b/crates/language_models/src/provider/bedrock.rs index f0902dd9cb..b686a3d11f 100644 --- a/crates/language_models/src/provider/bedrock.rs +++ b/crates/language_models/src/provider/bedrock.rs @@ -2606,9 +2606,24 @@ mod tests { #[test] fn test_builtin_mantle_models_support_thinking() { + assert!(MantleModel::Gpt5_6Sol.supports_thinking()); + assert!(MantleModel::Gpt5_6Terra.supports_thinking()); + assert!(MantleModel::Gpt5_6Luna.supports_thinking()); assert!(MantleModel::Gpt5_5.supports_thinking()); assert!(MantleModel::Gpt5_4.supports_thinking()); assert!(MantleModel::Grok4_3.supports_thinking()); + assert_eq!( + mantle_default_reasoning_effort(&MantleModel::Gpt5_6Sol), + Some(ReasoningEffort::Medium) + ); + assert_eq!( + mantle_default_reasoning_effort(&MantleModel::Gpt5_6Terra), + Some(ReasoningEffort::Medium) + ); + assert_eq!( + mantle_default_reasoning_effort(&MantleModel::Gpt5_6Luna), + Some(ReasoningEffort::Medium) + ); assert_eq!( mantle_default_reasoning_effort(&MantleModel::Gpt5_5), Some(ReasoningEffort::Medium) diff --git a/crates/settings_content/src/language_model.rs b/crates/settings_content/src/language_model.rs index 4203bc232a..735c3d20e1 100644 --- a/crates/settings_content/src/language_model.rs +++ b/crates/settings_content/src/language_model.rs @@ -118,7 +118,7 @@ pub struct AmazonBedrockSettingsContent { pub available_models: Option>, /// Custom models served through the `bedrock-mantle` endpoint's /// OpenAI-compatible APIs, in addition to the built-in Mantle models - /// (GPT-5.5, GPT-5.4, Grok 4.3). + /// (GPT-5.6 Sol, GPT-5.6 Terra, GPT-5.6 Luna, GPT-5.5, GPT-5.4, Grok 4.3). pub mantle_available_models: Option>, pub custom_headers: Option>, pub endpoint_url: Option, diff --git a/docs/src/ai/use-a-gateway.md b/docs/src/ai/use-a-gateway.md index 6cd1a7a614..b1e05dcb8d 100644 --- a/docs/src/ai/use-a-gateway.md +++ b/docs/src/ai/use-a-gateway.md @@ -215,7 +215,7 @@ Some AWS environments require a guardrail on every Bedrock API call. Add `guardr ### Bedrock Mantle Models {#bedrock-mantle-models} -Some models, such as GPT-5.5, GPT-5.4, and Grok 4.3, aren't available through Bedrock's Converse API and are only reachable through `bedrock-mantle`, AWS's OpenAI-compatible inference endpoint. Zed routes these models through `bedrock-mantle` automatically; they appear alongside the rest of the Bedrock models in the model picker once you're authenticated, with no extra configuration required. +Some models, such as the GPT-5.6 family (Sol, Terra, and Luna), GPT-5.5, GPT-5.4, and Grok 4.3, aren't available through Bedrock's Converse API and are only reachable through `bedrock-mantle`, AWS's OpenAI-compatible inference endpoint. Zed routes these models through `bedrock-mantle` automatically; they appear alongside the rest of the Bedrock models in the model picker once you're authenticated, with no extra configuration required. Mantle models require IAM permissions for the `bedrock-mantle` endpoint (for example via the `AmazonBedrockMantleInferenceAccess` managed policy) in addition to whatever permissions your existing Bedrock credentials already have, and `bedrock-mantle` is only available in [some AWS Regions](https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-mantle.html#regions). Zed surfaces an error naming the current Region and the supported ones if you try to use a Mantle model outside of them.