feat: Improved support for Anthropic's extended thinking

This commit is contained in:
2026-07-17 17:26:41 -06:00
parent 078e6e3744
commit 8629c1ca15
9 changed files with 121 additions and 24 deletions
+33
View File
@@ -5,6 +5,7 @@ pub(crate) mod todo;
pub(crate) mod user_interaction;
use crate::{
client::ThinkingBlock,
config::{Agent, RequestContext},
graph,
utils::*,
@@ -205,6 +206,8 @@ pub struct ToolResult {
pub output: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub thinking: Vec<ThinkingBlock>,
}
impl ToolResult {
@@ -213,6 +216,7 @@ impl ToolResult {
call,
output,
text: None,
thinking: vec![],
}
}
}
@@ -1914,4 +1918,33 @@ mod tests {
assert_eq!(result.call.name, "my_tool");
assert_eq!(result.output, json!({"result": "ok"}));
}
#[test]
fn thinking_block_matches_anthropic_wire_format() {
let block = ThinkingBlock::Thinking {
thinking: "chain of thought".to_string(),
signature: "sig123".to_string(),
};
assert_eq!(
serde_json::to_value(&block).unwrap(),
json!({"type": "thinking", "thinking": "chain of thought", "signature": "sig123"})
);
let redacted = ThinkingBlock::RedactedThinking {
data: "opaque".to_string(),
};
assert_eq!(
serde_json::to_value(&redacted).unwrap(),
json!({"type": "redacted_thinking", "data": "opaque"})
);
}
#[test]
fn tool_result_deserializes_without_text_and_thinking() {
let yaml = "call:\n name: my_tool\n arguments: {}\noutput: ok\n";
let result: ToolResult = serde_yaml::from_str(yaml).unwrap();
assert_eq!(result.call.name, "my_tool");
assert!(result.text.is_none());
assert!(result.thinking.is_empty());
}
}