fix(reasoning): promote reasoning field before empty-pad for DeepSeek/Kimi (#15812)#15830
Conversation
…/Kimi (NousResearch#15812) PR NousResearch#15749 reordered _copy_reasoning_content_for_api so the tool-call empty-string injection (step 2) ran before the reasoning field promotion (step 3). A tool-call message that had a valid 'reasoning' field but no 'reasoning_content' would hit step 2, get reasoning_content="" injected, and return early — the reasoning field was never promoted and was silently lost. Fix: move the 'reasoning' → 'reasoning_content' promotion to step 2, before the empty-string fallback paths. Empty-string injection now only fires when neither reasoning_content nor reasoning is present, which is the poisoned-history case it was designed to handle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a regression in AIAgent._copy_reasoning_content_for_api where DeepSeek/Kimi tool-call messages that have a valid internal reasoning field could incorrectly get reasoning_content="" injected and return early, dropping the reasoning content on API replay.
Changes:
- Reorders logic in
_copy_reasoning_content_for_apisoreasoning → reasoning_contentpromotion happens before the DeepSeek/Kimi empty-string fallback. - Narrows the “poisoned history” empty-string injection (for tool-call turns) to only apply when neither
reasoning_contentnorreasoningis present.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Closing as redundant — the DeepSeek
21 regression tests in |
Summary
_copy_reasoning_content_for_apihad a step-ordering bug introduced by PR fix(agent): ordering fix in _copy_reasoning_content_for_api — cross-provider reasoning isolation #15749: the tool-call empty-string injection (old step 2) ran before thereasoningfield promotion (old step 3)reasoningfield but noreasoning_contentwould hit the empty-string path, getreasoning_content=""injected, and return early — thereasoningvalue was silently discardedreasoning→reasoning_contentpromotion to step 2, before the empty-string fallbacksThe bug
run_agent.py—_copy_reasoning_content_for_api(pre-fix):A message like
{"role": "assistant", "reasoning": "thought trace", "tool_calls": [...]}on a DeepSeek session would returnreasoning_content=""instead ofreasoning_content="thought trace".The fix
Reorder steps 2 and 3 so
reasoningpromotion runs first:Empty-string injection now only fires when neither
reasoning_contentnorreasoningis present — the poisoned-history case it was designed for.Test plan
test_deepseek_reasoning_field_promoted→AssertionError: assert '' == 'thought trace'test_deepseek_reasoning_content_echo.pypassrun_agent.py→test_deepseek_reasoning_field_promotedfailed with'' != 'thought trace'; restored → 21 passingtests/run_agent/— 1048 passed, 1 pre-existing baseline failure (test_inf_stays_string_for_integer_onlyreproduces on cleanorigin/main)Related
🤖 Generated with Claude Code