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 46 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 adds provider-native tool exports under new Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant ToolFactory as Provider Tool Factory
participant Adapter as TextAdapter
participant TS as TypeScript Compiler
Dev->>ToolFactory: import webSearchTool from `@pkg/tools`\ncall webSearchTool(config)
ToolFactory->>Dev: returns AnthropicWebSearchTool (ProviderTool<'anthropic','web_search'>)
Dev->>Adapter: adapter.chat({ tools: [webSearchTool(...)] })
Adapter->>TS: validate tools[] elements vs\nAdapter['~types']['toolCapabilities']
alt tool kind ∈ supported capabilities
TS->>Adapter: Accept (compiles)
Adapter->>Runtime: Chat runs with tool passed through
else not supported
TS->>Dev: Compile-time error (type mismatch)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
🚀 Changeset Version Preview7 package(s) bumped directly, 26 bumped as dependents. 🟥 Major bumps
🟨 Minor bumps
🟩 Patch bumps
|
|
View your CI Pipeline Execution ↗ for commit 416d8b0
☁️ 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.
| ```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.
…ng-harp # Conflicts: # packages/typescript/ai-grok/src/adapters/text.ts # packages/typescript/ai-groq/src/adapters/text.ts
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/typescript/ai-grok/src/adapters/text.ts (1)
89-160:⚠️ Potential issue | 🟠 MajorUse the resolved provider-options generic in public methods.
chatStreamandstructuredOutputaccept the broadGrokTextProviderOptions, so direct adapter method calls lose the per-model option narrowing thatTProviderOptionswas added to carry. Based on learnings, Provide type safety per model by using model-specific provider options in adapter packages.🛠️ Proposed fix
async *chatStream( - options: TextOptions<GrokTextProviderOptions>, + options: TextOptions<TProviderOptions>, ): AsyncIterable<StreamChunk> {async structuredOutput( - options: StructuredOutputOptions<GrokTextProviderOptions>, + options: StructuredOutputOptions<TProviderOptions>, ): Promise<StructuredOutputResult<unknown>> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-grok/src/adapters/text.ts` around lines 89 - 160, The public adapter methods chatStream and structuredOutput currently use the broad GrokTextProviderOptions type and lose per-model provider-option narrowing; update their signatures to accept the generic provider-options type (e.g., TextOptions<TProviderOptions> and StructuredOutputOptions<TProviderOptions> or whatever generic alias is used in this file) so callers preserve model-specific options, and adjust any internal uses (mapTextOptionsToGrok, processGrokStreamChunks) to accept/return the same generic TProviderOptions to maintain type-safety across the adapter boundary.
🧹 Nitpick comments (2)
packages/typescript/ai-groq/src/adapters/text.ts (1)
42-47: Consolidate the twoGroqTextProviderOptionsdeclarations.Line 42 defines a local alias and line 62 separately re-exports
ExternalTextProviderOptions as GroqTextProviderOptionsviaexport type { ... } from '...'. Both are needed today because theexport ... fromform doesn't create a local binding, but this can be collapsed into a single declaration that is both locally usable and exported:♻️ Proposed consolidation
-type GroqTextProviderOptions = ExternalTextProviderOptions +export type GroqTextProviderOptions = ExternalTextProviderOptions…and remove the duplicate re-export at line 62:
-/** - * Alias for TextProviderOptions for external use - */ -export type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'You'll also need to add
ExternalTextProviderOptionsto the existing type import block at line 30 (already imported there, so no new import needed).Also note:
NonNullable<GroqChatModelToolCapabilitiesByName[TModel]>at line 46 is defensive — each entry in that map istypeof MODEL.supports.tools, which is non-optional by construction. Harmless, but you can drop theNonNullable<…>wrapper if you want a tighter signature.🤖 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 42 - 47, Replace the duplicate type alias and re-export by making the local alias GroqTextProviderOptions also the exported name: import ExternalTextProviderOptions into the existing type import block, then declare "export type GroqTextProviderOptions = ExternalTextProviderOptions" (or simply "export type GroqTextProviderOptions = ExternalTextProviderOptions" where the local alias currently exists) and remove the separate "export type { ExternalTextProviderOptions as GroqTextProviderOptions }" re-export; optionally tighten ResolveToolCapabilities<TModel> by removing the NonNullable wrapper around GroqChatModelToolCapabilitiesByName[TModel] if you want the stricter signature.packages/typescript/ai-openai/src/adapters/text.ts (1)
100-112: TightenTProviderOptionsconstraint for consistency withBaseTextAdapter.
BaseTextAdapterconstrains provider options toRecord<string, any>, butOpenAITextAdapterdeclaresTProviderOptions extends object. While the resolved types fromResolveProviderOptions<TModel>are always compatible objects, making the constraint explicit improves type clarity and consistency with the parent class.Suggested improvement
export class OpenAITextAdapter< TModel extends OpenAIChatModel, - TProviderOptions extends object = ResolveProviderOptions<TModel>, + TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,Note: This pattern is used consistently across all provider adapters (Groq, Gemini, Anthropic, etc.), so consider aligning them together if making this change.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-openai/src/adapters/text.ts` around lines 100 - 112, The generic constraint for TProviderOptions on OpenAITextAdapter is looser than BaseTextAdapter; change TProviderOptions extends object to TProviderOptions extends Record<string, any> so it matches BaseTextAdapter's constraint and keeps type consistency with ResolveProviderOptions<TModel> (update the generic declaration on the OpenAITextAdapter class signature that references TProviderOptions and ensure any imports/types still type-check).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts`:
- Around line 7-21: Reorder and alphabetize the imports so ESLint's
sort-imports/import/order rules are satisfied: group external packages first
(e.g., '@google/genai' Environment, '@tanstack/ai' toolDefinition,
'@tanstack/ai/adapters' TextActivityOptions, 'vitest' imports), then third-party
libs like 'zod' (z), and finally local imports (../src geminiText and
../src/tools exports such as codeExecutionTool, computerUseTool, fileSearchTool,
googleMapsTool, googleSearchRetrievalTool, googleSearchTool, urlContextTool);
also alphabetize specifiers within each import statement.
In `@packages/typescript/ai-grok/src/adapters/text.ts`:
- Around line 65-78: GrokTextAdapter's generic TProviderOptions uses a too-loose
constraint (extends object) that conflicts with BaseTextAdapter's
TProviderOptions constraint; change the generic declaration for TProviderOptions
in the GrokTextAdapter class so it matches BaseTextAdapter (use the same
Record<string, any> constraint) — update the generic on the GrokTextAdapter
class declaration (the TProviderOptions type parameter) to extend Record<string,
any> to align with BaseTextAdapter.
---
Outside diff comments:
In `@packages/typescript/ai-grok/src/adapters/text.ts`:
- Around line 89-160: The public adapter methods chatStream and structuredOutput
currently use the broad GrokTextProviderOptions type and lose per-model
provider-option narrowing; update their signatures to accept the generic
provider-options type (e.g., TextOptions<TProviderOptions> and
StructuredOutputOptions<TProviderOptions> or whatever generic alias is used in
this file) so callers preserve model-specific options, and adjust any internal
uses (mapTextOptionsToGrok, processGrokStreamChunks) to accept/return the same
generic TProviderOptions to maintain type-safety across the adapter boundary.
---
Nitpick comments:
In `@packages/typescript/ai-groq/src/adapters/text.ts`:
- Around line 42-47: Replace the duplicate type alias and re-export by making
the local alias GroqTextProviderOptions also the exported name: import
ExternalTextProviderOptions into the existing type import block, then declare
"export type GroqTextProviderOptions = ExternalTextProviderOptions" (or simply
"export type GroqTextProviderOptions = ExternalTextProviderOptions" where the
local alias currently exists) and remove the separate "export type {
ExternalTextProviderOptions as GroqTextProviderOptions }" re-export; optionally
tighten ResolveToolCapabilities<TModel> by removing the NonNullable wrapper
around GroqChatModelToolCapabilitiesByName[TModel] if you want the stricter
signature.
In `@packages/typescript/ai-openai/src/adapters/text.ts`:
- Around line 100-112: The generic constraint for TProviderOptions on
OpenAITextAdapter is looser than BaseTextAdapter; change TProviderOptions
extends object to TProviderOptions extends Record<string, any> so it matches
BaseTextAdapter's constraint and keeps type consistency with
ResolveProviderOptions<TModel> (update the generic declaration on the
OpenAITextAdapter class signature that references TProviderOptions and ensure
any imports/types still type-check).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e202b409-2ef1-40e5-9c94-c1267a5e9802
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (23)
docs/adapters/anthropic.mddocs/adapters/gemini.mddocs/adapters/grok.mddocs/adapters/groq.mddocs/adapters/openai.mddocs/adapters/openrouter.mddocs/config.jsondocs/migration/migration.mddocs/tools/tools.mdpackages/typescript/ai-anthropic/package.jsonpackages/typescript/ai-anthropic/src/adapters/text.tspackages/typescript/ai-gemini/package.jsonpackages/typescript/ai-gemini/src/adapters/text.tspackages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.tspackages/typescript/ai-grok/src/adapters/text.tspackages/typescript/ai-groq/src/adapters/text.tspackages/typescript/ai-openai/package.jsonpackages/typescript/ai-openai/src/adapters/text.tspackages/typescript/ai-openrouter/package.jsonpackages/typescript/ai-openrouter/src/adapters/text.tspackages/typescript/ai-openrouter/src/model-meta.tspackages/typescript/ai/src/activities/chat/index.tspackages/typescript/ai/tests/test-utils.ts
✅ Files skipped from review due to trivial changes (9)
- docs/config.json
- packages/typescript/ai-anthropic/package.json
- packages/typescript/ai-gemini/package.json
- docs/adapters/gemini.md
- docs/adapters/openai.md
- packages/typescript/ai-openai/package.json
- docs/adapters/grok.md
- docs/tools/tools.md
- docs/adapters/groq.md
🚧 Files skipped from review as they are similar to previous changes (7)
- packages/typescript/ai-openrouter/package.json
- packages/typescript/ai/src/activities/chat/index.ts
- packages/typescript/ai-anthropic/src/adapters/text.ts
- docs/migration/migration.md
- packages/typescript/ai-openrouter/src/model-meta.ts
- packages/typescript/ai-gemini/src/adapters/text.ts
- docs/adapters/anthropic.md
- Tighten TProviderOptions constraint from 'extends object' to 'extends Record<string, any>' across all five text adapters to match BaseTextAdapter (openai, anthropic, gemini, grok, groq). - OpenAI & Anthropic: wire chatStream / structuredOutput to the TProviderOptions generic so per-model provider options are enforced at the call site, matching the openrouter/gemini pattern. - OpenRouter webSearchTool: brand via a stable __kind marker and gate the converter on that; reject malformed metadata explicitly instead of trusting tool.name === 'web_search' (users can collide on name). - Docs: fix fileSearchTool example to use the real fileSearchStoreNames shape; switch webSearchTool example back to type: 'web_search' (preview variant is a separate factory); update local_shell/shell/apply_patch supported-models copy to match the actual supports.tools gating; drop 'todays' typo. - Tests: apply ESLint import/order + sort-imports to the five tools-per-model type-safety specs.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
packages/typescript/ai-anthropic/src/adapters/text.ts (2)
906-925: Consider propagatingTToolCapabilitiesin factory return types.
AnthropicTextAdapternow takes a 4th genericTToolCapabilities, butcreateAnthropicChatandanthropicTextonly specify the first three type arguments in their return type annotations. The defaultResolveToolCapabilities<TModel>will resolve correctly, so this is functionally fine, but being explicit keeps the public return type self-documenting and symmetric with the other resolved generics (and guards against future refactors that might change the default).♻️ Suggested change
): 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 906 - 925, Update the return type annotations for both createAnthropicChat and anthropicText to explicitly include the fourth generic TToolCapabilities (use ResolveToolCapabilities<TModel>) so the AnthropicTextAdapter/AnthropicChatAdapter signatures are fully specified; locate the factory functions named createAnthropicChat and anthropicText and change their return type generics from three type args to four (e.g., AnthropicTextAdapter<TModel, ResolveProviderOptions<TModel>, ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>>) so the public API documents and preserves the tool-capabilities generic explicitly.
249-251: Private helpers still typed againstAnthropicTextProviderOptions.
chatStream/structuredOutputnow acceptTextOptions<TProviderOptions>, butmapCommonOptionsToAnthropic(line 250) andprocessAnthropicStream(line 542) keep their parameters asTextOptions<AnthropicTextProviderOptions>. WhenTProviderOptionsresolves to a narrower per-model intersection (e.g.AnthropicContainerOptions & AnthropicThinkingOptions & …), passing it to a parameter typed against the broaderExternalTextProviderOptionsrelies on structural compatibility that may break onceAnthropicTextProviderOptionsdiverges from the per-model maps. For consistency with the new generic threading, consider parameterizing these helpers onTProviderOptions(or at least widening toTextOptions<Record<string, any>>).Also applies to: 540-544
🤖 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 249 - 251, The helpers mapCommonOptionsToAnthropic and processAnthropicStream are still typed against TextOptions<AnthropicTextProviderOptions>, which breaks the new generic threading; update their signatures to accept the generic provider options (e.g., make them mapCommonOptionsToAnthropic<TProviderOptions>(options: TextOptions<TProviderOptions>) and processAnthropicStream<TProviderOptions>(options: TextOptions<TProviderOptions>, ...) ) or alternatively widen the parameter to TextOptions<Record<string, any>>; then update all internal references and call sites to use the new generic/widened signature so narrow per-model intersections (e.g., AnthropicContainerOptions & AnthropicThinkingOptions) are accepted without relying on fragile structural compatibility.packages/typescript/ai-groq/src/adapters/text.ts (1)
42-42: Consolidate duplicateGroqTextProviderOptionsdeclarations.Line 42 declares a local (unexported) alias
GroqTextProviderOptions = ExternalTextProviderOptions, while line 62 re-exports the same name viaexport type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'. Functionally this works (theexport ... fromre-export does not introduce a local binding that collides with line 42), but now the module has two parallel sources of truth for the same alias — one internal, one public. A future edit to one is easy to forget in the other.Consider collapsing into a single exported alias and removing the re-export at line 62:
♻️ Proposed consolidation
-type GroqTextProviderOptions = ExternalTextProviderOptions +export type GroqTextProviderOptions = ExternalTextProviderOptions-/** - * Alias for TextProviderOptions for external use - */ -export type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'🤖 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` at line 42, There are two declarations for GroqTextProviderOptions; remove the duplication by turning the local alias into the single exported alias and deleting the separate re-export: change the local declaration `type GroqTextProviderOptions = ExternalTextProviderOptions` to `export type GroqTextProviderOptions = ExternalTextProviderOptions` (ensure ExternalTextProviderOptions is imported where needed), and remove the `export type { ExternalTextProviderOptions as GroqTextProviderOptions }` re-export so only the exported alias `GroqTextProviderOptions` remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts`:
- Around line 68-84: Add the missing provider tool to the rejection test: in the
"gemini-2.0-flash-lite rejects all provider tools" case, include
googleSearchRetrievalTool() in the array passed to typedTools(adapter, [...])
with the same `@ts-expect-error` comment used for other unsupported provider tools
so the test will catch regressions for that provider; locate the test by the it
description and modify the array listing userTool, codeExecutionTool(),
computerUseTool(), fileSearchTool(...), googleMapsTool(), googleSearchTool(),
urlContextTool() to also contain googleSearchRetrievalTool().
In `@packages/typescript/ai-groq/src/adapters/text.ts`:
- Around line 44-47: Add a compile-time type-safety test that asserts
provider-branded tools are not assignable to Groq models: create a new tests
file (e.g., tools-per-model-type-safety.test.ts) that imports
ResolveToolCapabilities, GroqChatModelToolCapabilitiesByName, ProviderTool,
Tool, and TextAdapter['~types'].toolCapabilities and adds `@ts-expect-error`
assertions showing that a ProviderTool with a branded TToolCapabilities cannot
be assigned to a Groq model (whose capabilities resolve to readonly []), while
plain/unbranded Tool remains assignable; ensure the test covers a representative
Groq model name and matches the pattern used in the Anthropic adapter's
type-safety test.
In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts`:
- Around line 45-54: The validation for metadata.web_search in
convertWebSearchToolToAdapterFormat is too loose (typeof allows null and
arrays); update the check to ensure metadata.web_search is a non-null plain
object and not an array by requiring metadata.web_search !== null &&
!Array.isArray(metadata.web_search) in addition to typeof metadata.web_search
=== 'object', so that malformed branded tools (null or []) fail fast with the
existing error for tool.name when metadata.__kind !== WEB_SEARCH_TOOL_KIND or
metadata.type !== 'web_search'.
---
Nitpick comments:
In `@packages/typescript/ai-anthropic/src/adapters/text.ts`:
- Around line 906-925: Update the return type annotations for both
createAnthropicChat and anthropicText to explicitly include the fourth generic
TToolCapabilities (use ResolveToolCapabilities<TModel>) so the
AnthropicTextAdapter/AnthropicChatAdapter signatures are fully specified; locate
the factory functions named createAnthropicChat and anthropicText and change
their return type generics from three type args to four (e.g.,
AnthropicTextAdapter<TModel, ResolveProviderOptions<TModel>,
ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>>) so the public
API documents and preserves the tool-capabilities generic explicitly.
- Around line 249-251: The helpers mapCommonOptionsToAnthropic and
processAnthropicStream are still typed against
TextOptions<AnthropicTextProviderOptions>, which breaks the new generic
threading; update their signatures to accept the generic provider options (e.g.,
make them mapCommonOptionsToAnthropic<TProviderOptions>(options:
TextOptions<TProviderOptions>) and
processAnthropicStream<TProviderOptions>(options: TextOptions<TProviderOptions>,
...) ) or alternatively widen the parameter to TextOptions<Record<string, any>>;
then update all internal references and call sites to use the new
generic/widened signature so narrow per-model intersections (e.g.,
AnthropicContainerOptions & AnthropicThinkingOptions) are accepted without
relying on fragile structural compatibility.
In `@packages/typescript/ai-groq/src/adapters/text.ts`:
- Line 42: There are two declarations for GroqTextProviderOptions; remove the
duplication by turning the local alias into the single exported alias and
deleting the separate re-export: change the local declaration `type
GroqTextProviderOptions = ExternalTextProviderOptions` to `export type
GroqTextProviderOptions = ExternalTextProviderOptions` (ensure
ExternalTextProviderOptions is imported where needed), and remove the `export
type { ExternalTextProviderOptions as GroqTextProviderOptions }` re-export so
only the exported alias `GroqTextProviderOptions` remains.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 010e1fd9-31a6-4657-bc0a-3ce99fdf8650
📒 Files selected for processing (15)
docs/adapters/gemini.mddocs/adapters/openai.mddocs/tools/provider-tools.mdpackages/typescript/ai-anthropic/src/adapters/text.tspackages/typescript/ai-anthropic/tests/tools-per-model-type-safety.test.tspackages/typescript/ai-gemini/src/adapters/text.tspackages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.tspackages/typescript/ai-grok/src/adapters/text.tspackages/typescript/ai-groq/src/adapters/text.tspackages/typescript/ai-openai/src/adapters/text.tspackages/typescript/ai-openai/tests/tools-per-model-type-safety.test.tspackages/typescript/ai-openrouter/src/tools/tool-converter.tspackages/typescript/ai-openrouter/src/tools/web-search-tool.tspackages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.tspackages/typescript/ai/tests/tools-per-model-type-safety.test.ts
✅ Files skipped from review due to trivial changes (3)
- docs/tools/provider-tools.md
- docs/adapters/openai.md
- packages/typescript/ai-openai/tests/tools-per-model-type-safety.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- docs/adapters/gemini.md
- packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts
- packages/typescript/ai/tests/tools-per-model-type-safety.test.ts
- packages/typescript/ai-grok/src/adapters/text.ts
- packages/typescript/ai-openrouter/src/tools/tool-converter.ts
- packages/typescript/ai-anthropic/tests/tools-per-model-type-safety.test.ts
- packages/typescript/ai-openai/src/adapters/text.ts
| it('gemini-2.0-flash-lite rejects all provider tools', () => { | ||
| const adapter = geminiText('gemini-2.0-flash-lite') | ||
| typedTools(adapter, [ | ||
| userTool, | ||
| // @ts-expect-error - gemini-2.0-flash-lite does not support code_execution | ||
| codeExecutionTool(), | ||
| // @ts-expect-error - gemini-2.0-flash-lite does not support computer_use | ||
| computerUseTool({}), | ||
| // @ts-expect-error - gemini-2.0-flash-lite does not support file_search | ||
| fileSearchTool({ fileSearchStoreNames: [] }), | ||
| // @ts-expect-error - gemini-2.0-flash-lite does not support google_maps | ||
| googleMapsTool(), | ||
| // @ts-expect-error - gemini-2.0-flash-lite does not support google_search | ||
| googleSearchTool(), | ||
| // @ts-expect-error - gemini-2.0-flash-lite does not support url_context | ||
| urlContextTool(), | ||
| ]) |
There was a problem hiding this comment.
Include googleSearchRetrievalTool() in the “rejects all provider tools” case.
This test covers every imported Gemini provider tool except googleSearchRetrievalTool(), so one provider-tool kind can regress without this no-tools model catching it.
Proposed test coverage addition
// `@ts-expect-error` - gemini-2.0-flash-lite does not support google_search
googleSearchTool(),
+ // `@ts-expect-error` - gemini-2.0-flash-lite does not support google_search_retrieval
+ googleSearchRetrievalTool(),
// `@ts-expect-error` - gemini-2.0-flash-lite does not support url_context
urlContextTool(),📝 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.
| it('gemini-2.0-flash-lite rejects all provider tools', () => { | |
| const adapter = geminiText('gemini-2.0-flash-lite') | |
| typedTools(adapter, [ | |
| userTool, | |
| // @ts-expect-error - gemini-2.0-flash-lite does not support code_execution | |
| codeExecutionTool(), | |
| // @ts-expect-error - gemini-2.0-flash-lite does not support computer_use | |
| computerUseTool({}), | |
| // @ts-expect-error - gemini-2.0-flash-lite does not support file_search | |
| fileSearchTool({ fileSearchStoreNames: [] }), | |
| // @ts-expect-error - gemini-2.0-flash-lite does not support google_maps | |
| googleMapsTool(), | |
| // @ts-expect-error - gemini-2.0-flash-lite does not support google_search | |
| googleSearchTool(), | |
| // @ts-expect-error - gemini-2.0-flash-lite does not support url_context | |
| urlContextTool(), | |
| ]) | |
| it('gemini-2.0-flash-lite rejects all provider tools', () => { | |
| const adapter = geminiText('gemini-2.0-flash-lite') | |
| typedTools(adapter, [ | |
| userTool, | |
| // `@ts-expect-error` - gemini-2.0-flash-lite does not support code_execution | |
| codeExecutionTool(), | |
| // `@ts-expect-error` - gemini-2.0-flash-lite does not support computer_use | |
| computerUseTool({}), | |
| // `@ts-expect-error` - gemini-2.0-flash-lite does not support file_search | |
| fileSearchTool({ fileSearchStoreNames: [] }), | |
| // `@ts-expect-error` - gemini-2.0-flash-lite does not support google_maps | |
| googleMapsTool(), | |
| // `@ts-expect-error` - gemini-2.0-flash-lite does not support google_search | |
| googleSearchTool(), | |
| // `@ts-expect-error` - gemini-2.0-flash-lite does not support google_search_retrieval | |
| googleSearchRetrievalTool(), | |
| // `@ts-expect-error` - gemini-2.0-flash-lite does not support url_context | |
| urlContextTool(), | |
| ]) |
🤖 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`
around lines 68 - 84, Add the missing provider tool to the rejection test: in
the "gemini-2.0-flash-lite rejects all provider tools" case, include
googleSearchRetrievalTool() in the array passed to typedTools(adapter, [...])
with the same `@ts-expect-error` comment used for other unsupported provider tools
so the test will catch regressions for that provider; locate the test by the it
description and modify the array listing userTool, codeExecutionTool(),
computerUseTool(), fileSearchTool(...), googleMapsTool(), googleSearchTool(),
urlContextTool() to also contain googleSearchRetrievalTool().
| type ResolveToolCapabilities<TModel extends string> = | ||
| TModel extends keyof GroqChatModelToolCapabilitiesByName | ||
| ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]> | ||
| : readonly [] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify Groq model-meta declares empty supports.tools for all chat models
# and that GroqChatModelToolCapabilitiesByName is exported.
fd -t f 'model-meta.ts' packages/typescript/ai-groq | xargs -I{} rg -n -C2 'GroqChatModelToolCapabilitiesByName|supports\s*:\s*\{|tools\s*:\s*\[\s*\]' {}
# Look for a per-model type-safety test for Groq (parallel to other adapters).
fd -t f 'tools-per-model-type-safety' packages/typescript/ai-groqRepository: TanStack/ai
Length of output: 3729
🏁 Script executed:
#!/bin/bash
# Get the full GroqChatModelToolCapabilitiesByName definition
rg -A 50 'export type GroqChatModelToolCapabilitiesByName' packages/typescript/ai-groq/src/
# Find all test files in the groq adapter package
fd -t f '\.test\.|\.spec\.' packages/typescript/ai-groq/src/Repository: TanStack/ai
Length of output: 3150
🏁 Script executed:
#!/bin/bash
# List entire structure of ai-groq src directory
find packages/typescript/ai-groq/src -type f -name '*.ts' -o -name '*.tsx' | head -30
# Search for any references to GroqChatModelToolCapabilitiesByName across entire ai-groq
rg 'GroqChatModelToolCapabilitiesByName|ResolveToolCapabilities' packages/typescript/ai-groq/
# Look for test patterns in other adapter packages (like ai-fal for comparison)
find packages/typescript/ai-fal -type f \( -name '*.test.ts' -o -name '*.spec.ts' \) | head -10Repository: TanStack/ai
Length of output: 1397
🏁 Script executed:
#!/bin/bash
# Check if there's a tests directory at the root level of ai-groq
find packages/typescript/ai-groq -type d -name 'tests' -o -type d -name '__tests__'
# List all files in ai-groq package root and direct subdirectories
ls -la packages/typescript/ai-groq/ 2>/dev/null | head -20
# Check if there are any test files at all in ai-groq (not just in src)
find packages/typescript/ai-groq -type f \( -name '*.test.ts' -o -name '*.spec.ts' -o -name '*.test.tsx' -o -name '*.spec.tsx' \)
# Also check the structure of ai-fal tests for comparison
ls -la packages/typescript/ai-fal/tests/ | head -20Repository: TanStack/ai
Length of output: 1003
🏁 Script executed:
#!/bin/bash
# Examine the groq-adapter test file
cat -n packages/typescript/ai-groq/tests/groq-adapter.test.ts
# Also search for any tool capability or type safety assertions
rg -n 'tool|Tool|capability|Capability|type.*safety|branded|ProviderTool' packages/typescript/ai-groq/tests/groq-adapter.test.ts -iRepository: TanStack/ai
Length of output: 20775
🏁 Script executed:
#!/bin/bash
# Examine the text.ts adapter around the ResolveToolCapabilities definition and its usage
cat -n packages/typescript/ai-groq/src/adapters/text.ts | head -100
# Search for where tools are actually constrained in the adapter type definition
rg -n 'toolCapabilities|TToolCapabilities|ResolveToolCapabilities' packages/typescript/ai-groq/src/adapters/text.ts -B 2 -A 2Repository: TanStack/ai
Length of output: 4401
🏁 Script executed:
#!/bin/bash
# Find BaseTextAdapter definition to understand how TToolCapabilities is used
rg -n 'class BaseTextAdapter|interface BaseTextAdapter' --type ts -A 20
# Also search for how toolCapabilities constrains tool types in the base adapter
rg -n 'TToolCapabilities|toolCapabilities' packages/typescript/ai-core/src/adapters -A 2 -B 2 | head -100
# Check what the TextOptions type signature looks like
rg -n 'type TextOptions|interface TextOptions' --type ts -A 10 | head -80Repository: TanStack/ai
Length of output: 2867
🏁 Script executed:
#!/bin/bash
# Get the complete ~types property definition in BaseTextAdapter
rg -n "declare '~types'" packages/typescript/ai/src/activities/chat/adapter.ts -A 15
# Also search for how tool capabilities are enforced in the adapter
rg -n 'toolCapabilities' packages/typescript/ai/src/activities/chat/adapter.ts -B 3 -A 3
# Check the TextAdapter interface definition
rg -n 'interface TextAdapter' packages/typescript/ai/src/types.ts -A 30Repository: TanStack/ai
Length of output: 995
🏁 Script executed:
#!/bin/bash
# Get the complete TextAdapter interface definition
rg -n 'export interface TextAdapter' packages/typescript/ai/src/types.ts -A 50
# Also check if there's a ProviderTool type and how it's constrained
rg -n 'ProviderTool|type.*Tool' packages/typescript/ai/src/types.ts | head -30
# Look at how ai-fal handles tool type safety (per the learnings)
find packages/typescript/ai-fal/src -name '*.ts' -exec grep -l 'ProviderTool\|toolCapabilities' {} \;Repository: TanStack/ai
Length of output: 118
🏁 Script executed:
#!/bin/bash
# Search more broadly for TextAdapter interface
rg -n 'interface TextAdapter' packages/typescript/ai/src/ -A 20
# Search for ProviderTool definition
rg -n 'type ProviderTool|interface ProviderTool|ProviderTool<' packages/typescript/ai/src/ -B 2 -A 5
# Check ai-fal's adapter to see pattern
cat -n packages/typescript/ai-fal/src/adapters/image.ts | head -100Repository: TanStack/ai
Length of output: 8957
🏁 Script executed:
#!/bin/bash
# Confirm: when TToolCapabilities = readonly [], what does ProviderTool<string, K[number]> become?
# Search for any compile-time test or type assertion examples
rg -n 'ProviderTool.*\[\]|toolCapabilities.*\[\]|tools.*never' packages/typescript/ai/ --type ts | head -20
# Check if there are any .ts files that test type constraints (not runtime)
find packages/typescript -name '*.ts' -path '*/tests/*' -o -name '*.type.ts' | head -20Repository: TanStack/ai
Length of output: 1513
🏁 Script executed:
#!/bin/bash
# Examine the anthropic tools-per-model-type-safety test as a reference pattern
cat -n packages/typescript/ai-anthropic/tests/tools-per-model-type-safety.test.ts
# Check if groq has the same test file
ls -la packages/typescript/ai-groq/tests/tools-per-model-type-safety.test.ts 2>&1
# Also look at the model-meta.test.ts in anthropic for comparison
cat -n packages/typescript/ai-anthropic/tests/model-meta.test.ts | head -60Repository: TanStack/ai
Length of output: 8768
ResolveToolCapabilities default is correct.
Falling back to readonly [] for unknown model strings is the right default — it makes any ProviderTool<_, _> un-assignable for models without declared tool capabilities, while keeping unbranded user Tools freely usable. Matches the core TextAdapter['~types'].toolCapabilities contract.
All 13 current Groq models declare supports.tools: [] as const, so type enforcement prevents provider-branded tools from being used with Groq. This constraint is enforced at compile time via TypeScript's TToolCapabilities generic parameter, not through runtime validation.
Unlike the Anthropic adapter, there is no dedicated tools-per-model-type-safety.test.ts to verify this constraint with explicit @ts-expect-error assertions. Adding one would improve test coverage and align with other adapters, though the type safety itself is already in effect.
🤖 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 44 - 47, Add a
compile-time type-safety test that asserts provider-branded tools are not
assignable to Groq models: create a new tests file (e.g.,
tools-per-model-type-safety.test.ts) that imports ResolveToolCapabilities,
GroqChatModelToolCapabilitiesByName, ProviderTool, Tool, and
TextAdapter['~types'].toolCapabilities and adds `@ts-expect-error` assertions
showing that a ProviderTool with a branded TToolCapabilities cannot be assigned
to a Groq model (whose capabilities resolve to readonly []), while
plain/unbranded Tool remains assignable; ensure the test covers a representative
Groq model name and matches the pattern used in the Anthropic adapter's
type-safety test.
| if ( | ||
| !metadata || | ||
| metadata.__kind !== WEB_SEARCH_TOOL_KIND || | ||
| metadata.type !== 'web_search' || | ||
| typeof metadata.web_search !== 'object' | ||
| ) { | ||
| throw new Error( | ||
| `convertWebSearchToolToAdapterFormat: tool "${tool.name}" is not a valid webSearchTool() output (missing branded metadata).`, | ||
| ) | ||
| } |
There was a problem hiding this comment.
Tighten web_search shape check against null/arrays.
typeof metadata.web_search !== 'object' accepts both null and arrays (since typeof null === 'object' and typeof [] === 'object'), so a malformed branded tool with metadata.web_search: null or [] would pass validation and produce a wire payload that OpenRouter will reject with a less helpful error. Given the validator exists specifically to give callers a clear failure before hitting the network, consider making it precise:
🛡️ Proposed fix
- if (
- !metadata ||
- metadata.__kind !== WEB_SEARCH_TOOL_KIND ||
- metadata.type !== 'web_search' ||
- typeof metadata.web_search !== 'object'
- ) {
+ if (
+ !metadata ||
+ metadata.__kind !== WEB_SEARCH_TOOL_KIND ||
+ metadata.type !== 'web_search' ||
+ metadata.web_search === null ||
+ typeof metadata.web_search !== 'object' ||
+ Array.isArray(metadata.web_search)
+ ) {🤖 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
45 - 54, The validation for metadata.web_search in
convertWebSearchToolToAdapterFormat is too loose (typeof allows null and
arrays); update the check to ensure metadata.web_search is a non-null plain
object and not an array by requiring metadata.web_search !== null &&
!Array.isArray(metadata.web_search) in addition to typeof metadata.web_search
=== 'object', so that malformed branded tools (null or []) fail fast with the
existing error for tool.name when metadata.__kind !== WEB_SEARCH_TOOL_KIND or
metadata.type !== 'web_search'.
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
New Features
Breaking Changes
Documentation
Tests