Add Claude Opus 4.7 BYOK (#54077) (cherry-pick to preview) (#54190)

Cherry-pick of #54077 to preview

----
<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

Co-authored-by: Richard Feldman <richard@zed.dev>
This commit is contained in:
zed-zippy[bot] 2026-04-17 15:03:30 +00:00 committed by GitHub
parent d84941461c
commit 41d15194ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 96 additions and 11 deletions

View file

@ -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
),
}
}

View file

@ -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"

View file

@ -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?;

View file

@ -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);

View file

@ -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