feat: Provider tools on /tools subpath with per-model type gating#466
feat: Provider tools on /tools subpath with per-model type gating#466AlemTuzlak wants to merge 44 commits intomainfrom
Conversation
This reverts commit 801255b.
Separates tool-type entries (code_execution, file_search, search_grounding, grounding_with_gmaps, url_context, image_generation) from general capability flags in ModelMeta.supports. Adds a new tools field to the supports shape, renames grounding_with_gmaps → google_maps and search_grounding → google_search, drops image_generation (no tool factory yet), and introduces GeminiChatModelToolCapabilitiesByName. Threads a fifth TToolCapabilities generic through GeminiTextAdapter and exports the new type map from the root.
…hTool on /tools subpath Renames createWebSearchTool → webSearchTool, brands its return type as OpenRouterWebSearchTool (ProviderTool<'openrouter', 'web_search'>), moves web_search exports to the new ./tools subpath, adds OpenRouterChatModelToolCapabilitiesByName mapped type (all chat models support web_search via the gateway), threads TToolCapabilities through the text adapter, and adds per-model type tests.
…d toolCapabilities - Add tools?: ReadonlyArray<never> to ModelMeta.supports interface in both packages - Add tools: [] as const to every chat model constant - Export GrokChatModelToolCapabilitiesByName / GroqChatModelToolCapabilitiesByName type maps - Add 5th TToolCapabilities generic to GrokTextAdapter / GroqTextAdapter via ResolveToolCapabilities - Add ./tools subpath to package.json exports and vite.config.ts entry for both packages - Re-export new ToolCapabilitiesByName types from root index.ts in both packages
…active models Investigated file_search and search_grounding entries in model-meta.ts: - All active (exported) models: correctly have these entries in tools: array - Commented-out preview models (GEMINI_2_5_FLASH_LIVE, GEMINI_2_FLASH_LIVE): still have them in capabilities: but are not part of the released API - Changeset claim is accurate: relocation is complete for all models that matter
📝 WalkthroughWalkthroughThis PR introduces a provider-native tool system across TanStack AI adapters. It adds Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Adapter as TextAdapter<br/>(with TToolCapabilities)
participant ProviderTool as Provider Tool Factory<br/>(e.g., webSearchTool)
participant TypeChecker as TypeScript Compiler
User->>ProviderTool: Call webSearchTool(config)
ProviderTool->>ProviderTool: Return AnthropicWebSearchTool<br/>(ProviderTool<'anthropic','web_search'>)
User->>Adapter: adapter.chat({tools:[webSearchTool(...)]})
Adapter->>TypeChecker: Check tool compatibility against<br/>TAdapter['~types']['toolCapabilities']
alt Tool kind in supportedCapabilities
TypeChecker->>Adapter: ✓ Accept tool
Adapter->>User: Execute chat with tool
else Tool kind not supported
TypeChecker->>User: ✗ Compile error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
🚀 Changeset Version Preview8 package(s) bumped directly, 25 bumped as dependents. 🟥 Major bumps
🟨 Minor bumps
🟩 Patch bumps
|
|
View your CI Pipeline Execution ↗ for commit 0c1bd22
☁️ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-anthropic
@tanstack/ai-client
@tanstack/ai-code-mode
@tanstack/ai-code-mode-skills
@tanstack/ai-devtools-core
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-groq
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-openrouter
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/typescript/ai/tests/type-check.test.ts (1)
11-22:⚠️ Potential issue | 🟡 MinorPass explicit
toolCapabilitiestype parameter toMockAdapter.The
TextAdapterinterface has 5 type parameters, but theMockAdapterinstantiation (lines 11–22) provides only 4 arguments. TheTToolCapabilitiesparameter relies on its default value (ReadonlyArray<string>) rather than explicitly matching thetoolCapabilities: [] as ReadonlyArray<string>type from the mock literal at line 38. Pass the 5th type argument to make the type instantiation explicit and align with the mock object definition:type MockAdapter = TextAdapter< 'test-model', { validOption: string; anotherOption?: number }, readonly ['text', 'image'], { text: unknown image: unknown audio: unknown video: unknown document: unknown }, readonly string[] >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai/tests/type-check.test.ts` around lines 11 - 22, The MockAdapter type instantiation omits the TToolCapabilities type parameter, causing it to fall back to the default instead of matching the mock's toolCapabilities literal; update the MockAdapter alias (the TextAdapter instantiation) to include the fifth type argument (e.g., readonly string[]) so the generic list of tool capabilities is explicit and aligns with the mock object’s toolCapabilities value.docs/migration/migration.md (1)
13-19:⚠️ Potential issue | 🟡 MinorOverview list is out of date — "Provider Tools" isn’t mentioned.
Section 6 was added below but the numbered list at the top still stops at item 5 (Embeddings Removed). Users skimming the overview will miss the OpenRouter breaking rename.
Proposed fix
1. **Adapter functions split** - Adapters are now split into activity-specific functions for optimal tree-shaking 2. **Common options flattened** - Options are now flattened in the config instead of nested 3. **`providerOptions` renamed** - Now called `modelOptions` for clarity 4. **`toResponseStream` renamed** - Now called `toServerSentEventsStream` for clarity 5. **Embeddings removed** - Embeddings support has been removed (most vector DB services have built-in support) +6. **Provider tools moved to `/tools` subpath** - Provider-specific tool factories are now imported from each adapter's `/tools` subpath; `@tanstack/ai-openrouter`'s `createWebSearchTool` is renamed to `webSearchTool`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/migration/migration.md` around lines 13 - 19, The top overview numbered list is missing the new Section 6 about Provider Tools/OpenRouter; update the list to include a sixth bullet describing "Provider Tools added — OpenRouter rename and related breaking changes" (or similar), and ensure the summary mentions the existing renames: `providerOptions` → `modelOptions` and `toResponseStream` → `toServerSentEventsStream` and removal of Embeddings so readers see the OpenRouter/provider-tools note at a glance; edit the list entries (the overview block containing "Adapter functions split", "Common options flattened", etc.) to add the sixth item and make sure it references the new Provider Tools/OpenRouter change so the overview and Section 6 are consistent.scripts/sync-provider-models.ts (1)
102-124:⚠️ Potential issue | 🟠 MajorTemplate-level tool capability drift for Anthropic/Gemini newly synced models.
Both the Anthropic (line 104) and Gemini (line 124) reference templates hardcode the full tool superset for every newly synced model. The real matrix already has restricted Anthropic families (
claude-3-haiku→['web_search'],claude-3-5-haiku→['web_search', 'web_fetch']) and narrower Gemini variants (Lite, image/video), perdocs/tools/provider-tools.mdandmodel-meta.ts. Any future haiku-class or Lite-class model picked up by this script will be minted with an over-broadsupports.tools, which feedsAnthropicChatModelToolCapabilitiesByName/ Gemini's capability map and silently permits unsupported tools at compile time — defeating the type gate this PR is introducing.This matches the "capability map drift" follow-up called out in the PR description, but it's worth leaving a defensive marker so the next sync run doesn't quietly regress type safety. A minimum mitigation is to emit
tools: [](or a conservative['web_search']) in the template and require manual review, rather than the full superset.🛡️ Suggested conservative defaults
referenceSupportsBody: ` extended_thinking: true, priority_tier: true, - tools: ['web_search', 'web_fetch', 'code_execution', 'computer_use', 'bash', 'text_editor', 'memory'],`, + // NOTE: conservative default — widen manually after verifying model capabilities. + tools: [],`,referenceSupportsBody: ` output: ['text'], capabilities: ['batch_api', 'caching', 'function_calling', 'structured_output', 'thinking'], - tools: ['code_execution', 'file_search', 'google_search', 'url_context'],`, + // NOTE: conservative default — widen manually after verifying model capabilities. + tools: [],`,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sync-provider-models.ts` around lines 102 - 124, The templates for new model entries (the referenceSupportsBody string used in the 'anthropic/' and 'google/' blocks) currently hardcode the full tools superset, which will incorrectly grant unsupported tools to newly synced models; change those template snippets to emit a conservative default tools list (e.g., tools: [] or tools: ['web_search']) instead of the full superset, add a short comment in the template reminding reviewers to manually map per-model tool capabilities, and ensure any existing per-model capability overrides (the canonical capability map) remain authoritative so the sync doesn't auto-promote unsupported tools.packages/typescript/ai-gemini/src/model-meta.ts (1)
289-313:⚠️ Potential issue | 🟠 MajorRemove
file_searchfrom the TTS model tool declarations.
GEMINI_2_5_PRO_TTSandGEMINI_2_5_FLASH_TTSincorrectly declaretools: ['file_search']. Official Gemini documentation explicitly states that TTS models do not support the file_search tool or any general tool use capabilities—they are specialized for audio generation from text inputs only. This appears to be a stale entry carried over from an earlier configuration.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-gemini/src/model-meta.ts` around lines 289 - 313, Remove the unsupported file_search tool from the TTS model metadata objects: update GEMINI_2_5_PRO_TTS and GEMINI_2_5_FLASH_TTS by deleting 'file_search' from their supports.tools arrays so their supports only reflect text->audio capability; ensure the objects still satisfy the ModelMeta type (GeminiToolConfigOptions & GeminiSafetyOptions & GeminiCommonConfigOptions & GeminiCachedContentOptions) after the removal and run type checks to confirm no other tool entries remain for TTS models.
🧹 Nitpick comments (20)
packages/typescript/ai-openrouter/src/tools/web-search-tool.ts (1)
40-51: Emptydescriptionmay be rejected by stricter consumers/tool registries.
description: ''is returned unconditionally. Some tool-pipeline paths / validators treat the description as required and non-empty (and LLM-facing tool schemas generally benefit from a meaningful description). Since this is a provider-native tool executed by OpenRouter (not the model), an empty string is likely fine on the wire, but consider a short static description (e.g.,'Search the web via OpenRouter.') for clarity and to avoid future validation surprises.Proposed tweak
- name: 'web_search', - description: '', + name: 'web_search', + description: 'Search the web via OpenRouter.',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts` around lines 40 - 51, The returned tool object for web_search uses an empty description which can break stricter validators; update the factory that returns the object (the one creating name: 'web_search' and casting to OpenRouterWebSearchTool) to provide a concise static description like "Search the web via OpenRouter." instead of '', ensuring the description field on the returned object is non-empty and stays compatible with the existing metadata and the OpenRouterWebSearchTool cast.packages/typescript/ai-openai/src/tools/computer-use-tool.ts (1)
29-39: Metadata shape mismatch between factory and converter.
computerUseToolstoresmetadata = { ...toolData }, which includestoolData.type('computer_use_preview'perOpenAI.Responses.ComputerTool).convertComputerUseToolToAdapterFormatthen caststool.metadata as ComputerUseToolConfigand readsdisplay_height/display_width/environment— this works, but it means the stored metadata isComputerUseToolConfigitself (type-tagged), not a plain options object. That’s inconsistent with e.g.webSearchToolinai-openrouterwhich stores a nested config. Not a bug, but worth making the pattern consistent across providers for future refactors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-openai/src/tools/computer-use-tool.ts` around lines 29 - 39, The factory computerUseTool currently spreads toolData directly into metadata causing metadata to be the full ComputerUseToolConfig (including its type tag); change metadata to wrap the options in a nested key (e.g., metadata: { config: toolData } or metadata: { computerUseConfig: toolData }) so metadata is a plain container like other providers, and update convertComputerUseToolToAdapterFormat to read the nested config key instead of casting metadata directly; reference computerUseTool, convertComputerUseToolToAdapterFormat, ComputerUseToolConfig and OpenAIComputerUseTool when making the change.packages/typescript/ai-anthropic/tsconfig.json (1)
6-10: Consider broadeningincludeto the wholetests/directory rather than a single file.Hard-coding
tests/tools-per-model-type-safety.test.tsmeans any future test file undertests/won't be type-checked bytscand must be added here manually. Prefer"tests"(or"tests/**/*.ts") so new type-level tests are picked up automatically, and rely onexclude/Vitest's own include patterns for runtime filtering.Proposed change
"include": [ "vite.config.ts", "./src", - "tests/tools-per-model-type-safety.test.ts" + "tests" ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-anthropic/tsconfig.json` around lines 6 - 10, The tsconfig currently only includes the single file "tests/tools-per-model-type-safety.test.ts", which will miss any new tests; update the "include" array in tsconfig.json (the entry used to list "vite.config.ts", "./src", and the single test file) to cover the whole tests directory (e.g., "tests" or "tests/**/*.ts") so all new type-level tests are picked up automatically while runtime filtering remains handled by Vitest.packages/typescript/ai-gemini/tsconfig.json (1)
6-10: Same note as the Anthropic tsconfig: prefer including thetests/directory.Listing a single test file by name is fragile and will silently skip type-checking of any future tests added under
tests/.Proposed change
"include": [ "vite.config.ts", "./src", - "tests/tools-per-model-type-safety.test.ts" + "tests" ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-gemini/tsconfig.json` around lines 6 - 10, The tsconfig.json "include" array currently references a single test file ("tests/tools-per-model-type-safety.test.ts"), which is fragile; update the "include" array (the include property in tsconfig.json) to include the entire tests directory (e.g., "tests" or "./tests") instead of the single filename so that all current and future tests under tests/ are type-checked; keep the existing entries ("vite.config.ts" and "./src") and replace or add the tests directory entry accordingly.docs/adapters/grok.md (1)
234-241: Minor: verify internal doc link style.Other docs in this repo typically reference pages by slug (no
.mdsuffix), e.g., the existing[Tools Guide](../tools/tools)on line 231 of this same file. The new links use../tools/tools.mdand../tools/provider-tools.md, which is inconsistent and may break under the docs site router. Please confirm withpnpm test:docs.✏️ Suggested alignment
-See [Tools](../tools/tools.md) for the general tool-definition flow, or -[Provider Tools](../tools/provider-tools.md) for other providers' -native-tool offerings. +See [Tools](../tools/tools) for the general tool-definition flow, or +[Provider Tools](../tools/provider-tools) for other providers' +native-tool offerings.As per coding guidelines: "Verify documentation links are valid via pnpm test:docs command".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/adapters/grok.md` around lines 234 - 241, Update the two internal links in the "Provider Tools" section to use slug-style references (remove the .md suffix) so they match the repo convention: change "../tools/tools.md" to "../tools/tools" and "../tools/provider-tools.md" to "../tools/provider-tools"; then run the docs validation command (pnpm test:docs) and fix any remaining link warnings/errors reported for the "[Tools]" and "[Provider Tools]" references.packages/typescript/ai-openai/tsconfig.json (1)
6-10: Consider broadening theincludeto cover all test files.Pinning a single test path means future type-safety tests added under
tests/won't be picked up bytsc(test:types). Consider a glob liketests/**/*.test.tsso compile-time assertions added later are automatically enforced.♻️ Proposed change
"include": [ "vite.config.ts", "./src", - "tests/tools-per-model-type-safety.test.ts" + "tests/**/*.test.ts" ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-openai/tsconfig.json` around lines 6 - 10, The tsconfig's "include" currently pins only a single test file ("tests/tools-per-model-type-safety.test.ts"), which will miss future tests; update the "include" array entry for tests to a glob such as "tests/**/*.test.ts" (or "tests/**/*") so all test files under tests/ are picked up by tsc for the test:types run—locate the "include" array in tsconfig.json and replace or add the broader glob entry.packages/typescript/ai-anthropic/tests/provider-tools-smoke.test.ts (1)
88-117: Consider asserting ontypetoo when converting multiple tools.The multi-tool conversion only checks
names, so a regression whereconvertFunctionToolToAdapterFormatis mistakenly picked for a branded provider tool (e.g., due to aname-based dispatch bug) would still pass. Addingexpect(converted).toContainEqual(expect.objectContaining({ type: 'web_search_20250305' }))(and equivalents) would close that gap and is especially relevant given name-based dispatch patterns exist elsewhere in this PR.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-anthropic/tests/provider-tools-smoke.test.ts` around lines 88 - 117, The test "converts multiple provider tools in one call" only asserts names, so add assertions that the returned converted array contains objects with the expected types to guard against name-based dispatch regressions; after calling convertToolsToProviderFormat (variable converted) include assertions like expect(converted).toContainEqual(expect.objectContaining({ type: 'web_search_20250305' })) and equivalents for 'code_execution_20250825' and 'bash_20250124' to ensure convertToolsToProviderFormat actually produced provider-shaped tool objects rather than e.g. convertFunctionToolToAdapterFormat outputs.packages/typescript/ai-groq/src/model-meta.ts (1)
367-386: Consider deriving the capabilities map fromGROQ_CHAT_MODELS.Since Groq exposes no provider tools, every entry resolves to
readonly []. A mapped type would avoid the risk of adding a new model toGROQ_CHAT_MODELSand forgetting to extend this map (which would silently fall back toneverin consumers). For example:export type GroqChatModelToolCapabilitiesByName = { [K in GroqChatModels]: readonly [] }Keeping the explicit listing is fine for consistency with other providers that do have per-model tool support; flagging as optional.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-groq/src/model-meta.ts` around lines 367 - 386, The explicit per-model mapping in GroqChatModelToolCapabilitiesByName is brittle (you must update it whenever GROQ_CHAT_MODELS changes); replace it with a derived mapped type from the Groq model union/collection (e.g., use GroqChatModels or GROQ_CHAT_MODELS as the key type) that maps each model name to readonly [] so all current and future Groq models automatically get the correct tool-capabilities type; update the GroqChatModelToolCapabilitiesByName definition to use that mapped type instead of enumerating LLAMA_3_1_8B_INSTANT, LLAMA_3_3_70B_VERSATILE, etc..changeset/provider-tools-ai-anthropic.md (1)
5-5: Nit: clarify "signatures unchanged" vs. branded return types.Return types now widen to branded subtypes (e.g.
AnthropicWebSearchTool extends Tool). Consumers who annotated variables asToolor previously-exported config types should still assign, but anyone who captured the prior return type by name may see differences. Consider noting "runtime behavior is unchanged; factory return types are now branded subtypes ofTool" for precision.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.changeset/provider-tools-ai-anthropic.md at line 5, Update the changelog sentence to explicitly state that runtime behavior and factory signatures remain unchanged but the declared return types are now branded subtypes of Tool (for example: AnthropicWebSearchTool extends Tool) and that consumers who typed variables as Tool or used the `@deprecated` config aliases are unaffected while callers that depended on the old named return types may observe a different compile-time type; reference the factories (webSearchTool, codeExecutionTool, computerUseTool, bashTool, textEditorTool, webFetchTool, memoryTool, customTool) and the branded examples (AnthropicWebSearchTool) in the wording for clarity.packages/typescript/ai-grok/src/model-meta.ts (1)
321-333: Consider a mapped type forGrokChatModelToolCapabilitiesByName.Listing every model by hand works, but drifts easily when models are added/removed. A mapped type keyed on the already-maintained const tuple keeps it in sync automatically (mirroring the pattern in
GrokChatModelProviderOptionsByName):♻️ Proposed refactor
-export type GrokChatModelToolCapabilitiesByName = { - [GROK_4_1_FAST_REASONING.name]: typeof GROK_4_1_FAST_REASONING.supports.tools - [GROK_4_1_FAST_NON_REASONING.name]: typeof GROK_4_1_FAST_NON_REASONING.supports.tools - ... -} +export type GrokChatModelToolCapabilitiesByName = { + [K in (typeof GROK_CHAT_MODELS)[number]]: readonly [] +}Since every Grok chat model currently has
tools: [] as const, collapsing toreadonly []is equivalent. If per-model divergence is anticipated, keep the per-model form but at minimum add a const-tuple exhaustiveness check.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-grok/src/model-meta.ts` around lines 321 - 333, The explicit GrokChatModelToolCapabilitiesByName object should be rewritten as a mapped type keyed from the shared models tuple to avoid drift: replace the manual keys in GrokChatModelToolCapabilitiesByName with a mapped type like Record<(typeof GROK_CHAT_MODELS)[number]['name'], (typeof GROK_CHAT_MODELS)[number]['supports']['tools']>; mirror the pattern used by GrokChatModelProviderOptionsByName, and ensure the shared tuple (e.g. GROK_CHAT_MODELS) is declared as const so the mapping is exhaustive and stays in sync when models are added/removed.packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts (1)
25-27: Env is set after module-load; verify no adapter is constructed at import time.
beforeAllruns after top-level module evaluation. Adapter construction here happens inside eachit, so this is fine — butuserTooland any future top-level adapter factory would execute before this hook. IfopenRouterText(...)is ever moved out of anitbody, this will throw. Consider setting the env synchronously at the top of the file instead of inbeforeAllto make the guarantee explicit and future-proof.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts` around lines 25 - 27, Set the OPENROUTER_API_KEY synchronously at module-evaluation time instead of in beforeAll so adapters aren't accidentally constructed before the env exists: move the assignment process.env['OPENROUTER_API_KEY'] = 'sk-or-test-dummy' to the top-level of the test file (above any imports or top-level declarations) and ensure no call to openRouterText or any adapter factory like userTool runs at import time; this guarantees adapters created during import will see the env and prevents future breakage if openRouterText is moved out of an it block.packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts (1)
44-44: Test title understates coverage.The
ittitle lists only the positive tools, but the body also asserts rejection ofcomputerUseTool,googleMapsTool, andgoogleSearchRetrievalTool. Consider renaming to reflect both halves, e.g."gemini-3.1-pro-preview: accepts {code_execution, file_search, google_search, url_context}; rejects {computer_use, google_maps, google_search_retrieval}", or split into twoitblocks.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts` at line 44, Update the test title for the `it` block whose current description is "gemini-3.1-pro-preview accepts code_execution, file_search, google_search, url_context" to accurately reflect that the test both accepts and rejects tools (e.g., "gemini-3.1-pro-preview: accepts {code_execution, file_search, google_search, url_context}; rejects {computer_use, google_maps, google_search_retrieval}") or split the body into two `it` blocks (one asserting accepted tools and one asserting rejected tools) so the test name matches its assertions; ensure you update any assertion messages/labels within that `it` block to remain consistent with the new titles.packages/typescript/ai/tests/tools-per-model-type-safety.test.ts (1)
53-59: ReplacetoMatchTypeOfwithtoExtendfor type expectations.Vitest's
expectTypeOf().toMatchTypeOf()is deprecated in favor of.toExtend(). Update lines 54 and 58 to use.toExtend()instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts` around lines 53 - 59, Replace deprecated expectTypeOf(...).toMatchTypeOf() calls with expectTypeOf(...).toExtend() for the two assertions: change the assertion on userTool to use expectTypeOf(userTool).toExtend<MockToolsOption>() and change the assertion on supportedProviderTool to use expectTypeOf(supportedProviderTool).toExtend<MockToolsOption>(); update the two occurrences referencing userTool, supportedProviderTool, and MockToolsOption accordingly.packages/typescript/ai-anthropic/src/adapters/text.ts (1)
794-821: PassResolveToolCapabilities<TModel>explicitly for symmetry.Both factory return types enumerate
ResolveProviderOptions<TModel>andResolveInputModalities<TModel>but omit the 4th argument. It currently resolves correctly via the class-level default, but explicit symmetry makes the intent obvious and is resilient if the default is ever reordered/removed.♻️ Optional consistency fix
): AnthropicTextAdapter< TModel, ResolveProviderOptions<TModel>, - ResolveInputModalities<TModel> + ResolveInputModalities<TModel>, + ResolveToolCapabilities<TModel> > {(Apply to both
createAnthropicChatandanthropicText.)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-anthropic/src/adapters/text.ts` around lines 794 - 821, The return types of createAnthropicChat and anthropicText omit the fourth generic (ResolveToolCapabilities<TModel>) which currently relies on the class default; update both function return type annotations to include ResolveToolCapabilities<TModel> as the fourth generic parameter so signatures read AnthropicTextAdapter<TModel, ResolveProviderOptions<TModel>, ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>> to make intent explicit and robust if class generics change.packages/typescript/ai-gemini/src/tools/google-maps-tool.ts (1)
11-27: Defensive default is optional; current pattern works correctly.When
googleMapsTool()is called without arguments,metadataisundefined, andconvertGoogleMapsToolToAdapterFormatreturns{ googleMaps: undefined }. However, tests confirm this pattern works—the@google/genaiSDK serializes and handles undefined fields gracefully (standard JSON behavior omits undefined values). The identical pattern ingoogleSearchTool()reinforces this is intentional.If you prefer explicit defensive defaults for clarity:
Optional improvement
export function googleMapsTool( config?: GoogleMapsToolConfig, ): GeminiGoogleMapsTool { // Phantom-brand cast: '~provider'/'~toolKind' are type-only and never assigned at runtime. return { name: 'google_maps', description: '', - metadata: config, + metadata: config ?? ({} as GoogleMapsToolConfig), } as unknown as GeminiGoogleMapsTool }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-gemini/src/tools/google-maps-tool.ts` around lines 11 - 27, No change required: the current pattern in googleMapsTool and convertGoogleMapsToolToAdapterFormat correctly allows metadata to be undefined (the SDK will omit undefined fields); leave the implementations of googleMapsTool(config?: GoogleMapsToolConfig) and convertGoogleMapsToolToAdapterFormat(tool: Tool) as-is. If you prefer an explicit defensive variant, set default metadata in googleMapsTool by using metadata: config ?? undefined or default to an empty object, but this is optional.packages/typescript/ai-gemini/src/tools/file-search-tool.ts (1)
18-26: LGTM — consistent with the cross-package provider-tool branding pattern.Factory shape and phantom-brand cast match the other provider tool files;
metadata: configpreserves the runtime payload expected byconvertFileSearchToolToAdapterFormat.One minor tightening (optional): you can preserve more type safety by asserting the object satisfies
Toolbefore branding:♻️ Optional tightening
- return { - name: 'file_search', - description: '', - metadata: config, - } as unknown as GeminiFileSearchTool + const tool: Tool = { + name: 'file_search', + description: '', + metadata: config, + } + return tool as unknown as GeminiFileSearchToolThis way, if the underlying
Toolshape ever changes, the factory fails to compile instead of silently drifting.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-gemini/src/tools/file-search-tool.ts` around lines 18 - 26, The factory currently returns a branded GeminiFileSearchTool via an unsafe cast; tighten types by first ensuring the returned object satisfies the Tool shape (e.g., construct the object and assert it satisfies the Tool interface using a TypeScript "satisfies" check or an explicit Tool-typed const) before performing the phantom-brand cast to GeminiFileSearchTool in fileSearchTool; update the return to build a Tool-typed value (retaining name, description, metadata) and then cast that value to GeminiFileSearchTool so future Tool shape changes cause a compile error.packages/typescript/ai-gemini/src/tools/google-search-retriveal-tool.ts (1)
4-30: LGTM.
GoogleSearchRetrievalToolConfig+ deprecated alias +GeminiGoogleSearchRetrievalToolbrand follow the PR-wide pattern. Optionalconfigparameter and resultingmetadata: config(possiblyundefined) preserves prior runtime behavior sinceconvertGoogleSearchRetrievalToolToAdapterFormatpassesmetadatastraight intogoogleSearchRetrieval.Filename typo
google-search-retriveal-tool.ts→google-search-retrieval-tool.tsis pre-existing and out of scope here, but worth fixing in a follow-up since this file is now re-exported from the new public./toolsentrypoint.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-gemini/src/tools/google-search-retriveal-tool.ts` around lines 4 - 30, Rename the file from google-search-retriveal-tool.ts to google-search-retrieval-tool.ts and update any imports/exports that reference it so the module continues to export GoogleSearchRetrievalToolConfig, GoogleSearchRetrievalTool (deprecated alias), GeminiGoogleSearchRetrievalTool, convertGoogleSearchRetrievalToolToAdapterFormat, and googleSearchRetrievalTool under the new filename; ensure public entrypoints (e.g., the new ./tools index) re-export the renamed module and run the build/tests to catch any remaining broken imports.packages/typescript/ai-groq/src/adapters/text.ts (1)
577-617: Minor: consider mirroring Gemini’s explicit factory return types.In
createGrokText/grokText(and here increateGroqText/groqText), the return type is declared asGroqTextAdapter<TModel>, relying on the class generic defaults (ResolveProviderOptions<TModel>,ResolveInputModalities<TModel>,ResolveToolCapabilities<TModel>) to fill in the rest. Functionally this is equivalent, butcreateGeminiChat/geminiTextinai-gemini/src/adapters/text.ts(Lines 732-759) spell the resolved generics out explicitly. Making these factory signatures consistent across adapters helps with IDE hovers and avoids surprises if a default is ever changed. Not a blocker.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-groq/src/adapters/text.ts` around lines 577 - 617, The factory functions createGroqText and groqText currently declare their return type as GroqTextAdapter<TModel>, relying on class generic defaults; change both signatures to explicitly spell out the resolved generics (e.g., GroqTextAdapter<TModel, ResolveProviderOptions<TModel>, ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>>) so IDE hovers and type resolution match the pattern used in createGeminiChat/geminiText; update both createGroqText and groqText return type annotations to reference GroqTextAdapter plus ResolveProviderOptions, ResolveInputModalities, and ResolveToolCapabilities generics.packages/typescript/ai-openai/src/model-meta.ts (1)
2297-2343: Consider derivingOpenAIChatModelToolCapabilitiesByNamevia a mapped type to avoid drift.The map is hand-maintained in parallel with the model constants and with
OpenAIChatModelProviderOptionsByName. The PR notes already list "capability map drift" as a known follow-up; one way to eliminate it is to tag each model constant withas const satisfies ModelMeta<...> & { readonly name: K }and build the map from a union of constants, or export the constants so aRecord<keyof …, …['supports']['tools']>can be generated. As per coding guidelines ("Provide type safety per model by using model-specific provider options in adapter packages"), this also keeps per-model capability typing authoritative without manual enumeration.Not blocking; flagging since the same drift risk will re-appear every time a chat model is added.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-openai/src/model-meta.ts` around lines 2297 - 2343, The current OpenAIChatModelToolCapabilitiesByName manual map will drift as models change; replace it with a derived mapped type built from the existing model constants/types: create (or use) a union/type of the model constants (e.g., the exported model constants used to define GPT5_2, GPT_4O, etc.), ensure each constant is typed as const satisfies ModelMeta<...> & { readonly name: K } (or export them if not already), then define OpenAIChatModelToolCapabilitiesByName as Record<typeof MODEL_CONSTANTS[number]['name'], typeof MODEL_CONSTANTS[number]['supports']['tools']> (or an equivalent mapped type over the union) so the per-model supports.tools type is generated automatically from the model metadata instead of being hand-enumerated.packages/typescript/ai-openai/src/tools/index.ts (1)
31-115: LGTM — explicit named re-exports are the right move here.Replacing
export *with enumerated exports makes the public surface auditable and the new brandedOpenAI*Tooltypes discoverable.customToolwithoutOpenAICustomToolandfunction-toolwithout a factory re-export are both intentional per the PR design (custom is debranded; function tools come from coretoolDefinition()).One small nit: the leading comment on lines 1-3 describes the
OpenAIToolunion below, so it would read more naturally if it sat directly above theexport type OpenAITool = ...block (line 17) rather than before the imports.♻️ Move the documentation comment next to the union it describes
-// Keep the existing discriminated union defined inline. -// Built from the deprecated config-type aliases — matches the SDK shape that -// `convertToolsToProviderFormat` emits. import type { ApplyPatchTool } from './apply-patch-tool' @@ import type { WebSearchTool } from './web-search-tool' +// Keep the existing discriminated union defined inline. +// Built from the deprecated config-type aliases — matches the SDK shape that +// `convertToolsToProviderFormat` emits. export type OpenAITool =🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-openai/src/tools/index.ts` around lines 31 - 115, Move the leading documentation comment that currently sits above the imports so it directly precedes the OpenAITool union declaration; locate the comment at the top of packages/typescript/ai-openai/src/tools/index.ts and cut/paste it to sit immediately above the export type OpenAITool = ... block (the union declaration) so the comment documents that union (keep the comment text unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.changeset/provider-tools-ai-gemini.md:
- Around line 1-7: This changeset reshapes exported model metadata by moving
entries from supports.capabilities into supports.tools and renaming
grounding_with_gmaps→google_maps and search_grounding→google_search while
removing code_execution, file_search, and url_context from capabilities; update
the changeset to reflect a breaking change by changing the release type from
minor to major (or add a prominent “BREAKING CHANGE” header and migration note)
so consumers inspecting model.supports.capabilities at runtime/types know to
migrate to supports.tools and the new names.
In @.changeset/provider-tools-ai-openrouter.md:
- Around line 1-7: The changeset incorrectly uses a minor bump for a breaking
export removal; update the changeset to mark the release as major. Change the
version tag from 'minor' to 'major' in the .changeset file for
'@tanstack/ai-openrouter' and adjust the description to clearly state that
createWebSearchTool was removed from the package root and consumers must import
webSearchTool from '@tanstack/ai-openrouter/tools' (mentioning symbols
createWebSearchTool and webSearchTool/OpenRouterWebSearchTool and the tools
subpath) so the changeset accurately represents the breaking change.
In `@docs/adapters/gemini.md`:
- Around line 431-437: Update the example and prose to use the correct
FileSearch property: replace the incorrect fileIds usage inside the
fileSearchTool call with fileSearchStoreNames (an array of file search store
resource names) and change any mentions of "corpus and file IDs" to describe
that the parameter accepts file search store names; reference the FileSearch
type and the fileSearchTool call so the example uses fileSearchStoreNames:
["fileSearchStores/my-store"] instead of fileIds.
In `@docs/adapters/openai.md`:
- Around line 518-568: The examples for localShellTool, shellTool, and
applyPatchTool use openaiText("gpt-5.2") but the "Supported models" note says
only Codex CLI / agent-capable models are supported, causing a mismatch with the
compile-time supports.tools gating; fix by either replacing
openaiText("gpt-5.2") in the localShellTool, shellTool, and applyPatchTool
examples with a model id that is actually listed under supports.tools for those
tools (e.g. an agent-capable/Codex CLI model id used by your provider), or
update the "Supported models" text to explicitly include gpt-5.2 if it truly
supports local_shell/shell/apply_patch; ensure you update the examples
referencing openaiText and the Supported models lines consistently.
- Around line 344-360: The docs example for webSearchTool uses the wrong type
value; update the example call to webSearchTool(...) to pass type: "web_search"
(not "web_search_preview") so it matches the implementation and distinguishes it
from webSearchPreviewTool; locate the example that calls webSearchTool in the
docs and change the type string accordingly.
In `@docs/tools/provider-tools.md`:
- Line 37: Update the string inside the messages array (the object with role:
'user' and content: 'Summarize todays AI news.') to correct the typo by changing
"todays" to "today's" so the content reads "Summarize today's AI news."; locate
the messages array/entry and edit the content value accordingly.
In `@packages/typescript/ai-anthropic/tests/tools-per-model-type-safety.test.ts`:
- Around line 7-21: Reorder the import block to satisfy ESLint import/order and
alphabetical ordering: move external package imports (e.g., z from 'zod' and
toolDefinition from '@tanstack/ai' and TextActivityOptions from
'@tanstack/ai/adapters') above local imports (../src and ../src/tools), and
alphabetize named imports so that beforeAll is correctly ordered inside the
'vitest' import (describe, it, beforeAll -> alphabetically: beforeAll, describe,
it or follow project rule); ensure the tools import list (bashTool,
codeExecutionTool, computerUseTool, customTool, memoryTool, textEditorTool,
webFetchTool, webSearchTool) is alphabetized as required.
In `@packages/typescript/ai-openai/src/adapters/text.ts`:
- Around line 119-121: The public method signatures use the broad
OpenAITextProviderOptions instead of the generic TProviderOptions resolved
per-model; update chatStream and structuredOutput to accept
TextOptions<ResolveProviderOptions<TModel>> and
StructuredOutputOptions<ResolveProviderOptions<TModel>> respectively (or, if
implementation requires the base type, keep the existing parameter type but
narrow it immediately inside the method by casting to
ResolveProviderOptions<TModel>) so callers get model-specific type safety;
reference the chatStream and structuredOutput methods and the
ResolveProviderOptions<TModel> helper when making the change.
In `@packages/typescript/ai-openai/src/model-meta.ts`:
- Around line 40-51: Fix three issues in the tool metadata: update the
computerUseTool() branding and its converter emission so the
ProviderTool<'openai','computer_use'> brand matches the wire type (replace any
emission of 'computer_use_preview' with the correct current wire type 'computer'
or align the ProviderTool brand to the actual wire type used), remove the
deprecated local_shell entry from supports.tools (or consolidate to only
'shell') so only the maintained shell tool is present, and add missing
ProviderTool<'openai',...> factory brands for web_search_preview,
image_generation, and code_interpreter (or remove those supports.tools entries)
so every entry in supports.tools corresponds to an actual ProviderTool factory
and the type-level gating is preserved. Ensure changes touch computerUseTool(),
the converter that emits the tool type, and the supports.tools array and the
respective tool factory definitions to keep compile-time brands and runtime wire
types consistent.
In `@packages/typescript/ai-openrouter/src/tools/tool-converter.ts`:
- Around line 9-18: The current dispatch in convertToolsToProviderFormat uses
the user-controllable tool.name which is brittle; change the branching to detect
a stable provider kind/brand on the Tool (e.g., a metadata.kind or __kind
property set by webSearchTool / ProviderTool) instead of name, so
convertWebSearchToolToAdapterFormat is only called for tools with that explicit
kind/brand; update the code that creates web-search tools (webSearchTool or
ProviderTool<TProvider, 'web_search'>) to set the metadata.kind or __kind marker
and alter convertToolsToProviderFormat to check that marker before delegating to
convertWebSearchToolToAdapterFormat, otherwise call
convertFunctionToolToAdapterFormat.
In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts`:
- Around line 20-25: The converter convertWebSearchToolToAdapterFormat currently
casts tool.metadata to WebSearchToolConfig without validation; update it to
verify that tool.metadata has the expected branded shape (e.g., metadata.type
=== 'web_search' and metadata.web_search is present and has required fields)
before returning, and throw/return a clear error if the shape is invalid.
Alternatively, restrict conversion to only tools created by webSearchTool by
checking an explicit brand (metadata.type or a specific sentinel) and reject
plain toolDefinition instances; reference convertWebSearchToolToAdapterFormat,
Tool, WebSearchToolConfig, and webSearchTool when implementing the
validation/guard.
In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts`:
- Around line 7-13: Reorder and alphabetize imports to satisfy ESLint
import/order and sort-imports: move external packages (vitest, `@tanstack/ai`,
`@tanstack/ai/adapters`, zod) before the local ../src import, and alphabetize
named imports (change "describe, it, beforeAll" to "beforeAll, describe, it")
and package import order (ensure `@tanstack/`* and zod appear before ../src);
apply the same ordering pattern to the Gemini and OpenAI type-safety test files
for consistency.
In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts`:
- Around line 8-13: Reorder the imports to satisfy ESLint: alphabetically sort
the named imports so expectTypeOf appears between describe and it (import {
describe, expectTypeOf, it }), and move the value import toolDefinition before
the type-only imports (ProviderTool, TextActivityOptions, TextAdapter) so that
all value imports come before type imports; update the import list in the file
containing expectTypeOf, toolDefinition, ProviderTool, TextActivityOptions, and
TextAdapter accordingly.
---
Outside diff comments:
In `@docs/migration/migration.md`:
- Around line 13-19: The top overview numbered list is missing the new Section 6
about Provider Tools/OpenRouter; update the list to include a sixth bullet
describing "Provider Tools added — OpenRouter rename and related breaking
changes" (or similar), and ensure the summary mentions the existing renames:
`providerOptions` → `modelOptions` and `toResponseStream` →
`toServerSentEventsStream` and removal of Embeddings so readers see the
OpenRouter/provider-tools note at a glance; edit the list entries (the overview
block containing "Adapter functions split", "Common options flattened", etc.) to
add the sixth item and make sure it references the new Provider Tools/OpenRouter
change so the overview and Section 6 are consistent.
In `@packages/typescript/ai-gemini/src/model-meta.ts`:
- Around line 289-313: Remove the unsupported file_search tool from the TTS
model metadata objects: update GEMINI_2_5_PRO_TTS and GEMINI_2_5_FLASH_TTS by
deleting 'file_search' from their supports.tools arrays so their supports only
reflect text->audio capability; ensure the objects still satisfy the ModelMeta
type (GeminiToolConfigOptions & GeminiSafetyOptions & GeminiCommonConfigOptions
& GeminiCachedContentOptions) after the removal and run type checks to confirm
no other tool entries remain for TTS models.
In `@packages/typescript/ai/tests/type-check.test.ts`:
- Around line 11-22: The MockAdapter type instantiation omits the
TToolCapabilities type parameter, causing it to fall back to the default instead
of matching the mock's toolCapabilities literal; update the MockAdapter alias
(the TextAdapter instantiation) to include the fifth type argument (e.g.,
readonly string[]) so the generic list of tool capabilities is explicit and
aligns with the mock object’s toolCapabilities value.
In `@scripts/sync-provider-models.ts`:
- Around line 102-124: The templates for new model entries (the
referenceSupportsBody string used in the 'anthropic/' and 'google/' blocks)
currently hardcode the full tools superset, which will incorrectly grant
unsupported tools to newly synced models; change those template snippets to emit
a conservative default tools list (e.g., tools: [] or tools: ['web_search'])
instead of the full superset, add a short comment in the template reminding
reviewers to manually map per-model tool capabilities, and ensure any existing
per-model capability overrides (the canonical capability map) remain
authoritative so the sync doesn't auto-promote unsupported tools.
---
Nitpick comments:
In @.changeset/provider-tools-ai-anthropic.md:
- Line 5: Update the changelog sentence to explicitly state that runtime
behavior and factory signatures remain unchanged but the declared return types
are now branded subtypes of Tool (for example: AnthropicWebSearchTool extends
Tool) and that consumers who typed variables as Tool or used the `@deprecated`
config aliases are unaffected while callers that depended on the old named
return types may observe a different compile-time type; reference the factories
(webSearchTool, codeExecutionTool, computerUseTool, bashTool, textEditorTool,
webFetchTool, memoryTool, customTool) and the branded examples
(AnthropicWebSearchTool) in the wording for clarity.
In `@docs/adapters/grok.md`:
- Around line 234-241: Update the two internal links in the "Provider Tools"
section to use slug-style references (remove the .md suffix) so they match the
repo convention: change "../tools/tools.md" to "../tools/tools" and
"../tools/provider-tools.md" to "../tools/provider-tools"; then run the docs
validation command (pnpm test:docs) and fix any remaining link warnings/errors
reported for the "[Tools]" and "[Provider Tools]" references.
In `@packages/typescript/ai-anthropic/src/adapters/text.ts`:
- Around line 794-821: The return types of createAnthropicChat and anthropicText
omit the fourth generic (ResolveToolCapabilities<TModel>) which currently relies
on the class default; update both function return type annotations to include
ResolveToolCapabilities<TModel> as the fourth generic parameter so signatures
read AnthropicTextAdapter<TModel, ResolveProviderOptions<TModel>,
ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>> to make intent
explicit and robust if class generics change.
In `@packages/typescript/ai-anthropic/tests/provider-tools-smoke.test.ts`:
- Around line 88-117: The test "converts multiple provider tools in one call"
only asserts names, so add assertions that the returned converted array contains
objects with the expected types to guard against name-based dispatch
regressions; after calling convertToolsToProviderFormat (variable converted)
include assertions like
expect(converted).toContainEqual(expect.objectContaining({ type:
'web_search_20250305' })) and equivalents for 'code_execution_20250825' and
'bash_20250124' to ensure convertToolsToProviderFormat actually produced
provider-shaped tool objects rather than e.g. convertFunctionToolToAdapterFormat
outputs.
In `@packages/typescript/ai-anthropic/tsconfig.json`:
- Around line 6-10: The tsconfig currently only includes the single file
"tests/tools-per-model-type-safety.test.ts", which will miss any new tests;
update the "include" array in tsconfig.json (the entry used to list
"vite.config.ts", "./src", and the single test file) to cover the whole tests
directory (e.g., "tests" or "tests/**/*.ts") so all new type-level tests are
picked up automatically while runtime filtering remains handled by Vitest.
In `@packages/typescript/ai-gemini/src/tools/file-search-tool.ts`:
- Around line 18-26: The factory currently returns a branded
GeminiFileSearchTool via an unsafe cast; tighten types by first ensuring the
returned object satisfies the Tool shape (e.g., construct the object and assert
it satisfies the Tool interface using a TypeScript "satisfies" check or an
explicit Tool-typed const) before performing the phantom-brand cast to
GeminiFileSearchTool in fileSearchTool; update the return to build a Tool-typed
value (retaining name, description, metadata) and then cast that value to
GeminiFileSearchTool so future Tool shape changes cause a compile error.
In `@packages/typescript/ai-gemini/src/tools/google-maps-tool.ts`:
- Around line 11-27: No change required: the current pattern in googleMapsTool
and convertGoogleMapsToolToAdapterFormat correctly allows metadata to be
undefined (the SDK will omit undefined fields); leave the implementations of
googleMapsTool(config?: GoogleMapsToolConfig) and
convertGoogleMapsToolToAdapterFormat(tool: Tool) as-is. If you prefer an
explicit defensive variant, set default metadata in googleMapsTool by using
metadata: config ?? undefined or default to an empty object, but this is
optional.
In `@packages/typescript/ai-gemini/src/tools/google-search-retriveal-tool.ts`:
- Around line 4-30: Rename the file from google-search-retriveal-tool.ts to
google-search-retrieval-tool.ts and update any imports/exports that reference it
so the module continues to export GoogleSearchRetrievalToolConfig,
GoogleSearchRetrievalTool (deprecated alias), GeminiGoogleSearchRetrievalTool,
convertGoogleSearchRetrievalToolToAdapterFormat, and googleSearchRetrievalTool
under the new filename; ensure public entrypoints (e.g., the new ./tools index)
re-export the renamed module and run the build/tests to catch any remaining
broken imports.
In `@packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts`:
- Line 44: Update the test title for the `it` block whose current description is
"gemini-3.1-pro-preview accepts code_execution, file_search, google_search,
url_context" to accurately reflect that the test both accepts and rejects tools
(e.g., "gemini-3.1-pro-preview: accepts {code_execution, file_search,
google_search, url_context}; rejects {computer_use, google_maps,
google_search_retrieval}") or split the body into two `it` blocks (one asserting
accepted tools and one asserting rejected tools) so the test name matches its
assertions; ensure you update any assertion messages/labels within that `it`
block to remain consistent with the new titles.
In `@packages/typescript/ai-gemini/tsconfig.json`:
- Around line 6-10: The tsconfig.json "include" array currently references a
single test file ("tests/tools-per-model-type-safety.test.ts"), which is
fragile; update the "include" array (the include property in tsconfig.json) to
include the entire tests directory (e.g., "tests" or "./tests") instead of the
single filename so that all current and future tests under tests/ are
type-checked; keep the existing entries ("vite.config.ts" and "./src") and
replace or add the tests directory entry accordingly.
In `@packages/typescript/ai-grok/src/model-meta.ts`:
- Around line 321-333: The explicit GrokChatModelToolCapabilitiesByName object
should be rewritten as a mapped type keyed from the shared models tuple to avoid
drift: replace the manual keys in GrokChatModelToolCapabilitiesByName with a
mapped type like Record<(typeof GROK_CHAT_MODELS)[number]['name'], (typeof
GROK_CHAT_MODELS)[number]['supports']['tools']>; mirror the pattern used by
GrokChatModelProviderOptionsByName, and ensure the shared tuple (e.g.
GROK_CHAT_MODELS) is declared as const so the mapping is exhaustive and stays in
sync when models are added/removed.
In `@packages/typescript/ai-groq/src/adapters/text.ts`:
- Around line 577-617: The factory functions createGroqText and groqText
currently declare their return type as GroqTextAdapter<TModel>, relying on class
generic defaults; change both signatures to explicitly spell out the resolved
generics (e.g., GroqTextAdapter<TModel, ResolveProviderOptions<TModel>,
ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>>) so IDE hovers
and type resolution match the pattern used in createGeminiChat/geminiText;
update both createGroqText and groqText return type annotations to reference
GroqTextAdapter plus ResolveProviderOptions, ResolveInputModalities, and
ResolveToolCapabilities generics.
In `@packages/typescript/ai-groq/src/model-meta.ts`:
- Around line 367-386: The explicit per-model mapping in
GroqChatModelToolCapabilitiesByName is brittle (you must update it whenever
GROQ_CHAT_MODELS changes); replace it with a derived mapped type from the Groq
model union/collection (e.g., use GroqChatModels or GROQ_CHAT_MODELS as the key
type) that maps each model name to readonly [] so all current and future Groq
models automatically get the correct tool-capabilities type; update the
GroqChatModelToolCapabilitiesByName definition to use that mapped type instead
of enumerating LLAMA_3_1_8B_INSTANT, LLAMA_3_3_70B_VERSATILE, etc.
In `@packages/typescript/ai-openai/src/model-meta.ts`:
- Around line 2297-2343: The current OpenAIChatModelToolCapabilitiesByName
manual map will drift as models change; replace it with a derived mapped type
built from the existing model constants/types: create (or use) a union/type of
the model constants (e.g., the exported model constants used to define GPT5_2,
GPT_4O, etc.), ensure each constant is typed as const satisfies ModelMeta<...> &
{ readonly name: K } (or export them if not already), then define
OpenAIChatModelToolCapabilitiesByName as Record<typeof
MODEL_CONSTANTS[number]['name'], typeof
MODEL_CONSTANTS[number]['supports']['tools']> (or an equivalent mapped type over
the union) so the per-model supports.tools type is generated automatically from
the model metadata instead of being hand-enumerated.
In `@packages/typescript/ai-openai/src/tools/computer-use-tool.ts`:
- Around line 29-39: The factory computerUseTool currently spreads toolData
directly into metadata causing metadata to be the full ComputerUseToolConfig
(including its type tag); change metadata to wrap the options in a nested key
(e.g., metadata: { config: toolData } or metadata: { computerUseConfig: toolData
}) so metadata is a plain container like other providers, and update
convertComputerUseToolToAdapterFormat to read the nested config key instead of
casting metadata directly; reference computerUseTool,
convertComputerUseToolToAdapterFormat, ComputerUseToolConfig and
OpenAIComputerUseTool when making the change.
In `@packages/typescript/ai-openai/src/tools/index.ts`:
- Around line 31-115: Move the leading documentation comment that currently sits
above the imports so it directly precedes the OpenAITool union declaration;
locate the comment at the top of
packages/typescript/ai-openai/src/tools/index.ts and cut/paste it to sit
immediately above the export type OpenAITool = ... block (the union declaration)
so the comment documents that union (keep the comment text unchanged).
In `@packages/typescript/ai-openai/tsconfig.json`:
- Around line 6-10: The tsconfig's "include" currently pins only a single test
file ("tests/tools-per-model-type-safety.test.ts"), which will miss future
tests; update the "include" array entry for tests to a glob such as
"tests/**/*.test.ts" (or "tests/**/*") so all test files under tests/ are picked
up by tsc for the test:types run—locate the "include" array in tsconfig.json and
replace or add the broader glob entry.
In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts`:
- Around line 40-51: The returned tool object for web_search uses an empty
description which can break stricter validators; update the factory that returns
the object (the one creating name: 'web_search' and casting to
OpenRouterWebSearchTool) to provide a concise static description like "Search
the web via OpenRouter." instead of '', ensuring the description field on the
returned object is non-empty and stays compatible with the existing metadata and
the OpenRouterWebSearchTool cast.
In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts`:
- Around line 25-27: Set the OPENROUTER_API_KEY synchronously at
module-evaluation time instead of in beforeAll so adapters aren't accidentally
constructed before the env exists: move the assignment
process.env['OPENROUTER_API_KEY'] = 'sk-or-test-dummy' to the top-level of the
test file (above any imports or top-level declarations) and ensure no call to
openRouterText or any adapter factory like userTool runs at import time; this
guarantees adapters created during import will see the env and prevents future
breakage if openRouterText is moved out of an it block.
In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts`:
- Around line 53-59: Replace deprecated expectTypeOf(...).toMatchTypeOf() calls
with expectTypeOf(...).toExtend() for the two assertions: change the assertion
on userTool to use expectTypeOf(userTool).toExtend<MockToolsOption>() and change
the assertion on supportedProviderTool to use
expectTypeOf(supportedProviderTool).toExtend<MockToolsOption>(); update the two
occurrences referencing userTool, supportedProviderTool, and MockToolsOption
accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| --- | ||
| '@tanstack/ai-gemini': minor | ||
| --- | ||
|
|
||
| Expose provider-tool factories (`codeExecutionTool`, `fileSearchTool`, `googleSearchTool`, `googleSearchRetrievalTool`, `googleMapsTool`, `urlContextTool`, `computerUseTool`) on a new `/tools` subpath, each returning a branded type gated against the selected model's `supports.tools` list. | ||
|
|
||
| Note: `supports.capabilities` entries that described tools (`code_execution`, `file_search`, `grounding_with_gmaps` → renamed `google_maps`, `search_grounding` → renamed `google_search`, `url_context`) have been relocated to the new `supports.tools` field. The `capabilities` array loses those entries. This is a model-meta shape change but not a runtime break. |
There was a problem hiding this comment.
Consider flagging the supports.capabilities reshape as breaking for consumers reading model metadata.
Renaming/relocating entries (grounding_with_gmaps → google_maps, search_grounding → google_search, and removal of code_execution/file_search/url_context from capabilities) is a visible shape change to exported model metadata. Downstream code that inspects model.supports.capabilities at runtime/type-level will break, even if no adapter runtime path changes. A minor bump may be appropriate only if these maps are considered internal; otherwise this warrants a major or at least a prominent note in the migration guide.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.changeset/provider-tools-ai-gemini.md around lines 1 - 7, This changeset
reshapes exported model metadata by moving entries from supports.capabilities
into supports.tools and renaming grounding_with_gmaps→google_maps and
search_grounding→google_search while removing code_execution, file_search, and
url_context from capabilities; update the changeset to reflect a breaking change
by changing the release type from minor to major (or add a prominent “BREAKING
CHANGE” header and migration note) so consumers inspecting
model.supports.capabilities at runtime/types know to migrate to supports.tools
and the new names.
| --- | ||
| '@tanstack/ai-openrouter': minor | ||
| --- | ||
|
|
||
| **Breaking export change.** `createWebSearchTool` has been removed from the package root. Import `webSearchTool` from `@tanstack/ai-openrouter/tools` instead. See Migration Guide §6 for the before/after snippet. | ||
|
|
||
| Alongside: the new `/tools` subpath exposes `webSearchTool` (branded `OpenRouterWebSearchTool`) and the existing `convertToolsToProviderFormat`. A new `supports.tools` channel on each chat model gates provider tools at the type level. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check current version and any prior breaking-change changesets for precedent.
fd -t f 'package.json' packages/typescript/ai-openrouter --exec jq -r '.name + " " + .version' {} \;
# Look at recent changesets for how past breaking changes were bumped
fd -t f -e md . .changeset | head -50Repository: TanStack/ai
Length of output: 466
🏁 Script executed:
# Check the other provider-tools changesets to see how breaking changes were bumped
cat .changeset/provider-tools-ai-anthropic.md
cat .changeset/provider-tools-ai-core.md
cat .changeset/provider-tools-ai-openai.mdRepository: TanStack/ai
Length of output: 1566
🏁 Script executed:
# Check .changeset configuration to understand versioning policy
cat .changeset/config.json 2>/dev/null || cat .changeset/config.js 2>/dev/null || echo "No config found"Repository: TanStack/ai
Length of output: 498
🏁 Script executed:
# Look for versioning policy documentation
fd -t f -e md '(CONTRIBUTING|README|CHANGELOG)' . | head -5 | xargs -I {} grep -l "major\|minor\|breaking\|pre-1.0\|0\." {} 2>/dev/null || echo "No policy found in docs"Repository: TanStack/ai
Length of output: 114
Change semantic version to major for this breaking export removal.
This changeset describes an actual breaking change—createWebSearchTool is removed from the package root without a deprecation path. The similar provider-tools changesets (@tanstack/ai-anthropic, @tanstack/ai-openai) are marked minor because they add new /tools exports while preserving backward compatibility via deprecated aliases. The openrouter change removes an export entirely, which is a breaking change and should be major per semver, regardless of the 0.x version number.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.changeset/provider-tools-ai-openrouter.md around lines 1 - 7, The changeset
incorrectly uses a minor bump for a breaking export removal; update the
changeset to mark the release as major. Change the version tag from 'minor' to
'major' in the .changeset file for '@tanstack/ai-openrouter' and adjust the
description to clearly state that createWebSearchTool was removed from the
package root and consumers must import webSearchTool from
'@tanstack/ai-openrouter/tools' (mentioning symbols createWebSearchTool and
webSearchTool/OpenRouterWebSearchTool and the tools subpath) so the changeset
accurately represents the breaking change.
| tools: [ | ||
| fileSearchTool({ | ||
| fileIds: ["file-abc123"], | ||
| }), | ||
| ], | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -t f 'file-search' packages/typescript/ai-gemini/src/tools | xargs -r cat
rg -nP -C3 "fileSearchTool|FileSearch" packages/typescript/ai-gemini/srcRepository: TanStack/ai
Length of output: 6954
🏁 Script executed:
# Read the documentation file around the mentioned lines to see the prose
head -n 450 docs/adapters/gemini.md | tail -n +400Repository: TanStack/ai
Length of output: 1795
🏁 Script executed:
# Search for the actual FileSearch type definition or usage examples
rg -n "corpus" docs/adapters/gemini.md -C3
rg -n "FileSearch" docs/adapters/gemini.md -C5Repository: TanStack/ai
Length of output: 836
🏁 Script executed:
# Look for test files or examples that use fileSearchTool
find packages/typescript/ai-gemini -name "*.test.*" -o -name "*.spec.*" | xargs -r grep -l "fileSearchTool" 2>/dev/null
rg -l "fileSearchTool" packages/typescript/ai-gemini --type ts --type tsxRepository: TanStack/ai
Length of output: 171
🏁 Script executed:
cat packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts | grep -A 10 -B 5 fileSearchToolRepository: TanStack/ai
Length of output: 2104
🏁 Script executed:
# Also check if there are any other documentation examples or comments mentioning corpus
rg -n "corpus|fileSearchTool" docs/ --type md -C 2Repository: TanStack/ai
Length of output: 3270
🏁 Script executed:
# Search for FileSearch usage pattern in the entire codebase
rg "fileSearchTool|FileSearch" packages/typescript/ai-gemini -A 5 -B 2Repository: TanStack/ai
Length of output: 9947
🏁 Script executed:
# Check package.json for `@google/genai` version
rg "@google/genai" packages/typescript/ai-gemini -A 2 -B 2Repository: TanStack/ai
Length of output: 8313
🌐 Web query:
Google Gemini AI FileSearch type definition configuration schema
💡 Result:
The Google Gemini API FileSearch is a tool for Retrieval Augmented Generation (RAG) that enables semantic search over uploaded files stored in File Search stores. It is configured within the tools array of a generateContent request. ## Python SDK Configuration (from official docs) python from google import genai from google.genai import types tools=[ types.Tool( file_search=types.FileSearch( file_search_store_names=[file_search_store.name] ) )] Optional chunking_config can be specified during file upload/import for advanced control. ## TypeScript/JavaScript SDK (from googleapis/js-genai types.ts) typescript export declare interface FileSearch { / The names of the file_search_stores to retrieve from. Example: fileSearchStores/my-file-search-store-123 */ fileSearchStoreNames?: string[]; / The number of file search retrieval chunks to retrieve. */ topK?: number; / Metadata filter to apply to the file search retrieval documents. */ filter?: string; } ## Protobuf Definition (from google.ai.generativelanguage.v1beta.content.proto) proto message FileSearch { message RetrievalResource { string rag_store_name = 1; // e.g., "ragStores/my-rag-store-123" or "fileSearchStores/..." } message RetrievalConfig { int32 top_k = 1; // Number of chunks to retrieve } repeated RetrievalResource retrieval_resources = 1; RetrievalConfig retrieval_config = 2; } Note: Early docs use file_search_store_names; protobuf uses retrieval_resources with rag_store_name. Official Python/REST examples confirm file_search_store_names=[...]. ## Usage in REST API json { "tools": [{ "fileSearch": { "fileSearchStoreNames": ["fileSearchStores/my-store"] } }] } FileSearch supports models like Gemini 2.5 Pro/Flash, Gemini 3 series. Files are uploaded via Files API, then imported to stores via uploadToFileSearchStore or similar. Supports PDF, DOCX, TXT, code files, etc. Responses include groundingMetadata with citations. Official documentation: https://ai.google.dev/gemini-api/docs/file-search
Citations:
- 1: https://ai.google.dev/gemini-api/docs/file-search
- 2: https://github.com/googleapis/js-genai/blob/v1.46.0/src/types.ts
- 3: https://github.com/googleapis/googleapis/blob/master/google/ai/generativelanguage/v1beta/content.proto
- 4: https://docs.cloud.google.com/nodejs/docs/reference/generativelanguage/latest/generativelanguage/protos.google.ai.generativelanguage.v1beta.filesearch-class
Fix example to use correct fileSearchStoreNames property.
The example uses fileIds, but the FileSearch type from @google/genai (v1.43.0) defines the property as fileSearchStoreNames (an array of file search store names, not individual file IDs). The prose also incorrectly mentions "corpus"—that property does not exist in the FileSearch interface. Users copying this example will encounter a type error.
Update the example to:
fileSearchTool({
fileSearchStoreNames: ["fileSearchStores/my-store"],
})Also revise the prose from "corpus and file IDs" to accurately describe what fileSearchStoreNames accepts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/adapters/gemini.md` around lines 431 - 437, Update the example and prose
to use the correct FileSearch property: replace the incorrect fileIds usage
inside the fileSearchTool call with fileSearchStoreNames (an array of file
search store resource names) and change any mentions of "corpus and file IDs" to
describe that the parameter accepts file search store names; reference the
FileSearch type and the fileSearchTool call so the example uses
fileSearchStoreNames: ["fileSearchStores/my-store"] instead of fileIds.
| ### `webSearchTool` | ||
|
|
||
| Enables the model to run a web search and return grounded results with | ||
| citations. Pass a `WebSearchToolConfig` object (typed from the OpenAI SDK) | ||
| to configure the tool. | ||
|
|
||
| ```typescript | ||
| import { chat } from "@tanstack/ai"; | ||
| import { openaiText } from "@tanstack/ai-openai"; | ||
| import { webSearchTool } from "@tanstack/ai-openai/tools"; | ||
|
|
||
| const stream = chat({ | ||
| adapter: openaiText("gpt-5.2"), | ||
| messages: [{ role: "user", content: "What's new in AI this week?" }], | ||
| tools: [webSearchTool({ type: "web_search_preview" })], | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
OpenAI Responses API web_search tool type values supported (web_search vs web_search_preview)
💡 Result:
The OpenAI Responses API supports two web_search tool type values: "web_search" (the generally available version) and "web_search_preview" (the earlier preview version). Both can be specified in the tools array, e.g., {"type": "web_search"} or {"type": "web_search_preview"}. The preview variant ignores the external_web_access parameter and always behaves as if it is true (live access).
Citations:
- 1: https://developers.openai.com/api/docs/guides/tools-web-search
- 2: https://platform.openai.com/docs/guides/tools-web-search?api-mode=responses
- 3: http://developers.openai.com/api/docs/guides/tools-web-search?api-mode=responses
- 4: https://developers.openai.com/cookbook/examples/responses_api/responses_example
🏁 Script executed:
# Find the openai.md file and check lines around 344-360
fd -t f "openai.md" | head -5Repository: TanStack/ai
Length of output: 78
🏁 Script executed:
# Search for webSearchTool and webSearchPreviewTool definitions
rg "webSearchTool|webSearchPreviewTool" --type typescript --type json -B 2 -A 2 | head -80Repository: TanStack/ai
Length of output: 89
🏁 Script executed:
# Check the actual types/exports in the codebase
fd -t f "\.(ts|js)$" | xargs rg "export.*webSearchTool" | head -20Repository: TanStack/ai
Length of output: 342
🏁 Script executed:
# Read the openai.md file around lines 344-360
sed -n '340,365p' docs/adapters/openai.md | cat -nRepository: TanStack/ai
Length of output: 1092
🏁 Script executed:
# Check the webSearchTool implementation
cat -n packages/typescript/ai-openai/src/tools/web-search-tool.tsRepository: TanStack/ai
Length of output: 1269
🏁 Script executed:
# Search for webSearchPreviewTool
rg "webSearchPreviewTool" --type ts -B 2 -A 10 2>/dev/null | head -100Repository: TanStack/ai
Length of output: 7978
🏁 Script executed:
# Verify the webSearchPreviewTool implementation to confirm the type difference
cat -n packages/typescript/ai-openai/src/tools/web-search-preview-tool.tsRepository: TanStack/ai
Length of output: 1583
webSearchTool example uses incorrect type value.
The example uses type: "web_search_preview", which is for webSearchPreviewTool. The regular webSearchTool should use type: "web_search" to match the implementation.
Suggested fix
- tools: [webSearchTool({ type: "web_search_preview" })],
+ tools: [webSearchTool({ type: "web_search" })],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/adapters/openai.md` around lines 344 - 360, The docs example for
webSearchTool uses the wrong type value; update the example call to
webSearchTool(...) to pass type: "web_search" (not "web_search_preview") so it
matches the implementation and distinguishes it from webSearchPreviewTool;
locate the example that calls webSearchTool in the docs and change the type
string accordingly.
| ```typescript | ||
| import { chat } from "@tanstack/ai"; | ||
| import { openaiText } from "@tanstack/ai-openai"; | ||
| import { localShellTool } from "@tanstack/ai-openai/tools"; | ||
|
|
||
| const stream = chat({ | ||
| adapter: openaiText("gpt-5.2"), | ||
| messages: [{ role: "user", content: "Run the test suite and summarise failures" }], | ||
| tools: [localShellTool()], | ||
| }); | ||
| ``` | ||
|
|
||
| **Supported models:** Codex CLI and agent-capable models. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools). | ||
|
|
||
| ### `shellTool` | ||
|
|
||
| A function-style shell tool that exposes shell execution as a structured | ||
| function call. Takes no arguments. | ||
|
|
||
| ```typescript | ||
| import { chat } from "@tanstack/ai"; | ||
| import { openaiText } from "@tanstack/ai-openai"; | ||
| import { shellTool } from "@tanstack/ai-openai/tools"; | ||
|
|
||
| const stream = chat({ | ||
| adapter: openaiText("gpt-5.2"), | ||
| messages: [{ role: "user", content: "Count lines in all JS files" }], | ||
| tools: [shellTool()], | ||
| }); | ||
| ``` | ||
|
|
||
| **Supported models:** Codex CLI and agent-capable models. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools). | ||
|
|
||
| ### `applyPatchTool` | ||
|
|
||
| Lets the model apply unified-diff patches to modify files directly. Takes no | ||
| arguments — include it in the `tools` array to enable patch application. | ||
|
|
||
| ```typescript | ||
| import { chat } from "@tanstack/ai"; | ||
| import { openaiText } from "@tanstack/ai-openai"; | ||
| import { applyPatchTool } from "@tanstack/ai-openai/tools"; | ||
|
|
||
| const stream = chat({ | ||
| adapter: openaiText("gpt-5.2"), | ||
| messages: [{ role: "user", content: "Fix the import paths in src/index.ts" }], | ||
| tools: [applyPatchTool()], | ||
| }); | ||
| ``` | ||
|
|
||
| **Supported models:** Codex CLI and agent-capable models. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools). |
There was a problem hiding this comment.
Shell/patch tool examples contradict their own “Supported models” notes and the type‑gating feature.
localShellTool, shellTool, and applyPatchTool examples all use openaiText("gpt-5.2"), but each section states they are only supported on "Codex CLI and agent-capable models". Since this PR’s central feature is compile-time gating via supports.tools, copy-pasting these snippets will either (a) hit a type error if gpt-5.2’s supports.tools excludes local_shell/shell/apply_patch, or (b) silently work and contradict the stated support matrix — whichever is the case, one of the two is wrong.
Please pick a model listed in the expanded supports.tools for each tool (e.g. a Codex/agent-capable model id) in the examples, or update the "Supported models" text to match reality.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/adapters/openai.md` around lines 518 - 568, The examples for
localShellTool, shellTool, and applyPatchTool use openaiText("gpt-5.2") but the
"Supported models" note says only Codex CLI / agent-capable models are
supported, causing a mismatch with the compile-time supports.tools gating; fix
by either replacing openaiText("gpt-5.2") in the localShellTool, shellTool, and
applyPatchTool examples with a model id that is actually listed under
supports.tools for those tools (e.g. an agent-capable/Codex CLI model id used by
your provider), or update the "Supported models" text to explicitly include
gpt-5.2 if it truly supports local_shell/shell/apply_patch; ensure you update
the examples referencing openaiText and the Supported models lines consistently.
| tools?: Array< | ||
| | 'web_search' | ||
| | 'web_search_preview' | ||
| | 'file_search' | ||
| | 'image_generation' | ||
| | 'code_interpreter' | ||
| | 'mcp' | ||
| | 'computer_use' | ||
| | 'local_shell' | ||
| | 'shell' | ||
| | 'apply_patch' | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Extract every OpenAI tool-kind string referenced on both sides so we can
# cross-check brand strings vs. supports.tools vs. wire `type:` values.
echo "=== ProviderTool<'openai', ...> brand kinds ==="
rg -nP "ProviderTool<\s*'openai'\s*,\s*'([a-z_]+)'" --type=ts packages/typescript/ai-openai
echo
echo "=== supports.tools entries (unique) ==="
rg -nP "^\s*'(web_search|web_search_preview|file_search|image_generation|code_interpreter|mcp|computer_use|computer_use_preview|local_shell|shell|apply_patch)'," --type=ts packages/typescript/ai-openai/src/model-meta.ts \
| sed -E "s/.*'([^']+)'.*/\1/" | sort -u
echo
echo "=== wire 'type:' values emitted by converters ==="
rg -nP --type=ts -C1 "type:\s*'[a-z_]+'" packages/typescript/ai-openai/src/toolsRepository: TanStack/ai
Length of output: 5264
🌐 Web query:
OpenAI Responses API tool types: is there a distinct "shell" tool or only "local_shell"? Also confirm the exact name for the computer-use tool (computer_use vs computer_use_preview).
💡 Result:
In the OpenAI Responses API, there is a "shell" tool that supports both hosted (e.g., container_auto) and local execution modes. The older "local_shell" tool is deprecated and marked as outdated in the documentation, recommending use of the "shell" tool instead. There is no distinct generic "shell" tool separate from "local_shell"; "shell" encompasses both hosted and local shell capabilities. The computer-use tool is named "computer" (type: "computer"). The older preview version was "computer_use_preview" (type: "computer_use_preview"), which is deprecated in favor of "computer". Official docs confirm migration from computer_use_preview to "computer".
Citations:
- 1: https://developers.openai.com/api/docs/guides/tools-shell
- 2: https://platform.openai.com/docs/guides/tools-shell
- 3: https://platform.openai.com/docs/guides/tools-local-shell
- 4: https://developers.openai.com/api/docs/guides/tools-local-shell
- 5: https://developers.openai.com/api/docs/guides/tools-computer-use
- 6: https://developers.openai.com/api/docs/apit-reference/responses
- 7: https://developers.openai.com/api/docs/guides/tools-shell/
Fix type mismatch between computerUseTool() brand and wire type emission, and verify supports.tools entries align with all tool factories.
The verification found critical issues:
-
Type mismatch on
computer_usetool: ThecomputerUseTool()factory is branded asProviderTool<'openai', 'computer_use'>but the converter incomputer-use-tool.ts:19emitstype: 'computer_use_preview'. This breaks the type-to-wire contract. Additionally, OpenAI API documentation indicates the current tool type should be'computer'(not the preview variant), suggesting a broader version mismatch. -
Deprecated
local_shellcoexists with currentshell: Both tools exist in the codebase (emitting types'local_shell'and'shell'respectively). Web search confirms'shell'is the current maintained tool and'local_shell'is deprecated. Having both insupports.toolsrisks breaking if OpenAI discontinues the preview variant. -
Missing
ProviderToolbrands: Three entries insupports.tools—web_search_preview,image_generation, andcode_interpreter—lack correspondingProviderTool<'openai', ...>factory brands, creating unreachable entries that cannot gate tool arguments at compile time.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/ai-openai/src/model-meta.ts` around lines 40 - 51, Fix
three issues in the tool metadata: update the computerUseTool() branding and its
converter emission so the ProviderTool<'openai','computer_use'> brand matches
the wire type (replace any emission of 'computer_use_preview' with the correct
current wire type 'computer' or align the ProviderTool brand to the actual wire
type used), remove the deprecated local_shell entry from supports.tools (or
consolidate to only 'shell') so only the maintained shell tool is present, and
add missing ProviderTool<'openai',...> factory brands for web_search_preview,
image_generation, and code_interpreter (or remove those supports.tools entries)
so every entry in supports.tools corresponds to an actual ProviderTool factory
and the type-level gating is preserved. Ensure changes touch computerUseTool(),
the converter that emits the tool type, and the supports.tools array and the
respective tool factory definitions to keep compile-time brands and runtime wire
types consistent.
| export function convertToolsToProviderFormat( | ||
| tools: Array<Tool>, | ||
| ): Array<OpenRouterTool> { | ||
| return tools.map((tool) => convertFunctionToolToAdapterFormat(tool)) | ||
| return tools.map((tool) => { | ||
| if (tool.name === 'web_search') { | ||
| return convertWebSearchToolToAdapterFormat(tool) | ||
| } | ||
| return convertFunctionToolToAdapterFormat(tool) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Dispatching on tool.name === 'web_search' is brittle.
name is user-controllable via toolDefinition({ name: 'web_search', ... }), so any user function tool that happens to be named web_search will be routed through convertWebSearchToolToAdapterFormat, which expects a WebSearchToolConfig shape and will produce a malformed OpenRouter payload (or throw) at runtime. Now that provider tools are phantom-branded via ProviderTool<TProvider, TKind>, prefer dispatching on a stable marker (e.g., a metadata.kind/__kind brand set by webSearchTool()) rather than the public name.
♻️ Suggested shape
- return tools.map((tool) => {
- if (tool.name === 'web_search') {
- return convertWebSearchToolToAdapterFormat(tool)
- }
- return convertFunctionToolToAdapterFormat(tool)
- })
+ return tools.map((tool) => {
+ if ((tool as { metadata?: { kind?: string } }).metadata?.kind === 'openrouter.web_search') {
+ return convertWebSearchToolToAdapterFormat(tool)
+ }
+ return convertFunctionToolToAdapterFormat(tool)
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function convertToolsToProviderFormat( | |
| tools: Array<Tool>, | |
| ): Array<OpenRouterTool> { | |
| return tools.map((tool) => convertFunctionToolToAdapterFormat(tool)) | |
| return tools.map((tool) => { | |
| if (tool.name === 'web_search') { | |
| return convertWebSearchToolToAdapterFormat(tool) | |
| } | |
| return convertFunctionToolToAdapterFormat(tool) | |
| }) | |
| } | |
| export function convertToolsToProviderFormat( | |
| tools: Array<Tool>, | |
| ): Array<OpenRouterTool> { | |
| return tools.map((tool) => { | |
| if ((tool as { metadata?: { kind?: string } }).metadata?.kind === 'openrouter.web_search') { | |
| return convertWebSearchToolToAdapterFormat(tool) | |
| } | |
| return convertFunctionToolToAdapterFormat(tool) | |
| }) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/ai-openrouter/src/tools/tool-converter.ts` around lines 9
- 18, The current dispatch in convertToolsToProviderFormat uses the
user-controllable tool.name which is brittle; change the branching to detect a
stable provider kind/brand on the Tool (e.g., a metadata.kind or __kind property
set by webSearchTool / ProviderTool) instead of name, so
convertWebSearchToolToAdapterFormat is only called for tools with that explicit
kind/brand; update the code that creates web-search tools (webSearchTool or
ProviderTool<TProvider, 'web_search'>) to set the metadata.kind or __kind marker
and alter convertToolsToProviderFormat to check that marker before delegating to
convertWebSearchToolToAdapterFormat, otherwise call
convertFunctionToolToAdapterFormat.
| export function convertWebSearchToolToAdapterFormat( | ||
| tool: Tool, | ||
| ): WebSearchToolConfig { | ||
| const metadata = tool.metadata as WebSearchToolConfig | ||
| return metadata | ||
| } |
There was a problem hiding this comment.
convertWebSearchToolToAdapterFormat relies on metadata already being the full config.
The converter casts tool.metadata directly to WebSearchToolConfig and returns it. This is correct given webSearchTool() stores metadata: { type: 'web_search', web_search: {...} }, but it silently trusts any Tool with name === 'web_search' to have that exact metadata shape. If a user constructs a plain toolDefinition({ name: 'web_search', ... }) it will be mis-converted. Consider either validating metadata shape here or keying the converter off the branded tool only.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts` around lines
20 - 25, The converter convertWebSearchToolToAdapterFormat currently casts
tool.metadata to WebSearchToolConfig without validation; update it to verify
that tool.metadata has the expected branded shape (e.g., metadata.type ===
'web_search' and metadata.web_search is present and has required fields) before
returning, and throw/return a clear error if the shape is invalid.
Alternatively, restrict conversion to only tools created by webSearchTool by
checking an explicit brand (metadata.type or a specific sentinel) and reject
plain toolDefinition instances; reference convertWebSearchToolToAdapterFormat,
Tool, WebSearchToolConfig, and webSearchTool when implementing the
validation/guard.
| import { describe, it, beforeAll } from 'vitest' | ||
| import { openRouterText } from '../src' | ||
| import { webSearchTool } from '../src/tools' | ||
| import type { TextActivityOptions } from '@tanstack/ai/adapters' | ||
| import type { ProviderTool } from '@tanstack/ai' | ||
| import { toolDefinition } from '@tanstack/ai' | ||
| import { z } from 'zod' |
There was a problem hiding this comment.
Fix ESLint import order / sort-imports violations.
Static analysis flags beforeAll ordering and @tanstack/ai/zod needing to precede the local ../src import. Same pattern repeats in the Gemini and OpenAI type-safety tests — please apply consistently.
Proposed fix
-import { describe, it, beforeAll } from 'vitest'
-import { openRouterText } from '../src'
-import { webSearchTool } from '../src/tools'
-import type { TextActivityOptions } from '@tanstack/ai/adapters'
-import type { ProviderTool } from '@tanstack/ai'
-import { toolDefinition } from '@tanstack/ai'
-import { z } from 'zod'
+import { beforeAll, describe, it } from 'vitest'
+import { z } from 'zod'
+import { toolDefinition } from '@tanstack/ai'
+import type { ProviderTool } from '@tanstack/ai'
+import type { TextActivityOptions } from '@tanstack/ai/adapters'
+import { openRouterText } from '../src'
+import { webSearchTool } from '../src/tools'🧰 Tools
🪛 ESLint
[error] 7-7: Member 'beforeAll' of the import declaration should be sorted alphabetically.
(sort-imports)
[error] 12-12: @tanstack/ai import should occur before import of ../src
(import/order)
[error] 13-13: zod import should occur before import of ../src
(import/order)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts`
around lines 7 - 13, Reorder and alphabetize imports to satisfy ESLint
import/order and sort-imports: move external packages (vitest, `@tanstack/ai`,
`@tanstack/ai/adapters`, zod) before the local ../src import, and alphabetize
named imports (change "describe, it, beforeAll" to "beforeAll, describe, it")
and package import order (ensure `@tanstack/`* and zod appear before ../src);
apply the same ordering pattern to the Gemini and OpenAI type-safety test files
for consistency.
| import { describe, it, expectTypeOf } from 'vitest' | ||
| import { z } from 'zod' | ||
| import type { ProviderTool } from '../src/index' | ||
| import type { TextActivityOptions } from '../src/activities/chat/index' | ||
| import type { TextAdapter } from '../src/activities/chat/adapter' | ||
| import { toolDefinition } from '../src/index' |
There was a problem hiding this comment.
ESLint violations flagged by static analysis.
expectTypeOf should be sorted, and the toolDefinition value import should precede the type imports per import/order.
Proposed fix
-import { describe, it, expectTypeOf } from 'vitest'
-import { z } from 'zod'
-import type { ProviderTool } from '../src/index'
-import type { TextActivityOptions } from '../src/activities/chat/index'
-import type { TextAdapter } from '../src/activities/chat/adapter'
-import { toolDefinition } from '../src/index'
+import { describe, expectTypeOf, it } from 'vitest'
+import { z } from 'zod'
+import { toolDefinition } from '../src/index'
+import type { ProviderTool } from '../src/index'
+import type { TextAdapter } from '../src/activities/chat/adapter'
+import type { TextActivityOptions } from '../src/activities/chat/index'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { describe, it, expectTypeOf } from 'vitest' | |
| import { z } from 'zod' | |
| import type { ProviderTool } from '../src/index' | |
| import type { TextActivityOptions } from '../src/activities/chat/index' | |
| import type { TextAdapter } from '../src/activities/chat/adapter' | |
| import { toolDefinition } from '../src/index' | |
| import { describe, expectTypeOf, it } from 'vitest' | |
| import { z } from 'zod' | |
| import { toolDefinition } from '../src/index' | |
| import type { ProviderTool } from '../src/index' | |
| import type { TextAdapter } from '../src/activities/chat/adapter' | |
| import type { TextActivityOptions } from '../src/activities/chat/index' |
🧰 Tools
🪛 ESLint
[error] 8-8: Member 'expectTypeOf' of the import declaration should be sorted alphabetically.
(sort-imports)
[error] 13-13: ../src/index import should occur before type import of ../src/index
(import/order)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts` around
lines 8 - 13, Reorder the imports to satisfy ESLint: alphabetically sort the
named imports so expectTypeOf appears between describe and it (import {
describe, expectTypeOf, it }), and move the value import toolDefinition before
the type-only imports (ProviderTool, TextActivityOptions, TextAdapter) so that
all value imports come before type imports; update the import list in the file
containing expectTypeOf, toolDefinition, ProviderTool, TextActivityOptions, and
TextAdapter accordingly.
Summary
ai-anthropic,ai-openai,ai-gemini,ai-openrouter,ai-grok,ai-groq) now exposes provider-specific tool factories (web search, code execution, computer use, etc.) on a dedicated tree-shakeable/toolssubpath.ProviderTool<TProvider, TKind>phantom-branded type in@tanstack/aigates tool usage at the type level — passing a provider tool to a model that doesn't support it produces a compile-time error on thetools: [...]array. User-definedtoolDefinition()tools stay unbranded and always assignable.@tanstack/ai-openrouter:createWebSearchToolremoved from package root, renamed towebSearchTool, moved to/toolssubpath. Documented in Migration Guide §6.What's new
Core (
@tanstack/ai, minor):ProviderTool<TProvider, TKind>phantom-branded subtype ofTooltoolCapabilitieschannel onTextAdapter['~types']TextActivityOptions['tools']gated with a discriminated union(Tool & { '~toolKind'?: never }) | ProviderTool<string, TAdapter['~types']['toolCapabilities'][number]>Adapters (minor for openai/anthropic/gemini/openrouter, patch for grok/groq):
/toolssubpath on every adapterpackage.jsonAnthropicWebSearchTool,OpenAICodeInterpreterTool,GeminiGoogleSearchTool, etc.)<Provider>ChatModelToolCapabilitiesByNametype maps exported from each adapter rootsupports.capabilitiessplit intocapabilities+ newtoolsfieldsupports.toolsunion expanded withweb_search_preview,local_shell,shell,apply_patchsupports.toolsarray added per model (haiku-3 web_search only, haiku-3-5 web tools only, all others full superset){ [K in OpenRouterChatModel]: readonly ['web_search'] }) — no per-model population neededcustomToolfactories in anthropic/openai return plainTool(universal, not branded — every model accepts them)Sync script (
scripts/sync-provider-models.ts):referenceSupportsBodytemplates updated for anthropic/openai/gemini/grok so newly-synced models land with correcttoolsfields out of the boxTests:
expectTypeOf+@ts-expect-errorpatterntsconfiginclude for new test files to avoid surfacing unrelated pre-existing errorsDocs:
docs/tools/provider-tools.mdwith provider × tools matrixdocs/adapters/*.md)docs/tools/tools.md→ provider-tools pagedocs/config.jsonTest plan
pnpm test:typespasses across all 30 projectspnpm buildproducesdist/esm/tools/index.{js,d.ts}for every adapter with a/toolssubpathpnpm test:libpasses (full test suite)pnpm test:eslintpasses (0 errors)pnpm test:build(publint) passespnpm test:sherifpasses (workspace consistency)pnpm test:knippasses (no unused deps introduced)@ts-expect-errordirectives actually fire: remove one per adapter, confirm TypeScript reports a real TS2322 on the linereferenceSupportsBodytemplates produce correctly-shaped new model entriesKnown follow-ups (out of scope)
<Provider>ChatModelToolCapabilitiesByNamemaps in anthropic/openai/gemini/grok/groq need manual updates when the sync script adds new models. Migrating them to mapped types (like OpenRouter already does) would eliminate the drift surface. Small follow-up PR.textEditorToolname hardcode:name: 'str_replace_editor'is the 2025-01-24 variant; newer SDK variants (20250429/20250728) use'str_replace_based_edit_tool'. Tool-result dispatcher matches by name — users on newer variants silently lose tool results. One-line fix (name: config.name) deferred to a focused PR.{ type: 'web_search', web_search: {...} }(legacy plugin shape) but the SDK typesChatRequest.toolsas a flatChatWebSearchShorthand. Shape predates this PR; worth a one-shot gateway test to confirm acceptance.testing/e2e/feature scenario for anthropicwebSearchToolis deferred. Runtime smoke tests inai-anthropic/tests/provider-tools-smoke.test.tscover the critical factory → converter → SDK shape path.Summary by CodeRabbit
Release Notes
New Features
/toolssubpath (web search, code execution, computer use, etc.) across Anthropic, Gemini, OpenAI, and OpenRouter adapters.Breaking Changes
createWebSearchToolremoved from package root; importwebSearchToolfrom@tanstack/ai-openrouter/tools.Documentation