review: post-v0.2.1 double check — tool_choice across wires, stop_reason on tool_use, docs - #94
Merged
Merged
Conversation
listen.max_request_bytes (PR 91) belongs next to max_reply_bytes and the x-gw-user cap in the security failure postures.
An Anthropic-shaped tool_choice ({type: auto|any|none|tool}) reached the
OpenAI chat wire verbatim and the vendor answered 400 "Missing required
parameter: 'tool_choice.function'"; a chat-shaped {type: function,
function: {name}} reached the Responses wire the same way ("Missing
required parameter: 'tool_choice.name'"). The reverse direction already
normalized (normalize_tool_choice_anthropic). Seen live on gpt-4o-mini
and gpt-4.1-mini through /v1/messages and /v1/chat/completions.
OpenAI reports finish_reason "stop" when tool_choice forces one function,
so the messages surface said end_turn next to a tool_use block and an
Anthropic client would not run the tool. end_turn becomes tool_use when
the content carries a tool_use block, on the buffered and the streamed
path; max_tokens and the other reasons stay. Seen live on gpt-4o-mini
with tool_choice {type: tool}.
normalize_tool_choice_openai took the request body to write parallel_tool_calls, which forced a two-step insert at both call sites and a scratch map in every test. The policy now comes from parallel_tool_calls(&choice) and the caller inserts it; the normalizer converts the shape and nothing else. Same wire output.
An OpenAI client's parallel_tool_calls rode the raw extras onto the Messages body and Anthropic answered 400 "parallel_tool_calls: Extra inputs are not permitted"; on Converse it landed in additionalModelRequestFields. It now becomes tool_choice.disable_parallel_tool_use (tool_choice defaults to auto, type none is left alone), the mirror of the OpenAI-wire direction. Seen live on claude-haiku-4-5 through /v1/chat/completions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Static double check of everything merged since PR 90 / v0.2.1 (PRs 91, 92, 93), plus the live checks the delta called for. Eleven commits: five fixes, two feature commits, three docs, one rustfmt.
engines: an Anthropic-shapedtool_choice({type: auto|any|none|tool}) reached the OpenAI chat wire verbatim and the vendor answered 400 "Missing required parameter: 'tool_choice.function'"; a chat-shaped{type: function, function: {name}}reached the Responses wire the same way ("Missing required parameter: 'tool_choice.name'"). Both convert now; the reverse direction (normalize_tool_choice_anthropic) already did.engines: the parallel tool policy travels with the choice in both directions —disable_parallel_tool_usebecomesparallel_tool_callson the OpenAI chat and Responses wires, and an OpenAI client'sparallel_tool_callsbecomestool_choice.disable_parallel_tool_useon the Messages wires (anthropic-messages,aws-anthropic) instead of riding the raw extras into a 400 "parallel_tool_calls: Extra inputs are not permitted". Converse has no such flag intoolConfig, so there the field stays inadditionalModelRequestFieldsfor the model to accept or reject: the OpenAI family accepts it, Anthropic models answer 400. The normalizer stays a pure shape conversion; the policy comes fromparallel_tool_calls(&choice).views:/v1/messagessaidend_turnnext to atool_useblock when OpenAI reportedstopfor a forced tool, so an Anthropic client would not run the tool.end_turnbecomestool_usewhen the content carries atool_useblock, on the buffered and the streamed path;max_tokensand the other reasons stay.tool_choice/parallel-policy conversion across wires and the derivedstop_reasonare documented in the API page.Static review (delta c2786c6..a66250a)
Every changed production file read in full:
config,protocol/openai,engines/openai_engine,engines/families,views. No finding in the delta itself. PR 92'stool_callsloosening (typed struct →Vec<Value>) was checked against every unchanged consumer of client-verbatim tool calls — token estimate, the two content walks,tool_calls_to_tool_use, the claude and openai engines — all match on the variant or filter before indexing, none can panic. Comment budget on the delta: one field doc, one doc rewritten, zero inline comments. This PR adds four one-line///docs and no inline comments.Live evidence (Linux testbed, real Gemini, OpenAI, Anthropic and Bedrock, 22/22 on this head's production code, gateway logs 0 WARN/ERROR)
extra_content.google.thought_signature; echoing the turn verbatim is accepted; stripping the field is refused by Google (400)index0 and 1, arguments redacted;/v1/messagesrenders onetool_useblock per callmax_request_bytes: 4194304: 5 MiB is 413, 3 MiB passes/v1/messagestool_choice{type: auto}/{type: any}/{type: tool}over gpt-4o-mini → 200 withstop_reason: tool_use, buffered and streamed; chat{type: function, function: {name}}over gpt-4.1-mini on the Responses wire → 200 with one callparallel_tool_calls: falseover claude-haiku-4-5 → 200 with a tool call;/v1/messagestool_choicewithdisable_parallel_tool_useover gpt-4o-mini → 200 with atool_useblockparallel_tool_calls: false: aws-anthropic InvokeModel haiku-4-5 → 200 (converted); Converse gpt-oss-20b → 200 (native field); Converse haiku-4-5 → 424 carrying the vendor's 400 "parallel_tool_calls: Extra inputs are not permitted" (the documented reject case; the same request without the field → 200)Before the fixes the five
tool_choicecases answered 424 carrying the vendor 400s quoted above, the forced-tool reply saidend_turn, and the Anthropicparallel_tool_callscase answered 424 with the vendor's "Extra inputs are not permitted".Hot path
tool_choicenormalization and the policy lookup run once per request and only when the field is present (one match; one small map for thetoolcase). Thestop_reasonderivation is one pass over the reply's content blocks on/v1/messages. The chat path is unchanged.Gates
cargo fmt --checkclean;cargo clippy --workspace --all-targets -- -D warningsclean;cargo test --workspace: 32 suites, 630 passed, 0 failed (engines re-run after the last two commits: 214 passed); Linux release build for the live run.