From 7ecc50d2e4f890d11da0dcfa11231a4a6afb7ebb Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Tue, 7 Apr 2026 22:49:35 -0400 Subject: [PATCH] Capture reasoning/thinking from API stream into Thinking entries StreamResult now includes accumulated reasoning text. After each stream completes, if reasoning was produced, a Thinking entry is pushed to the conversation before the response message. Reasoning content is visible in the context tree UI but not sent back to the API and doesn't count against the token budget. Co-Authored-By: Proof of Concept --- src/agent/api/mod.rs | 8 +++++--- src/agent/mod.rs | 8 +++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/agent/api/mod.rs b/src/agent/api/mod.rs index 48f4039..6822dfd 100644 --- a/src/agent/api/mod.rs +++ b/src/agent/api/mod.rs @@ -578,6 +578,8 @@ pub(crate) struct StreamResult { pub(crate) error: Option, /// Remaining display buffer (caller should flush if not in a tool call). pub(crate) display_buf: String, + /// Accumulated reasoning/thinking content. + pub(crate) reasoning: String, /// Whether we were mid-tool-call when the stream ended. pub(crate) in_tool_call: bool, } @@ -601,6 +603,7 @@ pub(crate) async fn collect_stream( let mut error = None; let mut first_content = true; let mut display_buf = String::new(); + let mut reasoning_buf = String::new(); let mut _streaming_guard: Option = None; while let Some(event) = rx.recv().await { @@ -668,8 +671,7 @@ pub(crate) async fn collect_stream( } } StreamEvent::Reasoning(text) => { - // TODO: reasoning tokens → context entries - let _ = text; + reasoning_buf.push_str(&text); } StreamEvent::ToolCallDelta { index, id, call_type, name, arguments } => { while tool_calls.len() <= index { @@ -696,5 +698,5 @@ pub(crate) async fn collect_stream( } } - StreamResult { content, tool_calls, usage, finish_reason, error, display_buf, in_tool_call } + StreamResult { content, tool_calls, usage, finish_reason, error, display_buf, in_tool_call, reasoning: reasoning_buf } } diff --git a/src/agent/mod.rs b/src/agent/mod.rs index 4d8f41c..4a1c86f 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -477,10 +477,16 @@ impl Agent { ).await; let api::StreamResult { content, tool_calls, usage, finish_reason, - error: stream_error, display_buf, in_tool_call, + error: stream_error, display_buf, in_tool_call, reasoning, } = sr; // --- Stream complete --- + // Push thinking entry if model produced reasoning + if !reasoning.is_empty() { + let mut me = agent.lock().await; + me.push_entry(context::ConversationEntry::Thinking(reasoning)); + } + // --- Lock 3: process results --- let (msg, pending) = { let mut me = agent.lock().await;