Skip to content

fix(openai): explicitly send tool_choice=auto when tools are provided#2813

Merged
dgageot merged 2 commits into
docker:mainfrom
dgageot:board/1c3b2d7c714035c0
May 18, 2026
Merged

fix(openai): explicitly send tool_choice=auto when tools are provided#2813
dgageot merged 2 commits into
docker:mainfrom
dgageot:board/1c3b2d7c714035c0

Conversation

@dgageot
Copy link
Copy Markdown
Member

@dgageot dgageot commented May 18, 2026

Summary

Fixes #2804.

The OpenAI spec treats an omitted tool_choice as equivalent to "auto", but strict OpenAI-compatible gateways (notably LiteLLM-based proxies) reject requests with a 400 when the field is absent:

HTTP 400: litellm.BadRequestError: BedrockException -
{"message":"The model returned the following errors: tool_choice.type: Field required"}

This change sends tool_choice: "auto" explicitly in both the Chat Completions and Responses API paths whenever tools is non-empty. The behavior is:

  • Spec-compliant — matches the OpenAI default exactly.
  • Backwards-compatible — no behavior change for gateways that already handle omission correctly.
  • Safe for the agentic loopauto preserves the model's autonomy to decide when to call tools.

When no tools are provided, tool_choice is correctly omitted (sending it with no tools would be invalid).

Changes

  • pkg/model/provider/openai/client.go — set ToolChoice explicitly in both CreateChatCompletionStream and CreateResponseStream when tools are present.
  • pkg/model/provider/openai/tool_choice_test.go — verifies the Chat Completions wire format.
  • pkg/model/provider/openai/tool_choice_responses_test.go — verifies the Responses API wire format.

Tests assert the actual JSON request body (not just SDK types) to confirm the field is on the wire.

Validation

  • go build ./...
  • go vet ./...
  • go test ./pkg/model/provider/openai/...
  • golangci-lint run ./pkg/model/provider/openai/... ✅ (0 issues)

Some strict OpenAI-compatible gateways like LiteLLM reject requests
where tool_choice is missing while tools are present, even though the
OpenAI spec treats omission as equivalent to auto. This fix sends the
default value explicitly to maintain compatibility.

Fixes docker#2804
@dgageot dgageot requested a review from a team as a code owner May 18, 2026 10:04
Copy link
Copy Markdown

@docker-agent docker-agent left a comment

Choose a reason for hiding this comment

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

Assessment: 🟢 APPROVE

The fix correctly addresses the LiteLLM/Bedrock gateway incompatibility by explicitly setting tool_choice: "auto" whenever tools are present — in both the Chat Completions and Responses API paths. The implementation is minimal, spec-compliant, and backwards-compatible.

Summary of analysis:

  • Both code paths (CreateChatCompletionStream and CreateResponseStream) guard the tool_choice field behind len(tools) > 0, which is correct.
  • The SDK union param usage (ChatCompletionToolChoiceOptionUnionParam{OfAuto: param.NewOpt("auto")}) is confirmed correct by the wire-format test that captures and deserializes the actual HTTP request body.
  • The Responses API path uses responses.ToolChoiceOptionsAuto constant — the correct typed approach.
  • Tests cover: tools present (verifies tool_choice is on the wire as bare string "auto"), and tools absent (verifies tool_choice is omitted).
  • No high or medium severity issues found.

rumpl
rumpl previously approved these changes May 18, 2026
Existing VCR cassettes were recorded before tool_choice was sent
explicitly. Normalize the field in the matcher so old cassettes keep
matching, matching the existing pattern used for max_tokens, reasoning,
and thinkingConfig.
@dgageot dgageot merged commit 109da70 into docker:main May 18, 2026
5 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.

tool_choice field not sent in API requests, causing 400 errors with strict OpenAI-compatible gateways

3 participants