Repository navigation
feat(ai, adapters): add chat({ toolChoice }) for every adapter - #1696
Conversation
OpenAI-compatible adapters, Anthropic, Bedrock, Gemini, Grok, Mistral, and OpenRouter send toolChoice on the wire. A tool choice in modelOptions wins. Claude models that reject a forced tool, and Claude with thinking on, get auto. Bedrock Converse sends the tools with auto for none after a tool call.
Claude Code, Codex, OpenCode, Grok Build, and ACP bridge only the chat() tools that toolChoice allows: none for 'none', and only the named tool for a named choice. Claude Code also turns its built-in tools off for those values. A value that the agent cannot follow logs one warning for each adapter instance.
OpenAI runs the tool-choice feature through /api/chat and reads tool_choice from the aimock journal. Anthropic uses /api/tool-choice-wire, which captures the request: claude-sonnet-4-5 gets the named tool, and claude-opus-5-5 gets auto.
Add Choose when the model calls a tool to the tools guide, Change the tool choice of a call to the middleware guide, a Tool choice section to the Claude Code, Codex, OpenCode, Grok Build, ACP-compatible, and Ollama pages, and fix the toolChoice row of the Vercel AI SDK migration.
🦋 Changeset detectedLatest commit: 6878870 The changes in this PR will be included in the next version bump. This PR includes changesets to release 51 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit e332c06
☁️ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-acp
@tanstack/ai-angular
@tanstack/ai-anthropic
@tanstack/ai-bedrock
@tanstack/ai-byteplus
@tanstack/ai-claude-code
@tanstack/ai-client
@tanstack/ai-cloudflare
@tanstack/ai-code-mode
@tanstack/ai-code-mode-snippets
@tanstack/ai-codex
@tanstack/ai-cohere
@tanstack/ai-compaction
@tanstack/ai-devtools-core
@tanstack/ai-durable-stream
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-grok-build
@tanstack/ai-groq
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-daytona
@tanstack/ai-isolate-e2b
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-isolate-quickjs-bun
@tanstack/ai-llmgateway
@tanstack/ai-lovable
@tanstack/ai-mcp
@tanstack/ai-memory
@tanstack/ai-mistral
@tanstack/ai-octane
@tanstack/ai-ollama
@tanstack/ai-ollaya
@tanstack/ai-openai
@tanstack/ai-opencode
@tanstack/ai-openrouter
@tanstack/ai-perplexity
@tanstack/ai-persistence
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-reactor
@tanstack/ai-remix
@tanstack/ai-sandbox
@tanstack/ai-sandbox-blaxel
@tanstack/ai-sandbox-boxd
@tanstack/ai-sandbox-cloudflare
@tanstack/ai-sandbox-daytona
@tanstack/ai-sandbox-docker
@tanstack/ai-sandbox-e2b
@tanstack/ai-sandbox-local-process
@tanstack/ai-sandbox-railway
@tanstack/ai-sandbox-sprites
@tanstack/ai-sandbox-upstash-box
@tanstack/ai-sandbox-vercel
@tanstack/ai-skills
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-typesafe
@tanstack/ai-utils
@tanstack/ai-vercel-gateway
@tanstack/ai-vertex
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/ai-worldlabs
@tanstack/openai-base
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
@tanstack/svelte-ai-devtools
commit: |
Coverage✅ Coverage held across 66 compared package(s). Each package is measured twice in this job — on this PR and on its merge-base with
|
ToolChoice takes an optional name type. In chat() options, { type: 'tool', name } offers the names of the tools array and still accepts any string, for provider tools and lazy tools.
You can now tell the model when to call a tool, with one option for every adapter:
chat({ toolChoice }). It takes'auto','none','required', or{ type: 'tool', name }. Before, you set a provider-specifictool_choiceinsidemodelOptions.Stacked on #1692 (reasoning). The Claude rule "thinking on →
auto" reads the thinking fields thatchat({ reasoning })sets. Merge #1692 first. GitHub then moves this PR tomain.This PR is split out of #1555.
🎯 Changes
chat({ toolChoice }), and a per-calltoolChoicefrom middlewareonConfig, which applies to the next model call only. A value inmodelOptionsstill wins. Nothing is sent when the request has no tools.openai-baseadapters, Anthropic, Bedrock, Gemini, Grok, Mistral, and OpenRouter send their owntool_choicefield.auto. Each model's "cannot force" flag issupports.forced_tool_choice: falsein its object inmodel-meta.ts. Bedrock keeps a list of family names, because its generated catalog has no Claude 5.x entries.none, so after tool calls it sends the tools withauto.toolChoice.subagenttool, the harness step limit, prompt caching, and replay.tools/tools.md.advanced/middleware.md.ollama.md.toolChoice.tool-choice-core(minor for@tanstack/aiandopenai-base),tool-choice-adapters, andtool-choice-cli-adapters(patch).✅ Checklist
pnpm run test:pr, or these tests do not apply to this pull request.docs/for this change, or this change is not user-facing.pnpm changeset), or this PR does not change a published package.🚀 Release Impact
Testing
Commands run
vitest run(--maxWorkers=2),test:types, andtest:oxlintin all 20 changed packages. All pass. That includes the adapters onopenai-basethat only got tests.pnpm test:docs: no broken links.kiira checkon the 9 changed pages: 127 snippets pass.tscontesting/e2e: no errors in the changed files.pnpm test:prand the E2E suite. CI runs them.Manual test
pnpm --dir packages/ai exec vitest run tests/tool-choice.test.ts.{ type: 'tool', name }is sent astool_choice.autoinstead.autofor'none'after tool calls.How this PR makes testing easy
tool-choice.spec.ts, with routeapi.tool-choice-wire.tsand fixturefixtures/tool-choice/basic.json. It checks thattoolChoicereaches the wire.Risk / rollback
Low. This is a new optional option, and with no
toolChoiceset the requests do not change. To undo, revert this PR.Public API change
Before
After