Skip to content

Commit 16c54e9

Browse files
committed
fix(reasoning): skip duplicate callback for <think>-extracted reasoning during streaming (NousResearch#3116)
Local models (Ollama, LM Studio) embed reasoning in <think> tags in delta.content. During streaming, _stream_delta() already displays these blocks. Then _build_assistant_message() extracts them again and fires reasoning_callback, causing duplicate display. Track whether reasoning came from structured fields (reasoning_content) vs <think> tag extraction. Only fire the callback for <think>-extracted reasoning when stream_delta_callback is NOT active. Structured reasoning always fires regardless. Salvaged from PR NousResearch#2076 by dusterbloom (Fix A only — Fix B was already covered by PR NousResearch#3013's _current_reasoning_callback centralization). Closes NousResearch#2069.
1 parent b51db2b commit 16c54e9

1 file changed

Lines changed: 10 additions & 4 deletions

File tree

run_agent.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4494,6 +4494,7 @@ def _build_assistant_message(self, assistant_message, finish_reason: str) -> dic
44944494
so both the tool-call path and the final-response path share one builder.
44954495
"""
44964496
reasoning_text = self._extract_reasoning(assistant_message)
4497+
_from_structured = bool(reasoning_text)
44974498

44984499
# Fallback: extract inline <think> blocks from content when no structured
44994500
# reasoning fields are present (some models/providers embed thinking
@@ -4509,10 +4510,15 @@ def _build_assistant_message(self, assistant_message, finish_reason: str) -> dic
45094510
logging.debug(f"Captured reasoning ({len(reasoning_text)} chars): {reasoning_text}")
45104511

45114512
if reasoning_text and self.reasoning_callback:
4512-
try:
4513-
self.reasoning_callback(reasoning_text)
4514-
except Exception:
4515-
pass
4513+
# Skip callback for <think>-extracted reasoning when streaming is active.
4514+
# _stream_delta() already displayed <think> blocks during streaming;
4515+
# firing the callback again would cause duplicate display.
4516+
# Structured reasoning (from reasoning_content field) always fires.
4517+
if _from_structured or not self.stream_delta_callback:
4518+
try:
4519+
self.reasoning_callback(reasoning_text)
4520+
except Exception:
4521+
pass
45164522

45174523
msg = {
45184524
"role": "assistant",

0 commit comments

Comments
 (0)