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]