diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index 311898f906b982e4bcacd331bc8b36085816498a..b6973ad51d265499d1db3548d2602ff33f4e8bd9 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -144,6 +144,10 @@ impl Message { } } + pub fn push_redacted_thinking(&mut self, data: String) { + self.segments.push(MessageSegment::RedactedThinking(data)); + } + pub fn push_text(&mut self, text: &str) { if let Some(MessageSegment::Text(segment)) = self.segments.last_mut() { segment.push_str(text); @@ -182,7 +186,7 @@ pub enum MessageSegment { text: String, signature: Option, }, - RedactedThinking(Vec), + RedactedThinking(String), } impl MessageSegment { @@ -1644,6 +1648,25 @@ impl Thread { }; } } + LanguageModelCompletionEvent::RedactedThinking { + data + } => { + thread.received_chunk(); + + if let Some(last_message) = thread.messages.last_mut() { + if last_message.role == Role::Assistant + && !thread.tool_use.has_tool_results(last_message.id) + { + last_message.push_redacted_thinking(data); + } else { + request_assistant_message_id = + Some(thread.insert_assistant_message( + vec![MessageSegment::RedactedThinking(data)], + cx, + )); + }; + } + } LanguageModelCompletionEvent::ToolUse(tool_use) => { let last_assistant_message_id = request_assistant_message_id .unwrap_or_else(|| { diff --git a/crates/agent/src/thread_store.rs b/crates/agent/src/thread_store.rs index db87bdd3a59ae70cd7550bc3ce359bd6f78617d0..1d26b3bef4dcaf50344cc8101bea83d1d68bca82 100644 --- a/crates/agent/src/thread_store.rs +++ b/crates/agent/src/thread_store.rs @@ -730,7 +730,7 @@ pub enum SerializedMessageSegment { signature: Option, }, RedactedThinking { - data: Vec, + data: String, }, } diff --git a/crates/assistant_context_editor/src/context.rs b/crates/assistant_context_editor/src/context.rs index 2a833eb130cef3e3ad901a17a6481bb7fbd1f794..2ca26321d19f7f66c7bcaf4f52a416c84b7acf34 100644 --- a/crates/assistant_context_editor/src/context.rs +++ b/crates/assistant_context_editor/src/context.rs @@ -2110,6 +2110,7 @@ impl AssistantContext { ); } } + LanguageModelCompletionEvent::RedactedThinking { .. } => {}, LanguageModelCompletionEvent::Text(mut chunk) => { if let Some(start) = thought_process_stack.pop() { let end = buffer.anchor_before(message_old_end_offset); diff --git a/crates/eval/src/instance.rs b/crates/eval/src/instance.rs index f28165e859be017b28e26e359d0df9e5b1f63391..5e286ad334d3d432d4caf0217eb2454343fb8d09 100644 --- a/crates/eval/src/instance.rs +++ b/crates/eval/src/instance.rs @@ -1024,6 +1024,7 @@ pub fn response_events_to_markdown( Ok(LanguageModelCompletionEvent::Thinking { text, .. }) => { thinking_buffer.push_str(text); } + Ok(LanguageModelCompletionEvent::RedactedThinking { .. }) => {} Ok(LanguageModelCompletionEvent::Stop(reason)) => { flush_buffers(&mut response, &mut text_buffer, &mut thinking_buffer); response.push_str(&format!("**Stop**: {:?}\n\n", reason)); @@ -1120,6 +1121,7 @@ impl ThreadDialog { // Skip these Ok(LanguageModelCompletionEvent::UsageUpdate(_)) + | Ok(LanguageModelCompletionEvent::RedactedThinking { .. }) | Ok(LanguageModelCompletionEvent::StatusUpdate { .. }) | Ok(LanguageModelCompletionEvent::StartMessage { .. }) | Ok(LanguageModelCompletionEvent::Stop(_)) => {} diff --git a/crates/language_model/src/language_model.rs b/crates/language_model/src/language_model.rs index 01f005d73c05f467a744fb34b88b243070d5e4b3..361494542f99d2384760bce362c65cf29eec04e1 100644 --- a/crates/language_model/src/language_model.rs +++ b/crates/language_model/src/language_model.rs @@ -66,6 +66,9 @@ pub enum LanguageModelCompletionEvent { text: String, signature: Option, }, + RedactedThinking { + data: String, + }, ToolUse(LanguageModelToolUse), StartMessage { message_id: String, @@ -314,6 +317,7 @@ pub trait LanguageModel: Send + Sync { Ok(LanguageModelCompletionEvent::StartMessage { .. }) => None, Ok(LanguageModelCompletionEvent::Text(text)) => Some(Ok(text)), Ok(LanguageModelCompletionEvent::Thinking { .. }) => None, + Ok(LanguageModelCompletionEvent::RedactedThinking { .. }) => None, Ok(LanguageModelCompletionEvent::Stop(_)) => None, Ok(LanguageModelCompletionEvent::ToolUse(_)) => None, Ok(LanguageModelCompletionEvent::UsageUpdate(token_usage)) => { diff --git a/crates/language_model/src/request.rs b/crates/language_model/src/request.rs index 559d8e9111405cef4c1b039a7c8ffa945de1d950..451a62775e6331b139ef5c4da57e4d7d930af6f8 100644 --- a/crates/language_model/src/request.rs +++ b/crates/language_model/src/request.rs @@ -303,7 +303,7 @@ pub enum MessageContent { text: String, signature: Option, }, - RedactedThinking(Vec), + RedactedThinking(String), Image(LanguageModelImage), ToolUse(LanguageModelToolUse), ToolResult(LanguageModelToolResult), diff --git a/crates/language_models/src/provider/anthropic.rs b/crates/language_models/src/provider/anthropic.rs index c581f01f4ce1dd56af59a22e32bcbb2d991a1627..307b279438bc80aa4503a9a04b0378fe7b059818 100644 --- a/crates/language_models/src/provider/anthropic.rs +++ b/crates/language_models/src/provider/anthropic.rs @@ -561,9 +561,7 @@ pub fn into_anthropic( } MessageContent::RedactedThinking(data) => { if !data.is_empty() { - Some(anthropic::RequestContent::RedactedThinking { - data: String::from_utf8(data).ok()?, - }) + Some(anthropic::RequestContent::RedactedThinking { data }) } else { None } @@ -737,10 +735,8 @@ impl AnthropicEventMapper { signature: None, })] } - ResponseContent::RedactedThinking { .. } => { - // Redacted thinking is encrypted and not accessible to the user, see: - // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#suggestions-for-handling-redacted-thinking-in-production - Vec::new() + ResponseContent::RedactedThinking { data } => { + vec![Ok(LanguageModelCompletionEvent::RedactedThinking { data })] } ResponseContent::ToolUse { id, name, .. } => { self.tool_uses_by_index.insert(