diff --git a/crates/anthropic/src/anthropic.rs b/crates/anthropic/src/anthropic.rs index 6df9d5da24..6ea08a5fd5 100644 --- a/crates/anthropic/src/anthropic.rs +++ b/crates/anthropic/src/anthropic.rs @@ -122,9 +122,6 @@ impl Model { let mut supported_effort_levels = Vec::new(); if let Some(effort) = entry.capabilities.as_ref().and_then(|e| e.effort.as_ref()) { - // The `xhigh` effort level reported by the API has no - // corresponding `Effort` variant in the request enum, so it is - // intentionally dropped here. for (level, supported) in [ (Effort::Low, effort.low.as_ref()), (Effort::Medium, effort.medium.as_ref()), @@ -148,7 +145,10 @@ impl Model { AnthropicModelMode::Default }; - let supports_speed = matches!(entry.id.as_str(), "claude-opus-4-6" | "claude-opus-4-7"); + let supports_speed = matches!( + entry.id.as_str(), + "claude-opus-4-6" | "claude-opus-4-7" | "claude-opus-4-8" + ); let mut extra_beta_headers = Vec::new(); if supports_speed { @@ -1056,6 +1056,17 @@ mod tests { assert_eq!(model.mode, AnthropicModelMode::Default); } + #[test] + fn from_listed_enables_fast_mode_for_opus_4_8() { + let model = Model::from_listed(listed_entry( + "claude-opus-4-8", + ModelCapabilities::default(), + )); + + assert!(model.supports_speed); + assert_eq!(model.beta_headers().as_deref(), Some(FAST_MODE_BETA_HEADER)); + } + #[test] fn from_listed_collects_supported_effort_levels() { let entry = listed_entry( diff --git a/crates/anthropic/src/completion.rs b/crates/anthropic/src/completion.rs index 5cb864a088..2b42bcae88 100644 --- a/crates/anthropic/src/completion.rs +++ b/crates/anthropic/src/completion.rs @@ -300,6 +300,7 @@ pub fn into_anthropic( "low" => Some(crate::Effort::Low), "medium" => Some(crate::Effort::Medium), "high" => Some(crate::Effort::High), + "xhigh" => Some(crate::Effort::XHigh), "max" => Some(crate::Effort::Max), _ => None, }; @@ -705,6 +706,44 @@ mod tests { )); } + #[test] + fn test_xhigh_effort_is_serialized_for_adaptive_thinking() { + let request = LanguageModelRequest { + messages: vec![LanguageModelRequestMessage { + role: Role::User, + content: vec![MessageContent::Text("Hi".to_string())], + cache: false, + reasoning_details: None, + }], + thread_id: None, + prompt_id: None, + intent: None, + stop: vec![], + temperature: None, + tools: vec![], + tool_choice: None, + thinking_allowed: true, + thinking_effort: Some("xhigh".into()), + speed: None, + }; + + let anthropic_request = into_anthropic( + request, + "claude-opus-4-8".to_string(), + 1.0, + 128_000, + AnthropicModelMode::AdaptiveThinking, + AnthropicPromptCacheMode::Automatic, + ); + + assert_eq!( + anthropic_request + .output_config + .and_then(|config| config.effort), + Some(crate::Effort::XHigh) + ); + } + #[test] fn test_no_cache_control_when_caching_disabled() { let request = LanguageModelRequest { diff --git a/crates/bedrock/src/models.rs b/crates/bedrock/src/models.rs index ee5d57ee80..a2f299a3c9 100644 --- a/crates/bedrock/src/models.rs +++ b/crates/bedrock/src/models.rs @@ -8,6 +8,7 @@ pub enum BedrockAdaptiveThinkingEffort { Medium, #[default] High, + XHigh, Max, } @@ -17,6 +18,7 @@ impl BedrockAdaptiveThinkingEffort { Self::Low => "low", Self::Medium => "medium", Self::High => "high", + Self::XHigh => "xhigh", Self::Max => "max", } } @@ -91,6 +93,13 @@ pub enum Model { alias = "claude-opus-4-7-thinking-latest" )] ClaudeOpus4_7, + #[serde( + rename = "claude-opus-4-8", + alias = "claude-opus-4-8-latest", + alias = "claude-opus-4-8-thinking", + alias = "claude-opus-4-8-thinking-latest" + )] + ClaudeOpus4_8, #[serde( rename = "claude-sonnet-4-6", alias = "claude-sonnet-4-6-latest", @@ -210,7 +219,9 @@ impl Model { } pub fn from_id(id: &str) -> anyhow::Result { - if id.starts_with("claude-opus-4-7") { + if id.starts_with("claude-opus-4-8") { + Ok(Self::ClaudeOpus4_8) + } else if id.starts_with("claude-opus-4-7") { Ok(Self::ClaudeOpus4_7) } else if id.starts_with("claude-opus-4-6") { Ok(Self::ClaudeOpus4_6) @@ -240,6 +251,7 @@ impl Model { Self::ClaudeOpus4_5 => "claude-opus-4-5", Self::ClaudeOpus4_6 => "claude-opus-4-6", Self::ClaudeOpus4_7 => "claude-opus-4-7", + Self::ClaudeOpus4_8 => "claude-opus-4-8", Self::ClaudeSonnet4_6 => "claude-sonnet-4-6", Self::Llama4Scout17B => "llama-4-scout-17b", Self::Llama4Maverick17B => "llama-4-maverick-17b", @@ -290,6 +302,7 @@ impl Model { Self::ClaudeOpus4_5 => "anthropic.claude-opus-4-5-20251101-v1:0", Self::ClaudeOpus4_6 => "anthropic.claude-opus-4-6-v1", Self::ClaudeOpus4_7 => "anthropic.claude-opus-4-7", + Self::ClaudeOpus4_8 => "anthropic.claude-opus-4-8", Self::ClaudeSonnet4_6 => "anthropic.claude-sonnet-4-6", Self::Llama4Scout17B => "meta.llama4-scout-17b-instruct-v1:0", Self::Llama4Maverick17B => "meta.llama4-maverick-17b-instruct-v1:0", @@ -340,6 +353,7 @@ impl Model { Self::ClaudeOpus4_5 => "Claude Opus 4.5", Self::ClaudeOpus4_6 => "Claude Opus 4.6", Self::ClaudeOpus4_7 => "Claude Opus 4.7", + Self::ClaudeOpus4_8 => "Claude Opus 4.8", Self::ClaudeSonnet4_6 => "Claude Sonnet 4.6", Self::Llama4Scout17B => "Llama 4 Scout 17B", Self::Llama4Maverick17B => "Llama 4 Maverick 17B", @@ -391,6 +405,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 => 1_000_000, Self::ClaudeOpus4_1 => 200_000, Self::Llama4Scout17B | Self::Llama4Maverick17B => 128_000, @@ -425,7 +440,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeSonnet4_6 => 64_000, Self::ClaudeOpus4_1 => 32_000, - Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 => 128_000, + Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 | Self::ClaudeOpus4_8 => 128_000, Self::Llama4Scout17B | Self::Llama4Maverick17B | Self::Gemma3_4B @@ -464,6 +479,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 => 1.0, Self::Custom { default_temperature, @@ -482,6 +498,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 => true, Self::NovaLite | Self::NovaPro | Self::NovaPremier | Self::Nova2Lite => true, Self::MistralLarge3 | Self::PixtralLarge | Self::MagistralSmall => true, @@ -513,6 +530,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 => true, Self::NovaLite | Self::NovaPro => true, Self::PixtralLarge => true, @@ -531,6 +549,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 => true, Self::Custom { cache_configuration, @@ -550,6 +569,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 ) } @@ -557,10 +577,14 @@ impl Model { pub fn supports_adaptive_thinking(&self) -> bool { matches!( self, - Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 | Self::ClaudeSonnet4_6 + Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 ) } + pub fn supports_xhigh_adaptive_thinking(&self) -> bool { + matches!(self, Self::ClaudeOpus4_8) + } + pub fn thinking_mode(&self) -> BedrockModelMode { if self.supports_adaptive_thinking() { BedrockModelMode::AdaptiveThinking { @@ -590,6 +614,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 | Self::Nova2Lite ); @@ -650,6 +675,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 | Self::Nova2Lite, "global", @@ -667,6 +693,7 @@ impl Model { | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 | Self::Llama4Scout17B | Self::Llama4Maverick17B @@ -689,6 +716,7 @@ impl Model { | Self::ClaudeSonnet4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6 | Self::NovaLite | Self::NovaPro @@ -702,6 +730,7 @@ impl Model { | Self::ClaudeSonnet4_5 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 + | Self::ClaudeOpus4_8 | Self::ClaudeSonnet4_6, "au", ) => Ok(format!("{}.{}", region_group, model_id)), @@ -779,6 +808,10 @@ mod tests { Model::ClaudeOpus4_7.cross_region_inference_id("eu-west-1", false)?, "eu.anthropic.claude-opus-4-7" ); + assert_eq!( + Model::ClaudeOpus4_8.cross_region_inference_id("eu-west-1", false)?, + "eu.anthropic.claude-opus-4-8" + ); Ok(()) } @@ -813,6 +846,10 @@ mod tests { Model::ClaudeOpus4_7.cross_region_inference_id("ap-southeast-2", false)?, "au.anthropic.claude-opus-4-7" ); + assert_eq!( + Model::ClaudeOpus4_8.cross_region_inference_id("ap-southeast-2", false)?, + "au.anthropic.claude-opus-4-8" + ); Ok(()) } @@ -877,6 +914,10 @@ mod tests { Model::ClaudeOpus4_7.cross_region_inference_id("us-east-1", true)?, "global.anthropic.claude-opus-4-7" ); + assert_eq!( + Model::ClaudeOpus4_8.cross_region_inference_id("us-east-1", true)?, + "global.anthropic.claude-opus-4-8" + ); assert_eq!( Model::Nova2Lite.cross_region_inference_id("us-east-1", true)?, "global.amazon.nova-2-lite-v1:0" @@ -978,6 +1019,9 @@ mod tests { assert!(!Model::ClaudeSonnet4.supports_adaptive_thinking()); assert!(Model::ClaudeOpus4_6.supports_adaptive_thinking()); assert!(Model::ClaudeSonnet4_6.supports_adaptive_thinking()); + assert!(!Model::ClaudeOpus4_7.supports_xhigh_adaptive_thinking()); + assert!(Model::ClaudeOpus4_8.supports_xhigh_adaptive_thinking()); + assert_eq!(BedrockAdaptiveThinkingEffort::XHigh.as_str(), "xhigh"); assert_eq!( Model::ClaudeSonnet4.thinking_mode(), diff --git a/crates/language_models/src/provider/bedrock.rs b/crates/language_models/src/provider/bedrock.rs index 23e5e4939a..9d595b516a 100644 --- a/crates/language_models/src/provider/bedrock.rs +++ b/crates/language_models/src/provider/bedrock.rs @@ -670,12 +670,22 @@ impl LanguageModel for BedrockModel { value: "high".into(), is_default: true, }, + language_model::LanguageModelEffortLevel { + name: "XHigh".into(), + value: "xhigh".into(), + is_default: false, + }, language_model::LanguageModelEffortLevel { name: "Max".into(), value: "max".into(), is_default: false, }, ] + .into_iter() + .filter(|effort_level| { + effort_level.value != "xhigh" || self.model.supports_xhigh_adaptive_thinking() + }) + .collect() } else { Vec::new() } @@ -1128,6 +1138,7 @@ pub fn into_bedrock( "low" => Some(bedrock::BedrockAdaptiveThinkingEffort::Low), "medium" => Some(bedrock::BedrockAdaptiveThinkingEffort::Medium), "high" => Some(bedrock::BedrockAdaptiveThinkingEffort::High), + "xhigh" => Some(bedrock::BedrockAdaptiveThinkingEffort::XHigh), "max" => Some(bedrock::BedrockAdaptiveThinkingEffort::Max), _ => None, }) diff --git a/crates/language_models/src/provider/opencode.rs b/crates/language_models/src/provider/opencode.rs index 8179713a6a..a5c98151fe 100644 --- a/crates/language_models/src/provider/opencode.rs +++ b/crates/language_models/src/provider/opencode.rs @@ -49,7 +49,7 @@ fn reasoning_effort_display(effort: ReasoningEffort) -> (&'static str, &'static ReasoningEffort::Low => ("Low", "low"), ReasoningEffort::Medium => ("Medium", "medium"), ReasoningEffort::High => ("High", "high"), - ReasoningEffort::XHigh => ("Max", "max"), + ReasoningEffort::XHigh => ("XHigh", "xhigh"), } } diff --git a/crates/opencode/src/opencode.rs b/crates/opencode/src/opencode.rs index c34644eb68..4dfc30c62b 100644 --- a/crates/opencode/src/opencode.rs +++ b/crates/opencode/src/opencode.rs @@ -56,6 +56,8 @@ impl OpenCodeSubscription { #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)] pub enum Model { // -- Anthropic protocol models -- + #[serde(rename = "claude-opus-4-8")] + ClaudeOpus4_8, #[serde(rename = "claude-opus-4-7")] ClaudeOpus4_7, #[serde(rename = "claude-opus-4-6")] @@ -219,6 +221,7 @@ impl Model { pub fn id(&self) -> &str { match self { + Self::ClaudeOpus4_8 => "claude-opus-4-8", Self::ClaudeOpus4_7 => "claude-opus-4-7", Self::ClaudeOpus4_6 => "claude-opus-4-6", Self::ClaudeOpus4_5 => "claude-opus-4-5", @@ -273,6 +276,7 @@ impl Model { pub fn display_name(&self) -> &str { match self { + Self::ClaudeOpus4_8 => "Claude Opus 4.8", Self::ClaudeOpus4_7 => "Claude Opus 4.7", Self::ClaudeOpus4_6 => "Claude Opus 4.6", Self::ClaudeOpus4_5 => "Claude Opus 4.5", @@ -339,7 +343,8 @@ impl Model { } } - Self::ClaudeOpus4_7 + Self::ClaudeOpus4_8 + | Self::ClaudeOpus4_7 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_1 @@ -413,7 +418,7 @@ impl Model { pub fn max_token_count(&self, subscription: OpenCodeSubscription) -> u64 { match self { // Anthropic models - Self::ClaudeOpus4_7 => 1_000_000, + Self::ClaudeOpus4_8 | Self::ClaudeOpus4_7 => 1_000_000, Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6 => 1_000_000, Self::ClaudeSonnet4_5 => 1_000_000, Self::ClaudeOpus4_5 | Self::ClaudeHaiku4_5 => 200_000, @@ -464,7 +469,7 @@ impl Model { pub fn max_output_tokens(&self, subscription: OpenCodeSubscription) -> Option { match self { // Anthropic models - Self::ClaudeOpus4_7 | Self::ClaudeOpus4_6 => Some(128_000), + Self::ClaudeOpus4_8 | Self::ClaudeOpus4_7 | Self::ClaudeOpus4_6 => Some(128_000), Self::ClaudeOpus4_5 | Self::ClaudeSonnet4_6 | Self::ClaudeSonnet4_5 @@ -531,7 +536,8 @@ impl Model { pub fn supports_images(&self) -> bool { match self { // Anthropic models support images - Self::ClaudeOpus4_7 + Self::ClaudeOpus4_8 + | Self::ClaudeOpus4_7 | Self::ClaudeOpus4_6 | Self::ClaudeOpus4_5 | Self::ClaudeOpus4_1 @@ -596,6 +602,13 @@ impl Model { pub fn supported_reasoning_effort_levels(&self) -> Option> { match self { + Self::ClaudeOpus4_8 => Some(vec![ + ReasoningEffort::Low, + ReasoningEffort::Medium, + ReasoningEffort::High, + ReasoningEffort::XHigh, + ]), + Self::MimoV2_5Pro | Self::MimoV2_5 => Some(vec![ ReasoningEffort::Low, ReasoningEffort::Medium, diff --git a/docs/src/ai/models.md b/docs/src/ai/models.md index f582f25af0..72ef43239f 100644 --- a/docs/src/ai/models.md +++ b/docs/src/ai/models.md @@ -23,6 +23,10 @@ Zed's plans offer hosted versions of major LLMs with higher rate limits than dir | | Anthropic | Output | $25.00 | $27.50 | | | Anthropic | Input - Cache Write | $6.25 | $6.875 | | | Anthropic | Input - Cache Read | $0.50 | $0.55 | +| Claude Opus 4.8 | Anthropic | Input | $5.00 | $5.50 | +| | Anthropic | Output | $25.00 | $27.50 | +| | Anthropic | Input - Cache Write | $6.25 | $6.875 | +| | Anthropic | Input - Cache Read | $0.50 | $0.55 | | Claude Sonnet 4.5 | Anthropic | Input | $3.00 | $3.30 | | | Anthropic | Output | $15.00 | $16.50 | | | Anthropic | Input - Cache Write | $3.75 | $4.125 | @@ -81,7 +85,7 @@ Zed's plans offer hosted versions of major LLMs with higher rate limits than dir As of February 19, 2026, Zed Pro serves newer model versions in place of the retired models below: -- Claude Opus 4.1 → Claude Opus 4.5, Claude Opus 4.6, or Claude Opus 4.7 +- Claude Opus 4.1 → Claude Opus 4.5, Claude Opus 4.6, Claude Opus 4.7, or Claude Opus 4.8 - Claude Sonnet 4 → Claude Sonnet 4.5 or Claude Sonnet 4.6 - Claude Sonnet 3.7 (retired Feb 19) → Claude Sonnet 4.5 or Claude Sonnet 4.6 - GPT-5.1 and GPT-5 → GPT-5.2 or GPT-5.2-Codex @@ -106,6 +110,7 @@ A context window is the maximum span of text and code an LLM can consider at onc | Claude Opus 4.5 | Anthropic | 200k | | Claude Opus 4.6 | Anthropic | 1M | | Claude Opus 4.7 | Anthropic | 1M | +| Claude Opus 4.8 | Anthropic | 1M | | Claude Sonnet 4.5 | Anthropic | 200k | | Claude Sonnet 4.6 | Anthropic | 1M | | Claude Haiku 4.5 | Anthropic | 200k |