Summary
server_chat_convert_anthropic_to_oai (tools/server/server-chat.cpp:327) always emits a user turn's tool_result blocks after that turn's text/content message, regardless of their original position. When an Anthropic user message mixes tool_result and text blocks, this reorders them — separating the tool result from the assistant tool_calls it answers and producing an invalid/incorrect OAI message sequence.
Detail
For each message, the loop (:390-453) sorts blocks into converted_content (text/image), tool_calls, and tool_results. It then emits:
- the main message (
{role, content: converted_content, ...}) at :468, then
- each
tool_result as a separate {role:"tool", ...} message at :471.
So a user turn with content [{type:"tool_result",...}, {type:"text","..."}] (Anthropic requires tool_result blocks first) converts to:
... assistant(tool_calls) , user(text) , tool(result)
but correct OAI ordering is:
... assistant(tool_calls) , tool(result) , user(text)
The tool message must directly follow the assistant tool_calls; interposing the user text breaks role adjacency and feeds the model the follow-up text before the tool result.
The pure case (user turn = only tool_result, the common Claude-Code flow) is unaffected: converted_content is empty so no main message is emitted (:455), and the tool messages land correctly. The bug is specific to mixed content in one user turn.
Repro
Anthropic request with:
{"messages":[
{"role":"user","content":"weather?"},
{"role":"assistant","content":[{"type":"tool_use","id":"t1","name":"w","input":{}}]},
{"role":"user","content":[
{"type":"tool_result","tool_use_id":"t1","content":"sunny"},
{"type":"text","text":"thanks, and tomorrow?"}
]}
]}
→ converts to ... assistant(tool_calls), user("thanks..."), tool("sunny") — tool result after the user text.
Suggested fix
Preserve block order: instead of collecting tool_results into a trailing list, interleave them into oai_messages at their original position (emit any pending tool messages before the text/content message when the tool_result blocks precede text), or split the turn so tool messages are emitted first. A unit test in test_compat_anthropic.py with a mixed tool_result+text user turn would lock it.
Severity
Low / edge — non-crash, and the common single-tool_result flow is fine; only mixed tool_result+text in one user turn is affected. Filing for the record (found via idle bug-hunt, verified against origin/ht 90ae01b7a).
Summary
server_chat_convert_anthropic_to_oai(tools/server/server-chat.cpp:327) always emits a user turn'stool_resultblocks after that turn's text/content message, regardless of their original position. When an Anthropicusermessage mixestool_resultandtextblocks, this reorders them — separating the tool result from the assistanttool_callsit answers and producing an invalid/incorrect OAI message sequence.Detail
For each message, the loop (
:390-453) sorts blocks intoconverted_content(text/image),tool_calls, andtool_results. It then emits:{role, content: converted_content, ...}) at:468, thentool_resultas a separate{role:"tool", ...}message at:471.So a user turn with content
[{type:"tool_result",...}, {type:"text","..."}](Anthropic requires tool_result blocks first) converts to:but correct OAI ordering is:
The tool message must directly follow the assistant
tool_calls; interposing the user text breaks role adjacency and feeds the model the follow-up text before the tool result.The pure case (user turn = only
tool_result, the common Claude-Code flow) is unaffected:converted_contentis empty so no main message is emitted (:455), and the tool messages land correctly. The bug is specific to mixed content in one user turn.Repro
Anthropic request with:
{"messages":[ {"role":"user","content":"weather?"}, {"role":"assistant","content":[{"type":"tool_use","id":"t1","name":"w","input":{}}]}, {"role":"user","content":[ {"type":"tool_result","tool_use_id":"t1","content":"sunny"}, {"type":"text","text":"thanks, and tomorrow?"} ]} ]}→ converts to
... assistant(tool_calls), user("thanks..."), tool("sunny")— tool result after the user text.Suggested fix
Preserve block order: instead of collecting
tool_resultsinto a trailing list, interleave them intooai_messagesat their original position (emit any pending tool messages before the text/content message when the tool_result blocks precede text), or split the turn so tool messages are emitted first. A unit test intest_compat_anthropic.pywith a mixedtool_result+textuser turn would lock it.Severity
Low / edge — non-crash, and the common single-
tool_resultflow is fine; only mixedtool_result+textin one user turn is affected. Filing for the record (found via idle bug-hunt, verified againstorigin/ht90ae01b7a).