nic-6443 opened a new pull request, #13674:
URL: https://github.com/apache/apisix/pull/13674

   ### Description
   
   `ai-proxy` / `ai-proxy-multi` reject a perfectly legal Anthropic request 
when a `user` message carries `tool_result` blocks alongside anything else. The 
converter emitted the leftover text *before* the `tool` messages:
   
   ```
   assistant(tool_calls) -> user(text) -> tool -> tool -> tool
   ```
   
   OpenAI-compatible providers require every `tool` message to immediately 
follow the assistant message holding the matching `tool_calls`, so they reject 
this with `An assistant message with 'tool_calls' must be followed by tool 
messages responding to each 'tool_call_id'`.
   
   Anthropic explicitly allows `tool_result` and `text` in the same user 
message (the `tool_result` blocks just have to come first), and Claude Code 
produces exactly that shape whenever a system reminder or a queued prompt rides 
along with the tool results. Once such a turn is in the history, every later 
request in that session carries it — so the route starts returning 400 and 
never recovers until the conversation is thrown away.
   
   This emits the `tool` messages first and moves the leftover content into a 
message after them. The same branch also silently dropped `image`/`document` 
blocks sitting next to a `tool_result`; those are now preserved in the trailing 
message.
   
   Three more gaps in the same converter, fixed here as well:
   
   - `tool_use` blocks in the conversation history never went through the 
tool-name sanitizer, so a tool could be declared to the upstream as `my_tool_x` 
while the history kept calling it `my tool!x`. Tool-name mapping now runs 
before message conversion and both paths share it.
   - `thinking: {"type": "adaptive"}` was ignored, so no `reasoning_effort` 
reached the upstream at all. It now resolves through `output_config.effort`, 
clamping `xhigh`/`max` to `high` since Chat Completions has no such level.
   - Structured outputs were read from `output_config.json_schema` / 
`output_format.json_schema`. Neither key exists — the schema lives at 
`output_config.format.schema` (GA) or `output_format.schema` (beta) — so 
`response_format` was never produced. It is now read from the right place and 
wrapped in the named `json_schema` object the OpenAI side expects.
   
   Verified the ordering fix by running the converter over a real 143-message 
Claude Code request body: before the change the converted message list violates 
the tool-message adjacency rule exactly once (at the turn where a system 
reminder rides along with three parallel tool results); after the change there 
are zero violations.
   
   Behavior changes worth calling out: `response_format` is now actually 
emitted for structured-output requests (it never was), and `reasoning_effort` 
is now emitted for adaptive thinking. `output_config` is no longer treated as 
an alias of `output_format`, because the two fields mean different things.
   
   No plugin configuration or user-facing option changed, so there is nothing 
to update under `docs/`.
   
   ### Checklist
   
   - [x] I have explained the need for this PR and the problem it solves
   - [x] I have explained the changes or the new features added to this PR
   - [x] I have added tests corresponding to this change
   - [x] I have updated the documentation to reflect this change
   - [x] I have verified that this change is backward compatible (If not, 
please discuss on the [APISIX mailing 
list](https://github.com/apache/apisix/tree/master#community) first)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to