mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 07:17:54 +08:00
core/todo-nag: inject reasoning_content on synthetic assistant messages
DeepSeek's reasoning API enforces: if an assistant with tool_calls sits between two user messages, its reasoning_content must be passed back. The synthetic todo nag pair creates an AssistantChatMessage with tool_calls but no reasoning_content, which triggered a 400 error: "The reasoning_content in the thinking mode must be passed back to the API." Fix: 1. _append_synthetic_todo_list now sets reasoning_content="" so NEW pairs carry the field. 2. refresh_synthetic_todo_nags runs _inject_reasoning_content_for_synthetic to fix EXISTING pairs (in DB) even if reasoning_content was UNSET. 3. The earlier _inject_thinking change is reverted in the next commit since the real root cause was the missing reasoning_content on forged tool_call messages, not the thinking parameter itself.
This commit is contained in:
@@ -69,6 +69,7 @@ def _append_synthetic_todo_list(messages: list[AnyChatMessage], body: str) -> st
|
||||
messages.append(
|
||||
AssistantChatMessage(
|
||||
content=UNSET,
|
||||
reasoning_content="",
|
||||
tool_calls=[
|
||||
AssistantFunctionToolCall(
|
||||
id=call_id,
|
||||
@@ -130,9 +131,39 @@ def refresh_synthetic_todo_nags(
|
||||
continue
|
||||
messages[index] = ToolChatMessage(tool_call_id=msg.tool_call_id, content=body)
|
||||
updated += 1
|
||||
|
||||
# Synthetic assistant messages (tool calls but no reasoning_content) trip
|
||||
# DeepSeek's rule: "if the model performed a tool call between two user
|
||||
# messages, the assistant's reasoning_content must be passed back."
|
||||
# Inject empty reasoning_content so the API doesn't reject the request.
|
||||
updated += _inject_reasoning_content_for_synthetic(messages)
|
||||
return updated
|
||||
|
||||
|
||||
def _inject_reasoning_content_for_synthetic(
|
||||
messages: list[AnyChatMessage],
|
||||
) -> int:
|
||||
"""Inject ``reasoning_content=\"\"`` into synthetic nag assistants that lack it."""
|
||||
injected = 0
|
||||
for index, msg in enumerate(messages):
|
||||
if not isinstance(msg, AssistantChatMessage):
|
||||
continue
|
||||
tool_calls = msg.tool_calls
|
||||
if tool_calls is UNSET or not tool_calls:
|
||||
continue
|
||||
if not any(
|
||||
isinstance(c, AssistantFunctionToolCall) and is_synthetic_todo_nag_call_id(c.id) for c in tool_calls
|
||||
):
|
||||
continue
|
||||
if msg.reasoning_content is not UNSET:
|
||||
continue
|
||||
from msgspec import structs
|
||||
|
||||
messages[index] = structs.replace(msg, reasoning_content="")
|
||||
injected += 1
|
||||
return injected
|
||||
|
||||
|
||||
def inject_todo_nag(
|
||||
messages: list[AnyChatMessage],
|
||||
body: str,
|
||||
|
||||
Reference in New Issue
Block a user