Add Claude Opus 4.7 BYOK (#54077)

Richard Feldman created

<img width="767" height="428" alt="Screenshot 2026-04-16 at 11 29 13 AM"
src="https://github.com/user-attachments/assets/e8b450fa-aefc-4dec-a286-b211bd492011"
/>

Add Claude Opus 4.7 (`claude-opus-4-7`) to the anthropic, bedrock, and
opencode provider crates.

Key specs:
- 1M token context window
- 128k max output tokens
- Adaptive thinking support
- AWS Bedrock cross-region inference (global, US, EU, AU)

Release Notes:

- Added Claude Opus 4.7 as an available language model

Change summary

crates/anthropic/src/anthropic.rs                         | 34 ++++++
crates/bedrock/src/models.rs                              | 46 ++++++++
crates/language_models/src/provider/anthropic.rs          |  9 +
crates/language_models_cloud/src/language_models_cloud.rs |  4 
crates/opencode/src/opencode.rs                           | 14 +
5 files changed, 96 insertions(+), 11 deletions(-)

Detailed changes

crates/anthropic/src/anthropic.rs 🔗

@@ -70,6 +70,17 @@ pub enum Model {
         alias = "claude-opus-4-6-1m-context-thinking-latest"
     )]
     ClaudeOpus4_6,
+    #[serde(
+        rename = "claude-opus-4-7",
+        alias = "claude-opus-4-7-latest",
+        alias = "claude-opus-4-7-1m-context",
+        alias = "claude-opus-4-7-1m-context-latest",
+        alias = "claude-opus-4-7-thinking",
+        alias = "claude-opus-4-7-thinking-latest",
+        alias = "claude-opus-4-7-1m-context-thinking",
+        alias = "claude-opus-4-7-1m-context-thinking-latest"
+    )]
+    ClaudeOpus4_7,
     #[serde(
         rename = "claude-sonnet-4",
         alias = "claude-sonnet-4-latest",
@@ -130,6 +141,10 @@ impl Model {
     }
 
     pub fn from_id(id: &str) -> Result<Self> {
+        if id.starts_with("claude-opus-4-7") {
+            return Ok(Self::ClaudeOpus4_7);
+        }
+
         if id.starts_with("claude-opus-4-6") {
             return Ok(Self::ClaudeOpus4_6);
         }
@@ -175,6 +190,7 @@ impl Model {
             Self::ClaudeOpus4_1 => "claude-opus-4-1-latest",
             Self::ClaudeOpus4_5 => "claude-opus-4-5-latest",
             Self::ClaudeOpus4_6 => "claude-opus-4-6-latest",
+            Self::ClaudeOpus4_7 => "claude-opus-4-7-latest",
             Self::ClaudeSonnet4 => "claude-sonnet-4-latest",
             Self::ClaudeSonnet4_5 => "claude-sonnet-4-5-latest",
             Self::ClaudeSonnet4_6 => "claude-sonnet-4-6-latest",
@@ -191,6 +207,7 @@ impl Model {
             Self::ClaudeOpus4_1 => "claude-opus-4-1-20250805",
             Self::ClaudeOpus4_5 => "claude-opus-4-5-20251101",
             Self::ClaudeOpus4_6 => "claude-opus-4-6",
+            Self::ClaudeOpus4_7 => "claude-opus-4-7",
             Self::ClaudeSonnet4 => "claude-sonnet-4-20250514",
             Self::ClaudeSonnet4_5 => "claude-sonnet-4-5-20250929",
             Self::ClaudeSonnet4_6 => "claude-sonnet-4-6",
@@ -206,6 +223,7 @@ impl Model {
             Self::ClaudeOpus4_1 => "Claude Opus 4.1",
             Self::ClaudeOpus4_5 => "Claude Opus 4.5",
             Self::ClaudeOpus4_6 => "Claude Opus 4.6",
+            Self::ClaudeOpus4_7 => "Claude Opus 4.7",
             Self::ClaudeSonnet4 => "Claude Sonnet 4",
             Self::ClaudeSonnet4_5 => "Claude Sonnet 4.5",
             Self::ClaudeSonnet4_6 => "Claude Sonnet 4.6",
@@ -223,6 +241,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4
             | Self::ClaudeSonnet4_5
             | Self::ClaudeSonnet4_6
@@ -248,7 +267,7 @@ impl Model {
             | Self::ClaudeSonnet4_5
             | Self::ClaudeHaiku4_5
             | Self::Claude3Haiku => 200_000,
-            Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6 => 1_000_000,
+            Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 | Self::ClaudeSonnet4_6 => 1_000_000,
             Self::Custom { max_tokens, .. } => *max_tokens,
         }
     }
@@ -261,7 +280,7 @@ impl Model {
             | Self::ClaudeSonnet4_5
             | Self::ClaudeSonnet4_6
             | Self::ClaudeHaiku4_5 => 64_000,
-            Self::ClaudeOpus4_6 => 128_000,
+            Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 => 128_000,
             Self::Claude3Haiku => 4_096,
             Self::Custom {
                 max_output_tokens, ..
@@ -275,6 +294,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4
             | Self::ClaudeSonnet4_5
             | Self::ClaudeSonnet4_6
@@ -312,6 +332,7 @@ impl Model {
                     | Self::ClaudeOpus4_1
                     | Self::ClaudeOpus4_5
                     | Self::ClaudeOpus4_6
+                    | Self::ClaudeOpus4_7
                     | Self::ClaudeSonnet4
                     | Self::ClaudeSonnet4_5
                     | Self::ClaudeSonnet4_6
@@ -320,10 +341,17 @@ impl Model {
         }
     }
 
+    pub fn supports_speed(&self) -> bool {
+        matches!(self, Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6)
+    }
+
     pub fn supports_adaptive_thinking(&self) -> bool {
         match self {
             Self::Custom { mode, .. } => matches!(mode, AnthropicModelMode::AdaptiveThinking),
-            _ => matches!(self, Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6),
+            _ => matches!(
+                self,
+                Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 | Self::ClaudeSonnet4_6
+            ),
         }
     }
 

crates/bedrock/src/models.rs 🔗

@@ -84,6 +84,13 @@ pub enum Model {
         alias = "claude-opus-4-6-thinking-latest"
     )]
     ClaudeOpus4_6,
+    #[serde(
+        rename = "claude-opus-4-7",
+        alias = "claude-opus-4-7-latest",
+        alias = "claude-opus-4-7-thinking",
+        alias = "claude-opus-4-7-thinking-latest"
+    )]
+    ClaudeOpus4_7,
     #[serde(
         rename = "claude-sonnet-4-6",
         alias = "claude-sonnet-4-6-latest",
@@ -203,7 +210,9 @@ impl Model {
     }
 
     pub fn from_id(id: &str) -> anyhow::Result<Self> {
-        if id.starts_with("claude-opus-4-6") {
+        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)
         } else if id.starts_with("claude-opus-4-5") {
             Ok(Self::ClaudeOpus4_5)
@@ -230,6 +239,7 @@ impl Model {
             Self::ClaudeOpus4_1 => "claude-opus-4-1",
             Self::ClaudeOpus4_5 => "claude-opus-4-5",
             Self::ClaudeOpus4_6 => "claude-opus-4-6",
+            Self::ClaudeOpus4_7 => "claude-opus-4-7",
             Self::ClaudeSonnet4_6 => "claude-sonnet-4-6",
             Self::Llama4Scout17B => "llama-4-scout-17b",
             Self::Llama4Maverick17B => "llama-4-maverick-17b",
@@ -279,6 +289,7 @@ impl Model {
             Self::ClaudeOpus4_1 => "anthropic.claude-opus-4-1-20250805-v1:0",
             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-v1",
             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",
@@ -328,6 +339,7 @@ impl Model {
             Self::ClaudeOpus4_1 => "Claude Opus 4.1",
             Self::ClaudeOpus4_5 => "Claude Opus 4.5",
             Self::ClaudeOpus4_6 => "Claude Opus 4.6",
+            Self::ClaudeOpus4_7 => "Claude Opus 4.7",
             Self::ClaudeSonnet4_6 => "Claude Sonnet 4.6",
             Self::Llama4Scout17B => "Llama 4 Scout 17B",
             Self::Llama4Maverick17B => "Llama 4 Maverick 17B",
@@ -383,6 +395,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4_6 => 200_000,
             Self::Llama4Scout17B | Self::Llama4Maverick17B => 128_000,
             Self::Gemma3_4B | Self::Gemma3_12B | Self::Gemma3_27B => 128_000,
@@ -416,7 +429,7 @@ impl Model {
             | Self::ClaudeOpus4_5
             | Self::ClaudeSonnet4_6 => 64_000,
             Self::ClaudeOpus4_1 => 32_000,
-            Self::ClaudeOpus4_6 => 128_000,
+            Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 => 128_000,
             Self::Llama4Scout17B
             | Self::Llama4Maverick17B
             | Self::Gemma3_4B
@@ -454,6 +467,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4_6 => 1.0,
             Self::Custom {
                 default_temperature,
@@ -471,6 +485,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4_6 => true,
             Self::NovaLite | Self::NovaPro | Self::NovaPremier | Self::Nova2Lite => true,
             Self::MistralLarge3 | Self::PixtralLarge | Self::MagistralSmall => true,
@@ -501,6 +516,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4_6 => true,
             Self::NovaLite | Self::NovaPro => true,
             Self::PixtralLarge => true,
@@ -517,6 +533,7 @@ impl Model {
                 | Self::ClaudeSonnet4_5
                 | Self::ClaudeOpus4_5
                 | Self::ClaudeOpus4_6
+                | Self::ClaudeOpus4_7
                 | Self::ClaudeSonnet4_6
         )
     }
@@ -529,6 +546,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4_6 => true,
             Self::Custom {
                 cache_configuration,
@@ -545,6 +563,7 @@ impl Model {
             | Self::ClaudeOpus4_1
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_6
+            | Self::ClaudeOpus4_7
             | Self::ClaudeSonnet4_6 => Some(BedrockModelCacheConfiguration {
                 max_cache_anchors: 4,
                 min_total_token: 1024,
@@ -570,12 +589,16 @@ impl Model {
                 | Self::ClaudeOpus4_1
                 | Self::ClaudeOpus4_5
                 | Self::ClaudeOpus4_6
+                | Self::ClaudeOpus4_7
                 | Self::ClaudeSonnet4_6
         )
     }
 
     pub fn supports_adaptive_thinking(&self) -> bool {
-        matches!(self, Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6)
+        matches!(
+            self,
+            Self::ClaudeOpus4_6 | Self::ClaudeOpus4_7 | Self::ClaudeSonnet4_6
+        )
     }
 
     pub fn thinking_mode(&self) -> BedrockModelMode {
@@ -606,6 +629,7 @@ impl Model {
                 | Self::ClaudeSonnet4_5
                 | Self::ClaudeOpus4_5
                 | Self::ClaudeOpus4_6
+                | Self::ClaudeOpus4_7
                 | Self::ClaudeSonnet4_6
                 | Self::Nova2Lite
         );
@@ -665,6 +689,7 @@ impl Model {
                 | Self::ClaudeSonnet4_5
                 | Self::ClaudeOpus4_5
                 | Self::ClaudeOpus4_6
+                | Self::ClaudeOpus4_7
                 | Self::ClaudeSonnet4_6
                 | Self::Nova2Lite,
                 "global",
@@ -681,6 +706,7 @@ impl Model {
                 | Self::ClaudeOpus4_1
                 | Self::ClaudeOpus4_5
                 | Self::ClaudeOpus4_6
+                | Self::ClaudeOpus4_7
                 | Self::ClaudeSonnet4_6
                 | Self::Llama4Scout17B
                 | Self::Llama4Maverick17B
@@ -702,6 +728,7 @@ impl Model {
                 | Self::ClaudeSonnet4
                 | Self::ClaudeSonnet4_5
                 | Self::ClaudeOpus4_6
+                | Self::ClaudeOpus4_7
                 | Self::ClaudeSonnet4_6
                 | Self::NovaLite
                 | Self::NovaPro
@@ -714,6 +741,7 @@ impl Model {
                 Self::ClaudeHaiku4_5
                 | Self::ClaudeSonnet4_5
                 | Self::ClaudeOpus4_6
+                | Self::ClaudeOpus4_7
                 | Self::ClaudeSonnet4_6,
                 "au",
             ) => Ok(format!("{}.{}", region_group, model_id)),
@@ -787,6 +815,10 @@ mod tests {
             Model::ClaudeOpus4_6.cross_region_inference_id("eu-west-1", false)?,
             "eu.anthropic.claude-opus-4-6-v1"
         );
+        assert_eq!(
+            Model::ClaudeOpus4_7.cross_region_inference_id("eu-west-1", false)?,
+            "eu.anthropic.claude-opus-4-7-v1"
+        );
         Ok(())
     }
 
@@ -817,6 +849,10 @@ mod tests {
             Model::ClaudeOpus4_6.cross_region_inference_id("ap-southeast-2", false)?,
             "au.anthropic.claude-opus-4-6-v1"
         );
+        assert_eq!(
+            Model::ClaudeOpus4_7.cross_region_inference_id("ap-southeast-2", false)?,
+            "au.anthropic.claude-opus-4-7-v1"
+        );
         Ok(())
     }
 
@@ -877,6 +913,10 @@ mod tests {
             Model::ClaudeOpus4_6.cross_region_inference_id("us-east-1", true)?,
             "global.anthropic.claude-opus-4-6-v1"
         );
+        assert_eq!(
+            Model::ClaudeOpus4_7.cross_region_inference_id("us-east-1", true)?,
+            "global.anthropic.claude-opus-4-7-v1"
+        );
         assert_eq!(
             Model::Nova2Lite.cross_region_inference_id("us-east-1", true)?,
             "global.amazon.nova-2-lite-v1:0"

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

@@ -326,6 +326,10 @@ impl LanguageModel for AnthropicModel {
         self.model.supports_thinking()
     }
 
+    fn supports_fast_mode(&self) -> bool {
+        self.model.supports_speed()
+    }
+
     fn supported_effort_levels(&self) -> Vec<language_model::LanguageModelEffortLevel> {
         if self.model.supports_adaptive_thinking() {
             vec![
@@ -431,13 +435,16 @@ impl LanguageModel for AnthropicModel {
             LanguageModelCompletionError,
         >,
     > {
-        let request = into_anthropic(
+        let mut request = into_anthropic(
             request,
             self.model.request_id().into(),
             self.model.default_temperature(),
             self.model.max_output_tokens(),
             self.model.mode(),
         );
+        if !self.model.supports_speed() {
+            request.speed = None;
+        }
         let request = self.stream_completion(request, cx);
         let future = self.request_limiter.stream(async move {
             let response = request.await?;

crates/language_models_cloud/src/language_models_cloud.rs 🔗

@@ -496,6 +496,10 @@ impl<TP: CloudLlmTokenProvider + 'static> LanguageModel for CloudLanguageModel<T
                     request.output_config = Some(anthropic::OutputConfig { effort });
                 }
 
+                if !self.model.supports_fast_mode {
+                    request.speed = None;
+                }
+
                 let http_client = self.http_client.clone();
                 let token_provider = self.token_provider.clone();
                 let auth_context = token_provider.auth_context(cx);

crates/opencode/src/opencode.rs 🔗

@@ -21,6 +21,8 @@ pub enum ApiProtocol {
 #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
 pub enum Model {
     // -- Anthropic protocol models --
+    #[serde(rename = "claude-opus-4-7")]
+    ClaudeOpus4_7,
     #[serde(rename = "claude-opus-4-6")]
     ClaudeOpus4_6,
     #[serde(rename = "claude-opus-4-5")]
@@ -117,6 +119,7 @@ impl Model {
 
     pub fn id(&self) -> &str {
         match self {
+            Self::ClaudeOpus4_7 => "claude-opus-4-7",
             Self::ClaudeOpus4_6 => "claude-opus-4-6",
             Self::ClaudeOpus4_5 => "claude-opus-4-5",
             Self::ClaudeOpus4_1 => "claude-opus-4-1",
@@ -162,6 +165,7 @@ impl Model {
 
     pub fn display_name(&self) -> &str {
         match self {
+            Self::ClaudeOpus4_7 => "Claude Opus 4.7",
             Self::ClaudeOpus4_6 => "Claude Opus 4.6",
             Self::ClaudeOpus4_5 => "Claude Opus 4.5",
             Self::ClaudeOpus4_1 => "Claude Opus 4.1",
@@ -209,7 +213,8 @@ impl Model {
 
     pub fn protocol(&self) -> ApiProtocol {
         match self {
-            Self::ClaudeOpus4_6
+            Self::ClaudeOpus4_7
+            | Self::ClaudeOpus4_6
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_1
             | Self::ClaudeSonnet4_6
@@ -254,7 +259,7 @@ impl Model {
     pub fn max_token_count(&self) -> u64 {
         match self {
             // Anthropic models
-            Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6 => 1_000_000,
+            Self::ClaudeOpus4_7 | Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6 => 1_000_000,
             Self::ClaudeOpus4_5 | Self::ClaudeSonnet4_5 | Self::ClaudeSonnet4 => 200_000,
             Self::ClaudeOpus4_1 => 200_000,
             Self::ClaudeHaiku4_5 => 200_000,
@@ -292,7 +297,7 @@ impl Model {
     pub fn max_output_tokens(&self) -> Option<u64> {
         match self {
             // Anthropic models
-            Self::ClaudeOpus4_6 => Some(128_000),
+            Self::ClaudeOpus4_7 | Self::ClaudeOpus4_6 => Some(128_000),
             Self::ClaudeSonnet4_6 => Some(64_000),
             Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_1
@@ -342,7 +347,8 @@ impl Model {
     pub fn supports_images(&self) -> bool {
         match self {
             // Anthropic models support images
-            Self::ClaudeOpus4_6
+            Self::ClaudeOpus4_7
+            | Self::ClaudeOpus4_6
             | Self::ClaudeOpus4_5
             | Self::ClaudeOpus4_1
             | Self::ClaudeSonnet4_6