From 86d625784bd13435b273c931306d329b92c503e7 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 23 Apr 2026 08:43:02 -0700 Subject: [PATCH] Request summarized thinking for Claude Opus 4.7 (#54217) Starting with Claude Opus 4.7, Anthropic omits thinking content from responses by default; callers must pass `display: "summarized"` to keep seeing thinking summaries. Without opting in, the agent UI shows a long pause with no visible thinking, and users get no progress indication during extended reasoning. This extends the adaptive-thinking wire type with an optional `display` field and requests `Summarized` from every call site that builds an adaptive thinking request (direct Anthropic, Copilot Chat proxy, Zed Cloud, and Bedrock). ## Notes - Applied at the adaptive-thinking layer rather than special-casing Opus 4.7. The `display` parameter is accepted by every adaptive-thinking-capable model, and the previous behavior (visible summaries) is what users already see on Opus 4.6 / Sonnet 4.6, so there is no behavior change for those models. Release Notes: - Restored thinking summaries for Claude Opus 4.7. --- crates/anthropic/src/anthropic.rs | 16 ++++++++++++++-- crates/anthropic/src/completion.rs | 10 ++++++---- crates/bedrock/src/bedrock.rs | 9 +++++++-- .../language_models/src/provider/copilot_chat.rs | 4 +++- .../src/language_models_cloud.rs | 4 +++- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/crates/anthropic/src/anthropic.rs b/crates/anthropic/src/anthropic.rs index ac94daba71..269edbea7e 100644 --- a/crates/anthropic/src/anthropic.rs +++ b/crates/anthropic/src/anthropic.rs @@ -764,8 +764,20 @@ pub enum ToolChoice { #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "lowercase")] pub enum Thinking { - Enabled { budget_tokens: Option }, - Adaptive, + Enabled { + budget_tokens: Option, + }, + Adaptive { + #[serde(default, skip_serializing_if = "Option::is_none")] + display: Option, + }, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum AdaptiveThinkingDisplay { + Omitted, + Summarized, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, EnumString)] diff --git a/crates/anthropic/src/completion.rs b/crates/anthropic/src/completion.rs index 16bb5012e5..7bb4821cc7 100644 --- a/crates/anthropic/src/completion.rs +++ b/crates/anthropic/src/completion.rs @@ -11,9 +11,9 @@ use std::pin::Pin; use std::str::FromStr; use crate::{ - AnthropicError, AnthropicModelMode, CacheControl, CacheControlType, ContentDelta, Event, - ImageSource, Message, RequestContent, ResponseContent, StringOrContents, Thinking, Tool, - ToolChoice, ToolResultContent, ToolResultPart, Usage, + AdaptiveThinkingDisplay, AnthropicError, AnthropicModelMode, CacheControl, CacheControlType, + ContentDelta, Event, ImageSource, Message, RequestContent, ResponseContent, StringOrContents, + Thinking, Tool, ToolChoice, ToolResultContent, ToolResultPart, Usage, }; fn to_anthropic_content(content: MessageContent) -> Option { @@ -180,7 +180,9 @@ pub fn into_anthropic( AnthropicModelMode::Thinking { budget_tokens } => { Some(Thinking::Enabled { budget_tokens }) } - AnthropicModelMode::AdaptiveThinking => Some(Thinking::Adaptive), + AnthropicModelMode::AdaptiveThinking => Some(Thinking::Adaptive { + display: Some(AdaptiveThinkingDisplay::Summarized), + }), AnthropicModelMode::Default => None, } } else { diff --git a/crates/bedrock/src/bedrock.rs b/crates/bedrock/src/bedrock.rs index 12db814d3a..a3f60fd7b5 100644 --- a/crates/bedrock/src/bedrock.rs +++ b/crates/bedrock/src/bedrock.rs @@ -58,8 +58,13 @@ pub async fn stream_completion( additional_fields.insert("thinking".to_string(), Document::from(thinking_config)); } Some(Thinking::Adaptive { effort: _ }) => { - let thinking_config = - HashMap::from([("type".to_string(), Document::String("adaptive".to_string()))]); + let thinking_config = HashMap::from([ + ("type".to_string(), Document::String("adaptive".to_string())), + ( + "display".to_string(), + Document::String("summarized".to_string()), + ), + ]); additional_fields.insert("thinking".to_string(), Document::from(thinking_config)); } _ => {} diff --git a/crates/language_models/src/provider/copilot_chat.rs b/crates/language_models/src/provider/copilot_chat.rs index ef9fbae113..db50f5161e 100644 --- a/crates/language_models/src/provider/copilot_chat.rs +++ b/crates/language_models/src/provider/copilot_chat.rs @@ -365,7 +365,9 @@ impl LanguageModel for CopilotChatLanguageModel { if model.supports_adaptive_thinking() { if anthropic_request.thinking.is_some() { - anthropic_request.thinking = Some(anthropic::Thinking::Adaptive); + anthropic_request.thinking = Some(anthropic::Thinking::Adaptive { + display: Some(anthropic::AdaptiveThinkingDisplay::Summarized), + }); anthropic_request.output_config = effort.map(|effort| anthropic::OutputConfig { effort: Some(effort), diff --git a/crates/language_models_cloud/src/language_models_cloud.rs b/crates/language_models_cloud/src/language_models_cloud.rs index adae72068c..da5659bf50 100644 --- a/crates/language_models_cloud/src/language_models_cloud.rs +++ b/crates/language_models_cloud/src/language_models_cloud.rs @@ -408,7 +408,9 @@ impl LanguageModel for CloudLanguageModel