Update Grok adapter for Responses API#531
Conversation
📝 WalkthroughWalkthroughGrok text adapter migrated from OpenAI-compatible Chat Completions API to xAI's Responses API, with encrypted reasoning enabled by default. Adapter now emits AG-UI reasoning event streams, uses flat Responses-API function tool shapes, handles structured output via ChangesGrok Responses API Migration
Sequence DiagramsequenceDiagram
actor Client
participant Adapter as Grok Adapter
participant API as xAI Responses API
participant Stream as Event Stream
Client->>Adapter: chat(messages, tools, options)
Note over Adapter: Map options to Responses format<br/>Apply encrypted reasoning defaults
Adapter->>API: POST /v1/responses<br/>(model, input, tools, store=false,<br/>include=['reasoning.encrypted_content'])
API-->>Stream: EventStream
Stream->>Adapter: RUN_STARTED
Adapter->>Client: RUN_STARTED event
alt Reasoning Present
Stream->>Adapter: reasoning_text.delta
Adapter->>Client: REASONING_START
Adapter->>Client: REASONING_MESSAGE_CONTENT (delta)
Adapter->>Client: REASONING_END
end
Stream->>Adapter: output_item (message.content)
Adapter->>Client: TEXT_MESSAGE_START
Adapter->>Client: TEXT_MESSAGE_CONTENT (delta)
Adapter->>Client: TEXT_MESSAGE_END
alt Tool Calls Present
Stream->>Adapter: output_item (function_call)<br/>+ function_call_arguments.delta
Adapter->>Client: TOOL_CALL_START
Adapter->>Client: TOOL_CALL_ARGS (delta)
Adapter->>Client: TOOL_CALL_END
end
Stream->>Adapter: RUN_FINISHED<br/>(usage, finish_reason)
Adapter->>Client: RUN_FINISHED event
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
|
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/code-mode/code-mode.md (1)
266-266:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winStale model name in "Strongest cloud picks" takeaway.
The table on line 249 was updated to
grok-4-2-non-reasoning, but the prose still says "Grok 4.1 Fast", making the docs self-contradictory.📝 Proposed fix
-- **Strongest cloud picks:** Grok 4.1 Fast, Claude Haiku 4.5, and Gemini 2.5 Flash all finish under 10s and handle the multi-step task cleanly. Claude Haiku 4.5 has the highest comprehensiveness score (10/10). +- **Strongest cloud picks:** Grok 4.2 Non-Reasoning, Claude Haiku 4.5, and Gemini 2.5 Flash all finish under 10s and handle the multi-step task cleanly. Claude Haiku 4.5 has the highest comprehensiveness score (10/10).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/code-mode/code-mode.md` at line 266, Update the stale model name in the "Strongest cloud picks" prose: replace the mention of "Grok 4.1 Fast" with the updated model name shown in the table ("grok-4-2-non-reasoning" or the human-friendly label used elsewhere, e.g., "Grok 4.2 Non-Reasoning") so the takeaway matches the table; look for the string "Strongest cloud picks" / the sentence containing "Grok 4.1 Fast" and edit that text to the corrected model identifier.packages/typescript/ai/skills/ai-core/adapter-configuration/references/grok-adapter.md (1)
13-15:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUpdate this reference to match the Responses API contract.
This section still documents Chat Completions-era behavior/options (
max_tokens,top_p, penalties, and “no Responses API features”), which now conflicts with the adapter behavior described elsewhere in this PR. Please align the factory description and provider-options/gotchas text to the current Responses API surface to avoid misleading users into invalid request shapes.Also applies to: 36-71
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/typescript/ai/skills/ai-core/adapter-configuration/references/grok-adapter.md` around lines 13 - 15, The reference for grok adapters still describes Chat Completions-era options; update the factory description and the provider-options/gotchas sections to reflect the Responses API contract instead: remove Chat Completions-specific fields (max_tokens, top_p, frequency/ presence penalties, “no Responses API features” wording) and document the correct Responses API fields and shapes used by grokText, grokImage, and grokSummarize (e.g., modality/inputs payload, response format/schema, temperature, top_k or n_best if applicable, and any model-specific metadata), and ensure examples and gotchas show valid request/body shapes matching the Responses API used by the adapter factory and provider options.
🧹 Nitpick comments (6)
packages/typescript/ai-grok/tests/tools-per-model-type-safety.test.ts (1)
58-69: ⚡ Quick winAdd negative type assertions for disallowed tools on
grok-4-2-non-reasoning.This block only proves a supported subset compiles. It will still pass if
codeInterpreterTool()orfileSearchTool()accidentally become accepted, so it does not actually lock down the per-model gate this suite is meant to cover.Suggested fix
it('grok-4-2-non-reasoning accepts server-side provider tools', () => { const adapter = grokText('grok-4-2-non-reasoning') typedTools(adapter, [ webSearchTool(), xSearchTool({ enable_image_understanding: true }), codeExecutionTool(), collectionsSearchTool({ vector_store_ids: ['vs_123'] }), mcpTool({ server_label: 'my-server', server_url: 'https://example.com/mcp', }), ]) + + // `@ts-expect-error` grok-4-2-non-reasoning should not accept code interpreter + typedTools(adapter, [codeInterpreterTool({ type: 'auto' })]) + + // `@ts-expect-error` grok-4-2-non-reasoning should not accept file search + typedTools(adapter, [ + fileSearchTool({ type: 'file_search', vector_store_ids: ['vs_123'] }), + ]) })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/typescript/ai-grok/tests/tools-per-model-type-safety.test.ts` around lines 58 - 69, The test only asserts allowed tools compile for grok-4-2-non-reasoning but doesn't assert that disallowed tools are rejected; add negative type assertions so the compiler fails if codeInterpreterTool() or fileSearchTool() become accepted. Concretely, in the test around the grokText('grok-4-2-non-reasoning') + typedTools(...) block, add TypeScript-negative checks (e.g., `@ts-expect-error` or equivalent compile-time assertions) for typedTools when passed codeInterpreterTool() and fileSearchTool() to guarantee these tools are rejected by the model gate.packages/typescript/ai-grok/live-tests/run.ts (1)
27-45: ⚡ Quick winCapability-gating uses the passed model, but execution targets a hardcoded one
supportsClientToolCalling(model)on Line 31 correctly gatestool-test.tsetc. based on the model passed to the runner. However, those scripts all hardcodemodel: 'grok-4.3'internally and ignore the--modelflag, so the actual API call always targetsgrok-4.3regardless. Similarly, when the runner loops over multiple models (e.g.,grok-4.2andgrok-4.3), the streaming/multi-turn/tool tests run twice but both times againstgrok-4.3.The fix is for each script to call
resolveModel()from helpers (asbuiltin-tools.tsalready does). See the comment onmulti-turn.tsfor the suggested diff.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/typescript/ai-grok/live-tests/run.ts` around lines 27 - 45, The tests are gating on supportsClientToolCalling(model) but the test scripts (tool-test.ts, tool-test-optional.ts, tool-test-empty-object.ts and multi-turn.ts) still hardcode "grok-4.3" for API calls; update each script to call resolveModel(...) from the helpers (as builtin-tools.ts already does) and use that resolvedModel for all client/API instantiation and calls instead of the hardcoded string, and ensure the runner's model variable and capability checks (supportsClientToolCalling(model) / supportsBuiltInServerTools(model)) refer to the same resolved model used by the script.packages/typescript/ai-grok/live-tests/multi-turn.ts (1)
10-12: ⚡ Quick winHardcoded model ignores
--modelflag fromrun.ts
model: 'grok-4.3'is hardcoded, so whenrun.tsinvokes this script with--model grok-4.2(or any other model), the test still runs againstgrok-4.3. This makes the runner's multi-model parameterization a no-op for this script.builtin-tools.tsalready does this correctly withresolveModel()from helpers.♻️ Suggested fix
-import { assert, loadApiKey, streamChat, textFromChunks } from './helpers' +import { assert, loadApiKey, resolveModel, streamChat, textFromChunks } from './helpers' import type { StreamChunk } from '@tanstack/ai' const apiKey = loadApiKey() +const model = resolveModel() async function testMultiTurn() { ... for await (const chunk of streamChat({ - model: 'grok-4.3', + model,The same issue applies to
tool-test.ts,tool-test-empty-object.ts, andstreaming.ts.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/typescript/ai-grok/live-tests/multi-turn.ts` around lines 10 - 12, The test hardcodes model: 'grok-4.3' inside the streamChat call; replace that hardcoded string with the resolved model used by the runner (use resolveModel(...) like builtin-tools.ts) so the --model flag flows through: import resolveModel from the helpers if needed, call resolveModel(model) (or otherwise use the already-resolved model variable) when building the options passed to streamChat, and apply the same change in tool-test.ts, tool-test-empty-object.ts, and streaming.ts to remove the hardcoded model.packages/typescript/ai-grok/src/tools/mcp-tool.ts (1)
30-40: 💤 Low valueDefault
descriptionto a meaningful string whenserver_descriptionis missing.
description: toolData.server_description || ''produces an emptyTool.descriptionwhenever the caller doesn't passserver_description. Most tool consumers (UIs, logs, planning prompts) expect a non-empty description. A small default ("Connect to an MCP server" or\MCP server: ${server_label || ...}``) is more useful and won't materially affect behavior.Proposed fix
-export function mcpTool(toolData: Omit<MCPToolConfig, 'type'>): GrokMCPTool { - validateMCPTool({ ...toolData, type: 'mcp' }) - return { - name: 'mcp', - description: toolData.server_description || '', - metadata: { - type: 'mcp', - ...toolData, - }, - } as unknown as GrokMCPTool -} +export function mcpTool(toolData: Omit<MCPToolConfig, 'type'>): GrokMCPTool { + validateMCPTool({ ...toolData, type: 'mcp' }) + return { + name: 'mcp', + description: + toolData.server_description || + `MCP server${toolData.server_label ? `: ${toolData.server_label}` : ''}`, + metadata: { + type: 'mcp', + ...toolData, + }, + } as unknown as GrokMCPTool +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/typescript/ai-grok/src/tools/mcp-tool.ts` around lines 30 - 40, The mcpTool factory returns an empty description when server_description is missing; update the description logic in mcpTool to default to a meaningful string (e.g., "Connect to an MCP server" or a dynamic fallback like `MCP server: ${toolData.server_label || toolData.server_host || 'unknown'}`) instead of ''. Modify the return object in mcpTool so description uses server_description if present else the chosen default, keeping validateMCPTool, metadata spread, and the GrokMCPTool cast unchanged.packages/typescript/ai-grok/live-tests/helpers.ts (2)
58-62: ⚡ Quick winUse
GROK_CHAT_MODELSconstant instead of hardcoded model listThe hardcoded list
['grok-4.2', 'grok-4-2-non-reasoning', 'grok-4.3']duplicates the canonicalGROK_CHAT_MODELSconstant from../src/model-meta. When models are added or removed upstream, this guard will silently drift without compile-time feedback.♻️ Proposed fix
+import { GROK_CHAT_MODELS } from '../src/model-meta' import type { GrokChatModel } from '../src/model-meta' function isGrokChatModel(model: string): model is GrokChatModel { - return ['grok-4.2', 'grok-4-2-non-reasoning', 'grok-4.3'].includes( - model, - ) + return (GROK_CHAT_MODELS as readonly string[]).includes(model) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/typescript/ai-grok/live-tests/helpers.ts` around lines 58 - 62, The function isGrokChatModel currently uses a hardcoded array of model names; replace that literal with the canonical GROK_CHAT_MODELS constant imported from ../src/model-meta so the type guard stays in sync with upstream changes. Update the isGrokChatModel implementation to reference GROK_CHAT_MODELS and add the import for GROK_CHAT_MODELS at the top of the file (keeping the function signature isGrokChatModel(model: string): model is GrokChatModel).
119-127: ⚡ Quick winRemove the double-cast
as unknown as AsyncIterable<StreamChunk>by making the return type explicitThe
chat()function from@tanstack/aireturns an async iterable whenstream: trueis passed, but TypeScript cannot narrow the return type toAsyncIterable<StreamChunk>because thestreamparameter is optional. The double-cast suppresses the type gap silently.The cleanest fix is to use an explicit async generator, which makes the return type guarantee clear to TypeScript:
♻️ Refactor using an explicit async generator
-export function streamChat(options: { +export async function* streamChat(options: { model: GrokChatModel apiKey: string messages: Array<GrokLiveTestMessage> tools?: Array<Tool> maxTokens?: number modelOptions?: Parameters<typeof chat>[0]['modelOptions'] }): AsyncIterable<StreamChunk> { - return chat({ + const stream = chat({ adapter: createLiveTestAdapter(options.model, options.apiKey), messages: options.messages, tools: options.tools, maxTokens: options.maxTokens, modelOptions: options.modelOptions, stream: true, - }) as unknown as AsyncIterable<StreamChunk> + }) + for await (const chunk of stream as AsyncIterable<StreamChunk>) { + yield chunk + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/typescript/ai-grok/live-tests/helpers.ts` around lines 119 - 127, The current return uses a double-cast on the chat(...) call; replace it with an explicit async generator to guarantee AsyncIterable<StreamChunk>. Change the function that currently returns chat({...}) (the call using createLiveTestAdapter, options.messages/tools/maxTokens/modelOptions and stream: true) to an async generator with return type AsyncIterable<StreamChunk>, call const iter = chat({...}), then for await (const chunk of iter) yield chunk, and remove the "as unknown as AsyncIterable<StreamChunk>" cast.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/adapters/grok.md`:
- Around line 346-349: The Provider Tools link uses a `.md` extension
(`../tools/provider-tools.md`) while sibling links omit it; update the link to
`../tools/provider-tools` for consistency (or add `.md` to the other links),
then run `pnpm test:docs` to verify the docs links resolve; locate and edit the
markdown list containing the `Getting Started`, `Tools Guide`, `Provider Tools`,
and `Other Adapters` entries to make the change.
In `@packages/typescript/ai-grok/live-tests/helpers.ts`:
- Around line 23-27: The current loop in envContent.split(...).forEach(...)
unconditionally assigns process.env[match[1]] = match[2], which overwrites
existing environment variables; change the logic in that loop (the block that
uses line.match(/^([^=]+)=(.*)$/)) so it only sets process.env[key] when the key
is not already defined (e.g., check if process.env[trimmedKey] === undefined or
!== undefined and skip), preserving the trimmed key/value extraction you already
have and skipping empty keys; this ensures pre-existing process.env entries take
precedence over .env.local values.
In `@packages/typescript/ai-grok/live-tests/README.md`:
- Around line 9-13: The code fence in the README currently uses an unlabelled
triple-backtick which triggers markdownlint MD040; update the fence that
contains the environment variables (the block with XAI_API_KEY and
XAI_VECTOR_STORE_ID) to include a language label such as dotenv or .env.local
(e.g., change ``` to ```dotenv) so the block is properly labeled and the linter
no longer flags MD040.
In `@packages/typescript/ai-grok/live-tests/run.ts`:
- Around line 49-53: The execFileSync invocation that runs tsx
(execFileSync('pnpm', ['exec','tsx', script, '--model', model], ...)) needs an
explicit cwd so script filenames like 'streaming.ts' resolve relative to the
live-tests directory; update the options object to include cwd pointing at the
test dir (use the same helper from helpers.ts that resolves the live-tests
directory or, on Node ≥21.2, use import.meta.dirname) so execFileSync runs from
that directory (add cwd: testsDir or cwd: path.resolve(import.meta.dirname,
'..', 'live-tests') in the execFileSync call).
In `@packages/typescript/ai-grok/live-tests/tool-test-optional.ts`:
- Around line 58-63: The test currently only asserts input.location and doesn't
verify that the optional param unit is omitted; update the assertions around
toolEnd.input (the input variable) to explicitly assert that unit is not present
(e.g. assert(!('unit' in input) || input.unit === undefined)) after the existing
location assertion so the test fails if the model returns unit: 'fahrenheit' and
thus actually covers the "optional params omitted" path.
In `@packages/typescript/ai-grok/src/adapters/text.ts`:
- Around line 320-360: handleContentPart currently returns only a STEP_FINISHED
chunk for reasoning_text, causing missing REASONING_MESSAGE_CONTENT events;
change handleContentPart to return an array of StreamChunk (emit both a
REASONING_MESSAGE_CONTENT when reasoningMessageId is set and the existing
STEP_FINISHED) so reasoning pieces are produced for each content_part; update
the two call sites that currently use yield to use yield* when invoking
handleContentPart (so they can yield multiple chunks), and add a
unit/integration test that sends a response.content_part.added event with a
reasoning_text part (verifying REASONING_MESSAGE_START,
REASONING_MESSAGE_CONTENT, then STEP_FINISHED are emitted) to prevent
regressions.
In `@packages/typescript/ai-grok/src/model-meta.ts`:
- Around line 64-72: GROK_4_2_NON_REASONING is exposing the entire
GROK_SERVER_SIDE_TOOLS tuple even though the model's type-safety test only
allows a smaller set; update the supports.tools for GROK_4_2_NON_REASONING to
the actual model-specific tool list used by tests (e.g.,
['web_search','x_search','code_execution','collections_search','mcp']) or define
a new constant like GROK_4_2_NON_REASONING_TOOLS and use that instead of
GROK_SERVER_SIDE_TOOLS, and ensure any exported type lookup (e.g.,
GrokChatModelToolCapabilitiesByName) derives from the narrowed tool list so
published types do not permit code_interpreter or file_search for this model.
- Around line 189-193: The mapping for GROK_4_2_NON_REASONING in
GrokChatModelProviderOptionsByName incorrectly uses
ExternalGrokTextProviderOptions (which exposes modelOptions.reasoning) — create
a new provider-options type that omits the entire reasoning field (e.g.,
ExternalGrokTextProviderOptionsNoReasoning or a version where
modelOptions.reasoning is never/undefined) and replace
ExternalGrokTextProviderOptions for the GROK_4_2_NON_REASONING.name entry in
GrokChatModelProviderOptionsByName; ensure the new type mirrors
ExternalGrokTextProviderOptionsWithoutEffort except with reasoning removed so
the type accurately reflects the model’s no-reasoning contract.
In `@packages/typescript/ai-grok/src/text/text-provider-options.ts`:
- Around line 167-181: The validation in validateTextProviderOptions currently
rejects reasoning.effort for models 'grok-4.3' and 'grok-4.2' but misses
'grok-4-2-non-reasoning'; update the guard in validateTextProviderOptions to
also reject any reasoning options for 'grok-4-2-non-reasoning' (e.g., add that
string to the unsupportedReasoningEffortModels set or include it in the model
check) so callers get the same clear SDK error when they pass
modelOptions.reasoning for that model.
In `@packages/typescript/ai-grok/src/tools/file-search-tool.ts`:
- Around line 35-36: Guard against missing or invalid tool.metadata before
casting and dereferencing vector_store_ids: in the FileSearchTool conversion
code where you reference metadata (currently casting to FileSearchToolConfig and
calling validateVectorStoreIds(metadata.vector_store_ids)), first check that
tool.metadata exists and matches expected shape (or run a schema
parse/validator) and return or throw a clear error if it’s absent/invalid; apply
the same guard to the other conversion path around the second usage at the block
that references metadata and validateVectorStoreIds on lines near the second
occurrence so validateVectorStoreIds always receives a defined array.
In `@packages/typescript/ai-grok/src/tools/x-search-tool.ts`:
- Around line 20-25: The conversion function convertXSearchToolToAdapterFormat
is currently casting tool.metadata to XSearchToolConfig without runtime
validation; add a Zod schema (e.g., xSearchToolConfigSchema using z.object with
the fields type: z.literal('x_search'), allowed_x_handles/excluded_x_handles as
optional string arrays, from_date/to_date as optional strings, and
enable_image_understanding/enable_video_understanding as optional booleans) and
replace the unsafe cast with xSearchToolConfigSchema.parse(tool.metadata) so
invalid metadata throws a clear error at parse time.
In `@testing/panel/src/lib/model-selection.ts`:
- Around line 83-97: Update the model identifiers in the model selection list
entries for provider 'grok' so they match xAI API naming: replace 'grok-4.2'
with the correct 'grok-4.20' (or 'grok-4.20-reasoning' if you intend the
reasoning variant) and replace 'grok-4-2-non-reasoning' with
'grok-4.20-non-reasoning'; locate the objects in the model selection array in
model-selection.ts (the entries where provider === 'grok' and model ===
'grok-4.3'/'grok-4.2'/'grok-4-2-non-reasoning') and update the model string and
label accordingly, then validate the final identifiers by checking the API
response from GET /v1/models before merging.
---
Outside diff comments:
In `@docs/code-mode/code-mode.md`:
- Line 266: Update the stale model name in the "Strongest cloud picks" prose:
replace the mention of "Grok 4.1 Fast" with the updated model name shown in the
table ("grok-4-2-non-reasoning" or the human-friendly label used elsewhere,
e.g., "Grok 4.2 Non-Reasoning") so the takeaway matches the table; look for the
string "Strongest cloud picks" / the sentence containing "Grok 4.1 Fast" and
edit that text to the corrected model identifier.
In
`@packages/typescript/ai/skills/ai-core/adapter-configuration/references/grok-adapter.md`:
- Around line 13-15: The reference for grok adapters still describes Chat
Completions-era options; update the factory description and the
provider-options/gotchas sections to reflect the Responses API contract instead:
remove Chat Completions-specific fields (max_tokens, top_p, frequency/ presence
penalties, “no Responses API features” wording) and document the correct
Responses API fields and shapes used by grokText, grokImage, and grokSummarize
(e.g., modality/inputs payload, response format/schema, temperature, top_k or
n_best if applicable, and any model-specific metadata), and ensure examples and
gotchas show valid request/body shapes matching the Responses API used by the
adapter factory and provider options.
---
Nitpick comments:
In `@packages/typescript/ai-grok/live-tests/helpers.ts`:
- Around line 58-62: The function isGrokChatModel currently uses a hardcoded
array of model names; replace that literal with the canonical GROK_CHAT_MODELS
constant imported from ../src/model-meta so the type guard stays in sync with
upstream changes. Update the isGrokChatModel implementation to reference
GROK_CHAT_MODELS and add the import for GROK_CHAT_MODELS at the top of the file
(keeping the function signature isGrokChatModel(model: string): model is
GrokChatModel).
- Around line 119-127: The current return uses a double-cast on the chat(...)
call; replace it with an explicit async generator to guarantee
AsyncIterable<StreamChunk>. Change the function that currently returns
chat({...}) (the call using createLiveTestAdapter,
options.messages/tools/maxTokens/modelOptions and stream: true) to an async
generator with return type AsyncIterable<StreamChunk>, call const iter =
chat({...}), then for await (const chunk of iter) yield chunk, and remove the
"as unknown as AsyncIterable<StreamChunk>" cast.
In `@packages/typescript/ai-grok/live-tests/multi-turn.ts`:
- Around line 10-12: The test hardcodes model: 'grok-4.3' inside the streamChat
call; replace that hardcoded string with the resolved model used by the runner
(use resolveModel(...) like builtin-tools.ts) so the --model flag flows through:
import resolveModel from the helpers if needed, call resolveModel(model) (or
otherwise use the already-resolved model variable) when building the options
passed to streamChat, and apply the same change in tool-test.ts,
tool-test-empty-object.ts, and streaming.ts to remove the hardcoded model.
In `@packages/typescript/ai-grok/live-tests/run.ts`:
- Around line 27-45: The tests are gating on supportsClientToolCalling(model)
but the test scripts (tool-test.ts, tool-test-optional.ts,
tool-test-empty-object.ts and multi-turn.ts) still hardcode "grok-4.3" for API
calls; update each script to call resolveModel(...) from the helpers (as
builtin-tools.ts already does) and use that resolvedModel for all client/API
instantiation and calls instead of the hardcoded string, and ensure the runner's
model variable and capability checks (supportsClientToolCalling(model) /
supportsBuiltInServerTools(model)) refer to the same resolved model used by the
script.
In `@packages/typescript/ai-grok/src/tools/mcp-tool.ts`:
- Around line 30-40: The mcpTool factory returns an empty description when
server_description is missing; update the description logic in mcpTool to
default to a meaningful string (e.g., "Connect to an MCP server" or a dynamic
fallback like `MCP server: ${toolData.server_label || toolData.server_host ||
'unknown'}`) instead of ''. Modify the return object in mcpTool so description
uses server_description if present else the chosen default, keeping
validateMCPTool, metadata spread, and the GrokMCPTool cast unchanged.
In `@packages/typescript/ai-grok/tests/tools-per-model-type-safety.test.ts`:
- Around line 58-69: The test only asserts allowed tools compile for
grok-4-2-non-reasoning but doesn't assert that disallowed tools are rejected;
add negative type assertions so the compiler fails if codeInterpreterTool() or
fileSearchTool() become accepted. Concretely, in the test around the
grokText('grok-4-2-non-reasoning') + typedTools(...) block, add
TypeScript-negative checks (e.g., `@ts-expect-error` or equivalent compile-time
assertions) for typedTools when passed codeInterpreterTool() and
fileSearchTool() to guarantee these tools are rejected by the model gate.
🪄 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: 184cbabb-2c2e-4b72-8d66-86e618cd49d2
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (56)
.changeset/grok-responses-api-encrypted-reasoning.mddocs/adapters/grok.mddocs/code-mode/code-mode.mddocs/community-adapters/cencori.mddocs/community-adapters/cloudflare.mdexamples/ts-react-chat/src/lib/model-selection.tsexamples/ts-react-chat/src/routes/api.tanchat.tsexamples/ts-react-chat/src/routes/generations.structured-output.tsxknip.jsonpackages/typescript/ai-code-mode/README.mdpackages/typescript/ai-code-mode/models-eval/eval-config.tspackages/typescript/ai-code-mode/models-eval/results.jsonpackages/typescript/ai-code-mode/models-eval/run-eval.tspackages/typescript/ai-grok/README.mdpackages/typescript/ai-grok/live-tests/README.mdpackages/typescript/ai-grok/live-tests/builtin-tools.tspackages/typescript/ai-grok/live-tests/helpers.tspackages/typescript/ai-grok/live-tests/multi-turn.tspackages/typescript/ai-grok/live-tests/package.jsonpackages/typescript/ai-grok/live-tests/reasoning.tspackages/typescript/ai-grok/live-tests/run.tspackages/typescript/ai-grok/live-tests/server-tools.tspackages/typescript/ai-grok/live-tests/streaming.tspackages/typescript/ai-grok/live-tests/structured-output.tspackages/typescript/ai-grok/live-tests/tool-test-empty-object.tspackages/typescript/ai-grok/live-tests/tool-test-optional.tspackages/typescript/ai-grok/live-tests/tool-test.tspackages/typescript/ai-grok/src/adapters/image.tspackages/typescript/ai-grok/src/adapters/summarize.tspackages/typescript/ai-grok/src/adapters/text.tspackages/typescript/ai-grok/src/image/image-provider-options.tspackages/typescript/ai-grok/src/model-meta.tspackages/typescript/ai-grok/src/text/text-provider-options.tspackages/typescript/ai-grok/src/tools/code-execution-tool.tspackages/typescript/ai-grok/src/tools/file-search-tool.tspackages/typescript/ai-grok/src/tools/function-tool.tspackages/typescript/ai-grok/src/tools/index.tspackages/typescript/ai-grok/src/tools/mcp-tool.tspackages/typescript/ai-grok/src/tools/tool-converter.tspackages/typescript/ai-grok/src/tools/web-search-tool.tspackages/typescript/ai-grok/src/tools/x-search-tool.tspackages/typescript/ai-grok/tests/grok-adapter.test.tspackages/typescript/ai-grok/tests/tools-per-model-type-safety.test.tspackages/typescript/ai/skills/ai-core/adapter-configuration/SKILL.mdpackages/typescript/ai/skills/ai-core/adapter-configuration/references/grok-adapter.mdscripts/sync-provider-models.tstesting/e2e/src/lib/media-providers.tstesting/e2e/src/lib/providers.tstesting/e2e/src/routes/api.summarize.tstesting/panel/src/lib/model-selection.tstesting/panel/src/routes/api.chat.tstesting/panel/src/routes/api.structured.tstesting/panel/src/routes/api.summarize.tstesting/panel/src/routes/structured.tsxtesting/panel/src/routes/summarize.tsxtesting/panel/tests/vendor-config.ts
| - [Getting Started](../getting-started/quick-start) - Learn the basics | ||
| - [Tools Guide](../tools/tools) - Learn about tools | ||
| - [Tools Guide](../tools/tools) - Learn about app-defined tools | ||
| - [Provider Tools](../tools/provider-tools.md) - Learn about provider-native tools | ||
| - [Other Adapters](./openai) - Explore other providers |
There was a problem hiding this comment.
Inconsistent doc-link extension on the Provider Tools entry.
../tools/provider-tools.md includes .md while the sibling links (../tools/tools, ../getting-started/quick-start, ./openai) omit the extension. Either drop .md here for consistency or add it to the others, then run pnpm test:docs to confirm the link resolves.
Proposed fix
-- [Provider Tools](../tools/provider-tools.md) - Learn about provider-native tools
+- [Provider Tools](../tools/provider-tools) - Learn about provider-native toolsAs per coding guidelines: "Verify documentation links are valid via pnpm test:docs command".
📝 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.
| - [Getting Started](../getting-started/quick-start) - Learn the basics | |
| - [Tools Guide](../tools/tools) - Learn about tools | |
| - [Tools Guide](../tools/tools) - Learn about app-defined tools | |
| - [Provider Tools](../tools/provider-tools.md) - Learn about provider-native tools | |
| - [Other Adapters](./openai) - Explore other providers | |
| - [Getting Started](../getting-started/quick-start) - Learn the basics | |
| - [Tools Guide](../tools/tools) - Learn about app-defined tools | |
| - [Provider Tools](../tools/provider-tools) - Learn about provider-native tools | |
| - [Other Adapters](./openai) - Explore other providers |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/adapters/grok.md` around lines 346 - 349, The Provider Tools link uses a
`.md` extension (`../tools/provider-tools.md`) while sibling links omit it;
update the link to `../tools/provider-tools` for consistency (or add `.md` to
the other links), then run `pnpm test:docs` to verify the docs links resolve;
locate and edit the markdown list containing the `Getting Started`, `Tools
Guide`, `Provider Tools`, and `Other Adapters` entries to make the change.
| envContent.split('\n').forEach((line) => { | ||
| const match = line.match(/^([^=]+)=(.*)$/) | ||
| if (match) { | ||
| process.env[match[1]!.trim()] = match[2]!.trim() | ||
| } |
There was a problem hiding this comment.
.env.local values unconditionally overwrite existing process.env entries
The loop always sets process.env[key] = value, which means a .env.local file silently wins over variables already in the process environment (e.g., injected by a CI runner or a parent shell). The standard dotenv convention is the opposite: pre-existing env vars take precedence.
🛠️ Proposed fix
- process.env[match[1]!.trim()] = match[2]!.trim()
+ const key = match[1]!.trim()
+ if (process.env[key] === undefined) {
+ process.env[key] = match[2]!.trim()
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/live-tests/helpers.ts` around lines 23 - 27, The
current loop in envContent.split(...).forEach(...) unconditionally assigns
process.env[match[1]] = match[2], which overwrites existing environment
variables; change the logic in that loop (the block that uses
line.match(/^([^=]+)=(.*)$/)) so it only sets process.env[key] when the key is
not already defined (e.g., check if process.env[trimmedKey] === undefined or !==
undefined and skip), preserving the trimmed key/value extraction you already
have and skipping empty keys; this ensures pre-existing process.env entries take
precedence over .env.local values.
| ``` | ||
| XAI_API_KEY=xai-... | ||
| # Optional, only needed for file_search / collections_search live tests | ||
| XAI_VECTOR_STORE_ID=vs_... | ||
| ``` |
There was a problem hiding this comment.
Add a language to the .env.local code fence.
This block currently trips markdownlint MD040. Using dotenv here keeps the new README lint-clean.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 9-9: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/live-tests/README.md` around lines 9 - 13, The
code fence in the README currently uses an unlabelled triple-backtick which
triggers markdownlint MD040; update the fence that contains the environment
variables (the block with XAI_API_KEY and XAI_VECTOR_STORE_ID) to include a
language label such as dotenv or .env.local (e.g., change ``` to ```dotenv) so
the block is properly labeled and the linter no longer flags MD040.
| try { | ||
| execFileSync('pnpm', ['exec', 'tsx', script, '--model', model], { | ||
| stdio: 'inherit', | ||
| env: process.env, | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how run.ts is invoked from the package scripts
fd -g 'package.json' packages/typescript/ai-grok/live-tests/ --exec cat {}Repository: TanStack/ai
Length of output: 930
🏁 Script executed:
# Check if run.ts exists and view its content
cat -n packages/typescript/ai-grok/live-tests/run.ts | head -80Repository: TanStack/ai
Length of output: 2124
🏁 Script executed:
# Search for references to run.ts - how is it invoked?
rg "run\.ts|live-tests" packages/typescript/ai-grok/package.jsonRepository: TanStack/ai
Length of output: 37
🏁 Script executed:
# Find the parent ai-grok package.json
cat packages/typescript/ai-grok/package.json | grep -A 20 '"scripts"'Repository: TanStack/ai
Length of output: 506
🏁 Script executed:
# Search for any invocation of run.ts in the codebase
rg "run\.ts" packages/typescript/ai-grok/ --type json --type yaml --type ymlRepository: TanStack/ai
Length of output: 83
🏁 Script executed:
# Check if there are CI workflows that might invoke the live tests
find . -type f \( -name "*.yml" -o -name "*.yaml" \) | xargs grep -l "ai-grok\|live-tests" | head -5Repository: TanStack/ai
Length of output: 101
🏁 Script executed:
# Search for CI workflows with ai-grok or live-tests
find . -type f -name "*.yaml" | xargs grep -l "ai-grok\|live-tests" 2>/dev/nullRepository: TanStack/ai
Length of output: 101
🏁 Script executed:
# Search for any docs or READMEs mentioning run.ts
rg "run\.ts" packages/typescript/ai-grok/ -t mdRepository: TanStack/ai
Length of output: 37
🏁 Script executed:
# Check the git diff to see if run.ts is new and how it was added
git log --oneline --all -- packages/typescript/ai-grok/live-tests/run.ts | head -5Repository: TanStack/ai
Length of output: 101
🏁 Script executed:
# View all of run.ts to check for any import.meta or relative path usage
cat packages/typescript/ai-grok/live-tests/run.tsRepository: TanStack/ai
Length of output: 1662
🏁 Script executed:
# Check helpers.ts which is imported, see if it has any cwd assumptions
cat packages/typescript/ai-grok/live-tests/helpers.ts | head -50Repository: TanStack/ai
Length of output: 1436
Missing cwd — script paths will fail if runner is not invoked from live-tests/
The Node.js docs specify: "Use cwd to specify the working directory from which the process is spawned. If not given, the default is to inherit the current working directory." The scripts are referenced by bare filenames ('streaming.ts', 'tool-test.ts', etc.), so tsx streaming.ts resolves relative to whatever directory the runner was launched from. If invoked from the package root (e.g., pnpm -F ai-grok exec tsx live-tests/run.ts), tsx would look for streaming.ts in the package root and hit ENOENT. The pattern for resolving cwd-relative paths already exists in helpers.ts and should be applied here as well.
🐛 Proposed fix
+import { fileURLToPath } from 'node:url'
+import { dirname } from 'node:path'
+
+const __dirname = dirname(fileURLToPath(import.meta.url))
...
execFileSync('pnpm', ['exec', 'tsx', script, '--model', model], {
+ cwd: __dirname,
stdio: 'inherit',
env: process.env,
})Or, for Node ≥ 21.2, simply use import.meta.dirname directly.
📝 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.
| try { | |
| execFileSync('pnpm', ['exec', 'tsx', script, '--model', model], { | |
| stdio: 'inherit', | |
| env: process.env, | |
| }) | |
| import { fileURLToPath } from 'node:url' | |
| import { dirname } from 'node:path' | |
| const __dirname = dirname(fileURLToPath(import.meta.url)) | |
| try { | |
| execFileSync('pnpm', ['exec', 'tsx', script, '--model', model], { | |
| cwd: __dirname, | |
| stdio: 'inherit', | |
| env: process.env, | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/live-tests/run.ts` around lines 49 - 53, The
execFileSync invocation that runs tsx (execFileSync('pnpm', ['exec','tsx',
script, '--model', model], ...)) needs an explicit cwd so script filenames like
'streaming.ts' resolve relative to the live-tests directory; update the options
object to include cwd pointing at the test dir (use the same helper from
helpers.ts that resolves the live-tests directory or, on Node ≥21.2, use
import.meta.dirname) so execFileSync runs from that directory (add cwd: testsDir
or cwd: path.resolve(import.meta.dirname, '..', 'live-tests') in the
execFileSync call).
| const input = toolEnd.input as { location?: unknown } | ||
| assert( | ||
| typeof input.location === 'string', | ||
| `Expected location string, got: ${JSON.stringify(input)}`, | ||
| ) | ||
| console.log(` Tool args: ${JSON.stringify(input)}`) |
There was a problem hiding this comment.
Assert that unit is actually omitted.
Right now this test still passes if the model always emits unit: 'fahrenheit', so it does not verify the "optional params omitted" path it claims to cover.
Suggested fix
- const input = toolEnd.input as { location?: unknown }
+ const input = toolEnd.input as { location?: unknown; unit?: unknown }
assert(
typeof input.location === 'string',
`Expected location string, got: ${JSON.stringify(input)}`,
)
+ assert(
+ !Object.prototype.hasOwnProperty.call(input, 'unit'),
+ `Expected optional unit to be omitted, got: ${JSON.stringify(input)}`,
+ )
console.log(` Tool args: ${JSON.stringify(input)}`)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/live-tests/tool-test-optional.ts` around lines 58
- 63, The test currently only asserts input.location and doesn't verify that the
optional param unit is omitted; update the assertions around toolEnd.input (the
input variable) to explicitly assert that unit is not present (e.g.
assert(!('unit' in input) || input.unit === undefined)) after the existing
location assertion so the test fails if the model returns unit: 'fahrenheit' and
thus actually covers the "optional params omitted" path.
| export type GrokChatModelProviderOptionsByName = { | ||
| [K in (typeof GROK_CHAT_MODELS)[number]]: GrokProviderOptions | ||
| [GROK_4_2.name]: ExternalGrokTextProviderOptionsWithoutEffort | ||
| [GROK_4_2_NON_REASONING.name]: ExternalGrokTextProviderOptions | ||
| [GROK_4_3.name]: ExternalGrokTextProviderOptionsWithoutEffort | ||
| } |
There was a problem hiding this comment.
Introduce a no-reasoning provider-options variant for grok-4-2-non-reasoning.
Lines 67-71 explicitly omit 'reasoning' from this model's capabilities, but Line 191 maps it to ExternalGrokTextProviderOptions, which still exposes modelOptions.reasoning. Even ExternalGrokTextProviderOptionsWithoutEffort would still allow reasoning.summary, so the current model map cannot express the actual contract for this model.
Suggested fix
// text/text-provider-options.ts
+export type ExternalTextProviderOptionsWithoutReasoning =
+ GrokSharedTextProviderOptions & {
+ reasoning?: never
+ }
// model-meta.ts
import type {
ExternalTextProviderOptions as ExternalGrokTextProviderOptions,
ExternalTextProviderOptionsWithoutEffort as ExternalGrokTextProviderOptionsWithoutEffort,
+ ExternalTextProviderOptionsWithoutReasoning as ExternalGrokTextProviderOptionsWithoutReasoning,
} from './text/text-provider-options'
export type GrokChatModelProviderOptionsByName = {
[GROK_4_2.name]: ExternalGrokTextProviderOptionsWithoutEffort
- [GROK_4_2_NON_REASONING.name]: ExternalGrokTextProviderOptions
+ [GROK_4_2_NON_REASONING.name]: ExternalGrokTextProviderOptionsWithoutReasoning
[GROK_4_3.name]: ExternalGrokTextProviderOptionsWithoutEffort
}As per coding guidelines, packages/typescript/ai-*/src/model-meta.ts: "Provide type safety per model by using model-specific provider options in adapter packages".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export type GrokChatModelProviderOptionsByName = { | |
| [K in (typeof GROK_CHAT_MODELS)[number]]: GrokProviderOptions | |
| [GROK_4_2.name]: ExternalGrokTextProviderOptionsWithoutEffort | |
| [GROK_4_2_NON_REASONING.name]: ExternalGrokTextProviderOptions | |
| [GROK_4_3.name]: ExternalGrokTextProviderOptionsWithoutEffort | |
| } | |
| export type GrokChatModelProviderOptionsByName = { | |
| [GROK_4_2.name]: ExternalGrokTextProviderOptionsWithoutEffort | |
| [GROK_4_2_NON_REASONING.name]: ExternalGrokTextProviderOptionsWithoutReasoning | |
| [GROK_4_3.name]: ExternalGrokTextProviderOptionsWithoutEffort | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/src/model-meta.ts` around lines 189 - 193, The
mapping for GROK_4_2_NON_REASONING in GrokChatModelProviderOptionsByName
incorrectly uses ExternalGrokTextProviderOptions (which exposes
modelOptions.reasoning) — create a new provider-options type that omits the
entire reasoning field (e.g., ExternalGrokTextProviderOptionsNoReasoning or a
version where modelOptions.reasoning is never/undefined) and replace
ExternalGrokTextProviderOptions for the GROK_4_2_NON_REASONING.name entry in
GrokChatModelProviderOptionsByName; ensure the new type mirrors
ExternalGrokTextProviderOptionsWithoutEffort except with reasoning removed so
the type accurately reflects the model’s no-reasoning contract.
| export function validateTextProviderOptions( | ||
| _options: InternalTextProviderOptions, | ||
| options: InternalTextProviderOptions, | ||
| ): void { | ||
| // Basic validation can be added here if needed | ||
| // For now, Grok API will handle validation | ||
| if (options.reasoning?.effort) { | ||
| const unsupportedReasoningEffortModels = new Set([ | ||
| 'grok-4.3', | ||
| 'grok-4.2', | ||
| ]) | ||
|
|
||
| if (unsupportedReasoningEffortModels.has(options.model)) { | ||
| throw new Error( | ||
| `${options.model} does not support modelOptions.reasoning.effort on xAI's Responses API. Remove reasoning.effort for this model and rely on the model's default reasoning behavior.`, | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
Reject reasoning options for grok-4-2-non-reasoning here as well.
packages/typescript/ai-grok/src/model-meta.ts:64-82 already models grok-4-2-non-reasoning without any reasoning capability, but this guard only blocks reasoning.effort on grok-4.2/grok-4.3. That means untyped/JS callers can still send modelOptions.reasoning and miss the clearer SDK-side error this function is meant to provide.
Suggested fix
export function validateTextProviderOptions(
options: InternalTextProviderOptions,
): void {
+ if (options.model === 'grok-4-2-non-reasoning' && options.reasoning) {
+ throw new Error(
+ `${options.model} does not support modelOptions.reasoning on xAI's Responses API.`,
+ )
+ }
+
if (options.reasoning?.effort) {
const unsupportedReasoningEffortModels = new Set([
'grok-4.3',
'grok-4.2',
])📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function validateTextProviderOptions( | |
| _options: InternalTextProviderOptions, | |
| options: InternalTextProviderOptions, | |
| ): void { | |
| // Basic validation can be added here if needed | |
| // For now, Grok API will handle validation | |
| if (options.reasoning?.effort) { | |
| const unsupportedReasoningEffortModels = new Set([ | |
| 'grok-4.3', | |
| 'grok-4.2', | |
| ]) | |
| if (unsupportedReasoningEffortModels.has(options.model)) { | |
| throw new Error( | |
| `${options.model} does not support modelOptions.reasoning.effort on xAI's Responses API. Remove reasoning.effort for this model and rely on the model's default reasoning behavior.`, | |
| ) | |
| } | |
| } | |
| export function validateTextProviderOptions( | |
| options: InternalTextProviderOptions, | |
| ): void { | |
| if (options.model === 'grok-4-2-non-reasoning' && options.reasoning) { | |
| throw new Error( | |
| `${options.model} does not support modelOptions.reasoning on xAI's Responses API.`, | |
| ) | |
| } | |
| if (options.reasoning?.effort) { | |
| const unsupportedReasoningEffortModels = new Set([ | |
| 'grok-4.3', | |
| 'grok-4.2', | |
| ]) | |
| if (unsupportedReasoningEffortModels.has(options.model)) { | |
| throw new Error( | |
| `${options.model} does not support modelOptions.reasoning.effort on xAI's Responses API. Remove reasoning.effort for this model and rely on the model's default reasoning behavior.`, | |
| ) | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/src/text/text-provider-options.ts` around lines
167 - 181, The validation in validateTextProviderOptions currently rejects
reasoning.effort for models 'grok-4.3' and 'grok-4.2' but misses
'grok-4-2-non-reasoning'; update the guard in validateTextProviderOptions to
also reject any reasoning options for 'grok-4-2-non-reasoning' (e.g., add that
string to the unsupportedReasoningEffortModels set or include it in the model
check) so callers get the same clear SDK error when they pass
modelOptions.reasoning for that model.
| const metadata = tool.metadata as FileSearchToolConfig | ||
| validateVectorStoreIds(metadata.vector_store_ids) |
There was a problem hiding this comment.
Guard tool.metadata before dereferencing vector_store_ids.
Both converter paths dereference metadata.vector_store_ids immediately after a cast. If tool.metadata is missing/invalid, this throws a TypeError before your intended validation runs. Add a boundary check (or schema parse) so failures are explicit and deterministic.
Also applies to: 49-50
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/src/tools/file-search-tool.ts` around lines 35 -
36, Guard against missing or invalid tool.metadata before casting and
dereferencing vector_store_ids: in the FileSearchTool conversion code where you
reference metadata (currently casting to FileSearchToolConfig and calling
validateVectorStoreIds(metadata.vector_store_ids)), first check that
tool.metadata exists and matches expected shape (or run a schema
parse/validator) and return or throw a clear error if it’s absent/invalid; apply
the same guard to the other conversion path around the second usage at the block
that references metadata and validateVectorStoreIds on lines near the second
occurrence so validateVectorStoreIds always receives a defined array.
| export function convertXSearchToolToAdapterFormat( | ||
| tool: Tool, | ||
| ): XSearchToolConfig { | ||
| const metadata = tool.metadata as XSearchToolConfig | ||
| return metadata | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
fd -t f "x-search-tool.ts" packages/Repository: TanStack/ai
Length of output: 110
🏁 Script executed:
cat -n packages/typescript/ai-grok/src/tools/x-search-tool.tsRepository: TanStack/ai
Length of output: 1295
🏁 Script executed:
rg -i "zod" packages/typescript/ --type ts | head -20Repository: TanStack/ai
Length of output: 2274
🏁 Script executed:
rg -i "XSearchToolConfig" packages/typescript/ --type tsRepository: TanStack/ai
Length of output: 942
🏁 Script executed:
rg -A 10 "convertToAdapterFormat|schema\.parse|z\.object" packages/typescript/ai-grok/src/ --type ts | head -40Repository: TanStack/ai
Length of output: 37
🏁 Script executed:
find packages/typescript -name "*tool*.ts" -path "*/src/*" -type f | xargs grep -l "z\." | head -10Repository: TanStack/ai
Length of output: 438
🏁 Script executed:
rg "Tool\>" packages/typescript/ai-grok/src/tools/ --type ts -B 2 -A 5Repository: TanStack/ai
Length of output: 132
🏁 Script executed:
fd -t f "*tools*" packages/typescript/ai-grok/src/ --type tsRepository: TanStack/ai
Length of output: 225
🏁 Script executed:
cat -n packages/typescript/ai-gemini/src/tools/tool-converter.tsRepository: TanStack/ai
Length of output: 3947
🏁 Script executed:
cat -n packages/typescript/ai-groq/src/tools/function-tool.tsRepository: TanStack/ai
Length of output: 1685
🏁 Script executed:
cat -n packages/typescript/ai/src/activities/chat/tools/tool-definition.ts | head -60Repository: TanStack/ai
Length of output: 1961
🏁 Script executed:
rg "packages/typescript/ai-grok" packages/typescript/ai-grok/package.json -A 10Repository: TanStack/ai
Length of output: 326
🏁 Script executed:
cat packages/typescript/ai-grok/package.jsonRepository: TanStack/ai
Length of output: 1400
🏁 Script executed:
rg "import.*zod|from.*zod" packages/typescript/ai-grok/src/ --type tsRepository: TanStack/ai
Length of output: 37
🏁 Script executed:
cat -n packages/typescript/ai-grok/src/tools/x-search-tool.ts | head -35Repository: TanStack/ai
Length of output: 1295
🏁 Script executed:
find packages/typescript/ai-grok/src -name "*.ts" -type f | xargs cat | grep -i "zod\|schema\|validate"Repository: TanStack/ai
Length of output: 3005
🏁 Script executed:
rg "tool\.metadata|metadata as" packages/typescript/ --type ts | grep -v test | grep -v ".test.ts"Repository: TanStack/ai
Length of output: 4607
🏁 Script executed:
cat packages/typescript/ai-grok/src/create-grok-provider.ts | head -80Repository: TanStack/ai
Length of output: 143
🏁 Script executed:
find packages/typescript/ai-grok/src -name "*.ts" -type f -exec grep -l "z\\.object\\|z\\.parse\\|ZodSchema" {} \;Repository: TanStack/ai
Length of output: 37
🏁 Script executed:
rg "z\\.object|z\\.parse" packages/typescript/ai-grok/ --type tsRepository: TanStack/ai
Length of output: 37
Add Zod schema validation for tool.metadata in the conversion function.
The metadata cast on line 23 lacks runtime validation. Per the coding guideline for this package ("Use Zod for schema validation and tool definition across the library"), create a Zod schema for XSearchToolConfig and parse the metadata before returning it. This ensures invalid or malformed metadata is caught early with a clear error message rather than failing silently downstream.
import { z } from 'zod'
const xSearchToolConfigSchema = z.object({
type: z.literal('x_search'),
allowed_x_handles: z.array(z.string()).optional(),
excluded_x_handles: z.array(z.string()).optional(),
from_date: z.string().optional(),
to_date: z.string().optional(),
enable_image_understanding: z.boolean().optional(),
enable_video_understanding: z.boolean().optional(),
})
export function convertXSearchToolToAdapterFormat(
tool: Tool,
): XSearchToolConfig {
return xSearchToolConfigSchema.parse(tool.metadata)
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/typescript/ai-grok/src/tools/x-search-tool.ts` around lines 20 - 25,
The conversion function convertXSearchToolToAdapterFormat is currently casting
tool.metadata to XSearchToolConfig without runtime validation; add a Zod schema
(e.g., xSearchToolConfigSchema using z.object with the fields type:
z.literal('x_search'), allowed_x_handles/excluded_x_handles as optional string
arrays, from_date/to_date as optional strings, and
enable_image_understanding/enable_video_understanding as optional booleans) and
replace the unsafe cast with xSearchToolConfigSchema.parse(tool.metadata) so
invalid metadata throws a clear error at parse time.
| { | ||
| provider: 'grok', | ||
| model: 'grok-4', | ||
| label: 'Grok - Grok 4 - slow thinking', | ||
| model: 'grok-4.3', | ||
| label: 'Grok - Grok 4.3', | ||
| }, | ||
| { | ||
| provider: 'grok', | ||
| model: 'grok-4-fast-non-reasoning', | ||
| label: 'Grok - Grok 4 Fast', | ||
| model: 'grok-4.2', | ||
| label: 'Grok - Grok 4.2', | ||
| }, | ||
| { | ||
| provider: 'grok', | ||
| model: 'grok-3', | ||
| label: 'Grok - Grok 3', | ||
| }, | ||
| { | ||
| provider: 'grok', | ||
| model: 'grok-3-mini', | ||
| label: 'Grok - Grok 3 Mini', | ||
| model: 'grok-4-2-non-reasoning', | ||
| label: 'Grok - Grok 4.2 Non-Reasoning', | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
xAI Responses API grok model identifiers grok-4.2 grok-4-2-non-reasoning
💡 Result:
No official xAI documentation lists exact model identifiers "grok-4.2" or "grok-4-2-non-reasoning" in the Responses API or Inference API [1][2][3][4]. Official sources show models like grok-4-0709, grok-4.3, grok-4.20-reasoning, grok-4.20-non-reasoning, grok-4-fast-non-reasoning [1][2][3][4]. "grok-4.2" refers to Grok 4.2 (also called Grok 4.20), a model with reasoning and non-reasoning variants available via xAI API, but identifiers use "4.20" format like grok-4.20-non-reasoning [5][6][3][7][8][9]. Third-party providers (e.g., Oracle OCI) use prefixed names like xai.grok-4.20-0309-non-reasoning and mention grok-4-2-non-reasoning-tokens-per-minute-count in metrics, suggesting possible internal or variant naming [6][8]. GitHub discussions reference experimental grok-4.20-experimental-beta-0304-non-reasoning, close but not exact [5]. To get current model list, query GET /v1/models endpoint with your API key [1]. xAI's Responses API (/v1/responses) supports these models for stateful chat with reasoning traces on supported variants [10][11].
Citations:
- 1: https://docs.x.ai/developers/rest-api-reference/inference/models
- 2: https://docs.x.ai/docs/models?cluster=us-west-1
- 3: https://x.ai/api
- 4: https://docs.x.ai/docs/guides/reasoning
- 5: Support for the new Grok 4.2 Multi Model openclaw/openclaw#38204
- 6: https://docs.oracle.com/en-us/iaas/Content/generative-ai/xai-grok-4-20.htm
- 7: https://ai.azure.com/catalog/models/grok-4-20-reasoning
- 8: https://docs.oracle.com/iaas/Content/generative-ai/xai-grok-4-20.htm
- 9: https://grokipedia.com/page/Grok_42
- 10: https://docs.x.ai/docs/guides/chat
- 11: https://docs.x.ai/developers/rest-api-reference/inference/chat
Fix model identifiers to match xAI API specifications.
grok-4.2 and grok-4-2-non-reasoning are not supported by xAI's official API. Official documentation shows:
grok-4.3✓ (correct)grok-4.2should begrok-4.20or a variant likegrok-4.20-reasoninggrok-4-2-non-reasoningshould begrok-4.20-non-reasoning(with dots, not dashes)
Verify these identifiers against the GET /v1/models endpoint response before merging.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@testing/panel/src/lib/model-selection.ts` around lines 83 - 97, Update the
model identifiers in the model selection list entries for provider 'grok' so
they match xAI API naming: replace 'grok-4.2' with the correct 'grok-4.20' (or
'grok-4.20-reasoning' if you intend the reasoning variant) and replace
'grok-4-2-non-reasoning' with 'grok-4.20-non-reasoning'; locate the objects in
the model selection array in model-selection.ts (the entries where provider ===
'grok' and model === 'grok-4.3'/'grok-4.2'/'grok-4-2-non-reasoning') and update
the model string and label accordingly, then validate the final identifiers by
checking the API response from GET /v1/models before merging.
🎯 Changes
This updates the Grok adapter to use xAI’s Responses API instead of the older Chat Completions API.
The main motivation is to support the newer Grok model/API surface more cleanly, including reasoning, structured output, and xAI server-side tools.
What changed:
/v1/responsesstore: falseandinclude: ['reasoning.encrypted_content']REASONING_*) alongside the existing AG-UI stream eventstext.format@tanstack/ai-grok/tools✅ Checklist
I have followed the steps in the Contributing guide.
I have tested this code locally with
pnpm run test:pr.Tested locally:
pnpm --filter @tanstack/ai-grok test:eslintpassedpackages/typescript/ai-grok/src/realtime/adapter.tspnpm --filter @tanstack/ai-grok test:typespassedpnpm --filter @tanstack/ai-grok test:libpassedpnpm --filter @tanstack/ai-e2e test:e2epassedpnpm test:pr; it got through the affected tasks but failed on@tanstack/ai-isolate-node:test:libbecause my local machine is missing the nativeisolated-vmbuild for my Node/macOS setup. Running that target directly reproduced the same environment issue.🚀 Release Impact
This change affects published code, and I have generated a changeset.
This change is docs/CI/dev-only (no release).
Summary by CodeRabbit
Release Notes
New Features
Breaking Changes
maxTokensandtopPare now top-level optionsfrequency_penalty,presence_penalty,stopgrok-imagine-imageDocumentation