Skip to content

fix(proxy): send a forced tool_choice in the Responses shape - #212

Merged
GoDiao merged 2 commits into
GoDiao:mainfrom
pcbeingused333:fix/responses-tool-choice-shape
Oct 30, 2026
Merged

GoDiao merged 2 commits into
GoDiao:mainfrom
pcbeingused333:fix/responses-tool-choice-shape

Conversation

@pcbeingused333

Copy link
Copy Markdown
Contributor

When a client sends an Anthropic request with a forced tool (tool_choice: { type: 'tool', name }) through a provider that uses the OpenAI Responses API, anthropicToOpenaiResponses converted it with the Chat Completions form, { type: 'function', function: { name } }. The Responses API takes the flat form { type: 'function', name }, so the upstream rejected the request with a 400.

The Responses converter now returns { type: 'function', name }; any, auto and none keep mapping to required, auto and none, and the Chat Completions converter is unchanged (its nested form is the correct one there).

The flat shape matches what this repo already assumes elsewhere for the same API: OpenAIResponsesRequest.tools in transform/types.ts carries { type: 'function', name, description, parameters }, with the name at the top level, while OpenAITool for Chat Completions keeps it under function.

Tests in proxy-transform.test.ts: the forced-tool shape (fails on the previous code) and the three string mappings. bun test src/server/__tests__/proxy-transform.test.ts: 42 pass. Not run against a live Responses upstream. CHANGELOG line under Unreleased.

This change was made with Claude Code.

pcbeingused333 and others added 2 commits October 29, 2026 08:24
anthropicToOpenaiResponses reused the Chat Completions form
{type:'function', function:{name}} for tool_choice type=tool. The Responses API
takes the flat {type:'function', name}, so a forced tool from an Anthropic client
was rejected with a 400.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@GoDiao GoDiao left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pcbeingused333, nice find! We checked it against OpenRouter's Responses API with one request that differs only in tool_choice: the nested { type: 'function', function: { name } } that main sent gets a 400 (invalid_prompt), and the flat { type: 'function', name } from this PR succeeds and the model calls the tool.

We pushed a merge of main to resolve the CHANGELOG conflict. Your entry had landed in the 0.6.1 section, so we moved it under [Unreleased] and added a thanks line. Approving and merging. Thanks again!

@GoDiao
GoDiao merged commit 90f4ce5 into GoDiao:main Oct 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants