fix(api-server): preserve tool_call_id in conversation_history - #17632
fix(api-server): preserve tool_call_id in conversation_history#17632yzxuyang wants to merge 1 commit into
Conversation
When the API server receives conversation_history from the web UI (via POST /v1/runs), it was reconstructing each message with only 'role' and 'content' fields, dropping tool_call_id, tool_calls, and other metadata. This caused a 400 error from strict providers (DeepSeek, etc.) on follow-up messages, since tool role messages must include tool_call_id. Fix: copy all fields from the incoming entry, preserving tool_call_id and other metadata while still ensuring role/content are stringified. Applies to both explicit conversation_history and multi-message input array paths.
|
Likely duplicate of #17422 — same root cause: api_server.py strips tool_call_id when reconstructing conversation_history messages. |
1 similar comment
|
Likely duplicate of #17422 — same root cause: api_server.py strips tool_call_id when reconstructing conversation_history messages. |
|
Thanks for isolating the two Problems
Suggested changes
Automated hermes-sweeper review. |
SummaryThree PRs address the reported history-normalization defect. #17422 preserves tool metadata in Related pull requests
Duplicates#17422 and #17632 substantially overlap on preserving tool metadata in Suggested consolidationKeep open #19415 with a salvage path: retain its shared role-aware allowlist and regression tests, and verify the full gateway history-normalization behavior before further triage. Close #17632 as duplicate of #19415 despite the MAINTAINER-BOT VERDICT recommending keep_open with high salvageability, because #19415's diff contains the same Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
subgraph Dup17422 ["PRs duplicating each other"]
P17422["PR #17422 (closed)"]
P17632["PR #17632 (open)"]
P19415["PR #19415 (open)"]
end
class P17422 closed
class P17632 open
class P19415 open
class P17632 target
click P17422 "https://github.com/NousResearch/hermes-agent/pull/17422"
click P17632 "https://github.com/NousResearch/hermes-agent/pull/17632"
click P19415 "https://github.com/NousResearch/hermes-agent/pull/19415"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 3 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 39 kB of PR diffs, 7 kB of issue/PR text, 2 kB of discussion (7 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
Problem
When sending follow-up messages via the Hermes Web UI through the API Server, DeepSeek (and other strict OpenAI-format providers) returns a 400 error:
Root Cause
_handle_runs()ingateway/platforms/api_server.pyreconstructsconversation_historymessages with onlyroleandcontentfields, droppingtool_call_id,tool_calls, and other metadata.This affects two code paths:
conversation_historyfrom request body (web UI) [line 2409]inputarray mode [line 2438]Fix
Copy all fields from the incoming message dict instead of creating a new dict with only
role+content. This preservestool_call_idand other metadata while still ensuringrole/contentvalues are stringified.Related
Fixes tool-calling conversations via Hermes Web UI when using providers that enforce the OpenAI message schema strictly (DeepSeek, OpenAI, etc.).