bedrock: Add Claude Opus 4.6 (#48525)

Shardul Vaidya and Ona created

Release Notes:

- Added Claude Opus 4.6 and 4.6 Thinking with Cross region inference for
US, EU, and Global endpoints.

---------

Co-authored-by: Ona <no-reply@ona.com>

Change summary

crates/bedrock/src/bedrock.rs                  | 49 +++++++++----
crates/bedrock/src/models.rs                   | 70 ++++++++++++++++++-
crates/language_models/Cargo.toml              |  2 
crates/language_models/src/provider/bedrock.rs | 19 ++++-
crates/zed/Cargo.toml                          |  2 
5 files changed, 116 insertions(+), 26 deletions(-)

Detailed changes

crates/bedrock/src/bedrock.rs 🔗

@@ -39,21 +39,33 @@ pub async fn stream_completion(
         .model_id(request.model.clone())
         .set_messages(request.messages.into());
 
-    if let Some(Thinking::Enabled {
-        budget_tokens: Some(budget_tokens),
-    }) = request.thinking
-    {
-        let thinking_config = HashMap::from([
-            ("type".to_string(), Document::String("enabled".to_string())),
-            (
-                "budget_tokens".to_string(),
-                Document::Number(AwsNumber::PosInt(budget_tokens)),
-            ),
-        ]);
-        response = response.additional_model_request_fields(Document::Object(HashMap::from([(
-            "thinking".to_string(),
-            Document::from(thinking_config),
-        )])));
+    match request.thinking {
+        Some(Thinking::Enabled {
+            budget_tokens: Some(budget_tokens),
+        }) => {
+            let thinking_config = HashMap::from([
+                ("type".to_string(), Document::String("enabled".to_string())),
+                (
+                    "budget_tokens".to_string(),
+                    Document::Number(AwsNumber::PosInt(budget_tokens)),
+                ),
+            ]);
+            response =
+                response.additional_model_request_fields(Document::Object(HashMap::from([(
+                    "thinking".to_string(),
+                    Document::from(thinking_config),
+                )])));
+        }
+        Some(Thinking::Adaptive { effort: _ }) => {
+            let thinking_config =
+                HashMap::from([("type".to_string(), Document::String("adaptive".to_string()))]);
+            response =
+                response.additional_model_request_fields(Document::Object(HashMap::from([(
+                    "thinking".to_string(),
+                    Document::from(thinking_config),
+                )])));
+        }
+        _ => {}
     }
 
     if request.tools.as_ref().is_some_and(|t| !t.tools.is_empty()) {
@@ -145,7 +157,12 @@ pub fn value_to_aws_document(value: &Value) -> Document {
 
 #[derive(Debug, Serialize, Deserialize)]
 pub enum Thinking {
-    Enabled { budget_tokens: Option<u64> },
+    Enabled {
+        budget_tokens: Option<u64>,
+    },
+    Adaptive {
+        effort: BedrockAdaptiveThinkingEffort,
+    },
 }
 
 #[derive(Debug)]

crates/bedrock/src/models.rs 🔗

@@ -1,6 +1,27 @@
 use serde::{Deserialize, Serialize};
 use strum::EnumIter;
 
+#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
+#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq)]
+pub enum BedrockAdaptiveThinkingEffort {
+    Low,
+    Medium,
+    #[default]
+    High,
+    Max,
+}
+
+impl BedrockAdaptiveThinkingEffort {
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            Self::Low => "low",
+            Self::Medium => "medium",
+            Self::High => "high",
+            Self::Max => "max",
+        }
+    }
+}
+
 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
 #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
 pub enum BedrockModelMode {
@@ -9,6 +30,9 @@ pub enum BedrockModelMode {
     Thinking {
         budget_tokens: Option<u64>,
     },
+    AdaptiveThinking {
+        effort: BedrockAdaptiveThinkingEffort,
+    },
 }
 
 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@@ -58,6 +82,13 @@ pub enum Model {
         alias = "claude-opus-4-5-thinking-latest"
     )]
     ClaudeOpus4_5Thinking,
+    #[serde(rename = "claude-opus-4-6", alias = "claude-opus-4-6-latest")]
+    ClaudeOpus4_6,
+    #[serde(
+        rename = "claude-opus-4-6-thinking",
+        alias = "claude-opus-4-6-thinking-latest"
+    )]
+    ClaudeOpus4_6Thinking,
     #[serde(rename = "claude-3-5-sonnet-v2", alias = "claude-3-5-sonnet-latest")]
     Claude3_5SonnetV2,
     #[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")]
@@ -148,7 +179,11 @@ impl Model {
     }
 
     pub fn from_id(id: &str) -> anyhow::Result<Self> {
-        if id.starts_with("claude-opus-4-5-thinking") {
+        if id.starts_with("claude-opus-4-6-thinking") {
+            Ok(Self::ClaudeOpus4_6Thinking)
+        } else if id.starts_with("claude-opus-4-6") {
+            Ok(Self::ClaudeOpus4_6)
+        } else if id.starts_with("claude-opus-4-5-thinking") {
             Ok(Self::ClaudeOpus4_5Thinking)
         } else if id.starts_with("claude-opus-4-5") {
             Ok(Self::ClaudeOpus4_5)
@@ -199,6 +234,8 @@ impl Model {
             Model::ClaudeOpus4_1Thinking => "claude-opus-4-1-thinking",
             Model::ClaudeOpus4_5 => "claude-opus-4-5",
             Model::ClaudeOpus4_5Thinking => "claude-opus-4-5-thinking",
+            Model::ClaudeOpus4_6 => "claude-opus-4-6",
+            Model::ClaudeOpus4_6Thinking => "claude-opus-4-6-thinking",
             Model::Claude3_5SonnetV2 => "claude-3-5-sonnet-v2",
             Model::Claude3_5Sonnet => "claude-3-5-sonnet",
             Model::Claude3Opus => "claude-3-opus",
@@ -269,6 +306,7 @@ impl Model {
             Model::ClaudeOpus4_5 | Model::ClaudeOpus4_5Thinking => {
                 "anthropic.claude-opus-4-5-20251101-v1:0"
             }
+            Model::ClaudeOpus4_6 | Model::ClaudeOpus4_6Thinking => "anthropic.claude-opus-4-6-v1",
             Model::Claude3_5SonnetV2 => "anthropic.claude-3-5-sonnet-20241022-v2:0",
             Model::Claude3_5Sonnet => "anthropic.claude-3-5-sonnet-20240620-v1:0",
             Model::Claude3Opus => "anthropic.claude-3-opus-20240229-v1:0",
@@ -335,6 +373,8 @@ impl Model {
             Self::ClaudeOpus4_1Thinking => "Claude Opus 4.1 Thinking",
             Self::ClaudeOpus4_5 => "Claude Opus 4.5",
             Self::ClaudeOpus4_5Thinking => "Claude Opus 4.5 Thinking",
+            Self::ClaudeOpus4_6 => "Claude Opus 4.6",
+            Self::ClaudeOpus4_6Thinking => "Claude Opus 4.6 Thinking",
             Self::Claude3_5SonnetV2 => "Claude 3.5 Sonnet v2",
             Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
             Self::Claude3Opus => "Claude 3 Opus",
@@ -407,7 +447,9 @@ impl Model {
             | Self::ClaudeOpus4Thinking
             | Self::ClaudeOpus4_1Thinking
             | Self::ClaudeOpus4_5
-            | Self::ClaudeOpus4_5Thinking => 200_000,
+            | Self::ClaudeOpus4_5Thinking
+            | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_6Thinking => 200_000,
             Self::AmazonNovaPremier => 1_000_000,
             Self::PalmyraWriterX5 => 1_000_000,
             Self::PalmyraWriterX4 => 128_000,
@@ -426,6 +468,7 @@ impl Model {
             | Self::ClaudeHaiku4_5
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_5Thinking => 64_000,
+            Self::ClaudeOpus4_6 | Self::ClaudeOpus4_6Thinking => 128_000,
             Self::ClaudeOpus4
             | Self::ClaudeOpus4Thinking
             | Self::ClaudeOpus4_1
@@ -452,6 +495,8 @@ impl Model {
             | Self::ClaudeOpus4_1Thinking
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_5Thinking
+            | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_6Thinking
             | Self::ClaudeSonnet4
             | Self::ClaudeSonnet4Thinking
             | Self::ClaudeSonnet4_5
@@ -479,6 +524,8 @@ impl Model {
             | Self::ClaudeOpus4_1Thinking
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_5Thinking
+            | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_6Thinking
             | Self::ClaudeSonnet4
             | Self::ClaudeSonnet4Thinking
             | Self::ClaudeSonnet4_5
@@ -522,7 +569,9 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_1Thinking
             | Self::ClaudeOpus4_5
-            | Self::ClaudeOpus4_5Thinking => true,
+            | Self::ClaudeOpus4_5Thinking
+            | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_6Thinking => true,
 
             // Custom models - check if they have cache configuration
             Self::Custom {
@@ -546,7 +595,9 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_1Thinking
             | Self::ClaudeOpus4_5
-            | Self::ClaudeOpus4_5Thinking => Some(BedrockModelCacheConfiguration {
+            | Self::ClaudeOpus4_5Thinking
+            | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_6Thinking => Some(BedrockModelCacheConfiguration {
                 max_cache_anchors: 4,
                 min_total_token: 1024,
             }),
@@ -580,6 +631,9 @@ impl Model {
             | Model::ClaudeOpus4_5Thinking => BedrockModelMode::Thinking {
                 budget_tokens: Some(4096),
             },
+            Model::ClaudeOpus4_6Thinking => BedrockModelMode::AdaptiveThinking {
+                effort: BedrockAdaptiveThinkingEffort::default(),
+            },
             _ => BedrockModelMode::Default,
         }
     }
@@ -597,6 +651,8 @@ impl Model {
             self,
             Model::ClaudeOpus4_5
                 | Model::ClaudeOpus4_5Thinking
+                | Model::ClaudeOpus4_6
+                | Model::ClaudeOpus4_6Thinking
                 | Model::ClaudeHaiku4_5
                 | Model::ClaudeSonnet4
                 | Model::ClaudeSonnet4Thinking
@@ -637,6 +693,8 @@ impl Model {
             (
                 Model::ClaudeOpus4_5
                 | Model::ClaudeOpus4_5Thinking
+                | Model::ClaudeOpus4_6
+                | Model::ClaudeOpus4_6Thinking
                 | Model::ClaudeHaiku4_5
                 | Model::ClaudeSonnet4
                 | Model::ClaudeSonnet4Thinking
@@ -694,6 +752,8 @@ impl Model {
                 | Model::ClaudeOpus4_1Thinking
                 | Model::ClaudeOpus4_5
                 | Model::ClaudeOpus4_5Thinking
+                | Model::ClaudeOpus4_6
+                | Model::ClaudeOpus4_6Thinking
                 | Model::Claude3Haiku
                 | Model::Claude3Opus
                 | Model::Claude3Sonnet
@@ -728,6 +788,8 @@ impl Model {
                 | Model::ClaudeSonnet4
                 | Model::ClaudeSonnet4_5
                 | Model::ClaudeSonnet4_5Thinking
+                | Model::ClaudeOpus4_6
+                | Model::ClaudeOpus4_6Thinking
                 | Model::Claude3Haiku
                 | Model::Claude3Sonnet
                 | Model::MetaLlama321BInstructV1

crates/language_models/Cargo.toml 🔗

@@ -18,7 +18,7 @@ anyhow.workspace = true
 aws-config = { workspace = true, features = ["behavior-version-latest"] }
 aws-credential-types = { workspace = true, features = ["hardcoded-credentials"] }
 aws_http_client.workspace = true
-bedrock.workspace = true
+bedrock = { workspace = true, features = ["schemars"] }
 chrono.workspace = true
 client.workspace = true
 cloud_api_types.workspace = true

crates/language_models/src/provider/bedrock.rs 🔗

@@ -146,6 +146,9 @@ pub enum ModelMode {
         /// The maximum number of tokens to use for reasoning. Must be lower than the model's `max_output_tokens`.
         budget_tokens: Option<u64>,
     },
+    AdaptiveThinking {
+        effort: bedrock::BedrockAdaptiveThinkingEffort,
+    },
 }
 
 impl From<ModelMode> for BedrockModelMode {
@@ -153,6 +156,7 @@ impl From<ModelMode> for BedrockModelMode {
         match value {
             ModelMode::Default => BedrockModelMode::Default,
             ModelMode::Thinking { budget_tokens } => BedrockModelMode::Thinking { budget_tokens },
+            ModelMode::AdaptiveThinking { effort } => BedrockModelMode::AdaptiveThinking { effort },
         }
     }
 }
@@ -162,6 +166,7 @@ impl From<BedrockModelMode> for ModelMode {
         match value {
             BedrockModelMode::Default => ModelMode::Default,
             BedrockModelMode::Thinking { budget_tokens } => ModelMode::Thinking { budget_tokens },
+            BedrockModelMode::AdaptiveThinking { effort } => ModelMode::AdaptiveThinking { effort },
         }
     }
 }
@@ -932,10 +937,16 @@ pub fn into_bedrock(
         max_tokens: max_output_tokens,
         system: Some(system_message),
         tools: Some(tool_config),
-        thinking: if request.thinking_allowed
-            && let BedrockModelMode::Thinking { budget_tokens } = mode
-        {
-            Some(bedrock::Thinking::Enabled { budget_tokens })
+        thinking: if request.thinking_allowed {
+            match mode {
+                BedrockModelMode::Thinking { budget_tokens } => {
+                    Some(bedrock::Thinking::Enabled { budget_tokens })
+                }
+                BedrockModelMode::AdaptiveThinking { effort } => {
+                    Some(bedrock::Thinking::Adaptive { effort })
+                }
+                BedrockModelMode::Default => None,
+            }
         } else {
             None
         },

crates/zed/Cargo.toml 🔗

@@ -261,7 +261,7 @@ agent_ui_v2 = { workspace = true, features = ["test-support"] }
 search = { workspace = true, features = ["test-support"] }
 repl = { workspace = true, features = ["test-support"] }
 
-[package.metadata.bundle-dev]
+[package.metadata.bundle]
 icon = ["resources/app-icon-dev@2x.png", "resources/app-icon-dev.png"]
 identifier = "dev.zed.Zed-Dev"
 name = "Zed Dev"