fix: serialize dict tool content to string before LLM API call - #31963
fix: serialize dict tool content to string before LLM API call#31963Joyatulya wants to merge 1 commit into
Conversation
Some tool handlers return structured dicts (e.g. plugin errors) in the tool content field instead of a plain string or content list. Strict providers like DeepSeek v4 Flash reject these with HTTP 400. Add the same safety guard to both the concurrent and sequential tool execution paths in tool_executor.py: any non-string, non-list content from a tool result is serialized via json.dumps() before being built into the API message. This keeps conversations recoverable regardless of which provider is in use. Fixes NousResearch#17381
|
Heads-up on an overlap (disclosure: I authored a related PR, so flagging neutrally for the maintainer to choose): this targets the same bug as #31770 — non-string tool The approaches differ in placement, and it's worth noting they're redundant:
Both are legitimate; just noting whichever lands, the other's coerce becomes a no-op. One thing specific to this diff worth a maintainer's eye regardless of the dedup question: it also drops |
|
Thanks for identifying the strict-provider failure mode. This automated hermes-sweeper review found that current
Closing as implemented on main; thank you for the focused report and proposed guard. |
Bug Description
When tool handlers return structured dicts (e.g. plugin errors with
{"error": "...", "code": ...}) in thecontentfield instead of a plain string or OpenAI-style content list, strict providers like DeepSeek v4 Flash reject the request with HTTP 400.This causes the entire conversation to error out — the agent loses a full turn of progress.
Fixes #17381
Root Cause
The
tool_executor.pyparallel and sequential paths appended tool content directly to the API message without type-checking. OpenAI-compatible endpoints expectcontentto be either astringor alistof content parts. Dicts are not valid in either schema.Fix
Added a safety guard in both execution paths (concurrent at line 451, sequential at line 873):
This serializes any non-string, non-list content to JSON before building the API message. The conversation stays recoverable regardless of which provider is in use.
How to Verify
Test Plan
nititool (niti action list,niti action create,niti action cancelall work without crashes)Risk Assessment
Low — The change only wraps non-conforming content types. Strings and lists (the vast majority of tool results) pass through unchanged.