fix(loop): preserve reasoning_content on V4 deepseek-chat round-trips

V4-era deepseek-chat now returns non-empty `reasoning_content` even
with `extra_body.thinking.type = "disabled"`. The API then rejects
round-trips that drop the field with "reasoning_content in the thinking
mode must be passed back".

The whitelist in `isThinkingModeModel` was too narrow — it only tagged
deepseek-reasoner / v4-flash / v4-pro. `assistantMessage()` now also
preserves reasoning_content whenever the producer emitted any non-empty
content, regardless of model name. The whitelist still applies to
synthetic messages where reasoningContent is empty (so they get an
empty string stamp for thinking-mode endpoints).

Caught by tau-bench (24/24 reasonix runs were failing on the kernel
HEAD; same test on v0.2.1 passed 100%). Single-task verify now hits
92.3% cache and passes.
This commit is contained in:
esengine committed 2026-04-29 09:19:25 -07:00
1 parent b3c602c112
commit a27d46fe48
1 file changed
+4 -1
+4 -1
View File
@@ -1430,7 +1430,10 @@ export class CacheFirstLoop {
): ChatMessage {
const msg: ChatMessage = { role: "assistant", content };
if (toolCalls.length > 0) msg.tool_calls = toolCalls;
if (isThinkingModeModel(producingModel)) {
// V4-era deepseek-chat returns reasoning_content even with thinking.type
// disabled, and the API rejects round-trips that drop it. Whitelist on
// model name is too brittle — preserve whenever the producer emitted any.
if (isThinkingModeModel(producingModel) || (reasoningContent && reasoningContent.length > 0)) {
msg.reasoning_content = reasoningContent ?? "";
}
return msg;