Skip to content

Update Grok adapter for Responses API#531

Open
Jaaneek wants to merge 1 commit intoTanStack:mainfrom
Jaaneek:grok-responses-api-encrypted-reasoning
Open

Update Grok adapter for Responses API#531
Jaaneek wants to merge 1 commit intoTanStack:mainfrom
Jaaneek:grok-responses-api-encrypted-reasoning

Conversation

@Jaaneek
Copy link
Copy Markdown

@Jaaneek Jaaneek commented May 5, 2026

🎯 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:

  • Switched Grok text generation to /v1/responses
  • Added encrypted reasoning support by default with store: false and include: ['reasoning.encrypted_content']
  • Added streamed reasoning events (REASONING_*) alongside the existing AG-UI stream events
  • Updated structured output to use Responses API text.format
  • Updated tool conversion to use the Responses API function-tool shape
  • Added xAI server-side tools under @tanstack/ai-grok/tools
    • web search
    • X search
    • code execution / code interpreter
    • file search / collections search
    • MCP
  • Refreshed Grok model metadata, docs, examples, panel/e2e config, and code-mode references
  • Added live-test scripts for manually testing Grok streaming, tools, structured output, reasoning, and multi-turn flows

✅ Checklist

  • I have followed the steps in the Contributing guide.

  • I have tested this code locally with pnpm run test:pr.

Tested locally:

  • Built an app using these changes and tested the Grok Responses API flow end-to-end
  • pnpm --filter @tanstack/ai-grok test:eslint passed
    • There is one existing warning in packages/typescript/ai-grok/src/realtime/adapter.ts
  • pnpm --filter @tanstack/ai-grok test:types passed
  • pnpm --filter @tanstack/ai-grok test:lib passed
  • pnpm --filter @tanstack/ai-e2e test:e2e passed
  • I also ran pnpm test:pr; it got through the affected tasks but failed on @tanstack/ai-isolate-node:test:lib because my local machine is missing the native isolated-vm build 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

    • Grok adapter now supports encrypted reasoning by default with new reasoning streaming events
    • Added server-side tool support: web search, X search, code execution, file search, collections search, and MCP
    • Updated to latest Grok models: 4.3, 4.2, and 4.2 non-reasoning variants
  • Breaking Changes

    • Provider options structure updated; maxTokens and topP are now top-level options
    • Removed unsupported parameters: frequency_penalty, presence_penalty, stop
    • Updated image model identifier to grok-imagine-image
  • Documentation

    • Grok adapter docs updated with Responses API details, server-side tools guidance, and model examples

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 5, 2026

📝 Walkthrough

Walkthrough

Grok 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 text.format instead of response_format, and provides stateless encrypted reasoning via store: false with include: ['reasoning.encrypted_content'] by default. Model support narrowed to grok-4.3, grok-4.2, and grok-4-2-non-reasoning. Provider options surface restructured to match Responses API specifications.


Changes

Grok Responses API Migration

Layer / File(s) Summary
Model & Type Definitions
packages/typescript/ai-grok/src/model-meta.ts, packages/typescript/ai-grok/src/text/text-provider-options.ts, packages/typescript/ai-grok/src/image/image-provider-options.ts
Supported models narrowed to grok-4.3, grok-4.2, grok-4-2-non-reasoning (chat) and grok-imagine-image (image). GrokProviderOptions changed from explicit interface to type alias mirroring ExternalGrokTextProviderOptions. Base options expanded to include Responses-API controls (include, store, truncation, previous_response_id). New reasoning, structured output, tools, streaming, and metadata option types added. validateTextProviderOptions now enforces reasoning.effort unavailability for certain models.
Tool System
packages/typescript/ai-grok/src/tools/*, packages/typescript/ai-grok/src/tools/index.ts, packages/typescript/ai-grok/src/tools/tool-converter.ts
Function tools reshaped to Responses-API flat format ({ type: 'function', name, description, parameters, strict }). New provider-native tool modules added: code-execution, code-interpreter, file-search, collections-search, mcp, web-search, x-search. Tool converter updated to dispatch on metadata.type with per-tool format adapters. convertToolsToProviderFormat return type broadened to Array<GrokTool> union.
Core Adapter Implementation
packages/typescript/ai-grok/src/adapters/text.ts
Requests routed to client.responses.create instead of client.chat.completions.create. Streaming now processes Responses-API stream events, emitting AG-UI REASONING_* events alongside TEXT_MESSAGE_* and TOOL_CALL_* events. Structured output switched from response_format to text.format with JSON-schema strict mode. Message conversion refactored to build Responses.ResponseInput format. Encrypted reasoning defaults (store: false, include: ['reasoning.encrypted_content']) applied unless overridden via modelOptions.
Adapter Factories
packages/typescript/ai-grok/src/adapters/image.ts, packages/typescript/ai-grok/src/adapters/summarize.ts
Documentation updated to reference grok-imagine-image (image) and grok-4.3/grok-4.2 (summarize). No runtime logic changes.
Live Integration Tests
packages/typescript/ai-grok/live-tests/helpers.ts, packages/typescript/ai-grok/live-tests/*.ts, packages/typescript/ai-grok/live-tests/package.json, packages/typescript/ai-grok/live-tests/README.md
Comprehensive test suite added covering streaming, tool-calling (function/code-exec/web-search/x-search/file-search/mcp), structured output, reasoning encryption, and multi-turn conversations via streamChat helper. Live tests validate Responses-API streaming event sequences, tool lifecycle events, and encrypted reasoning content retrieval.
Adapter Tests
packages/typescript/ai-grok/tests/grok-adapter.test.ts, packages/typescript/ai-grok/tests/tools-per-model-type-safety.test.ts
Test suite reworked from Chat Completions to Responses-API assertions. Validates request body targeting responses.create, message/input conversion, encrypted-reasoning defaults and overrides, early reasoning.effort validation, Responses-API tool encoding, and AG-UI event emission (REASONING_, TEXT_MESSAGE_, TOOL_CALL_*) from Responses stream deltas. Type-safety tests added for per-model tool gating.
Main Package Documentation
packages/typescript/ai-grok/README.md, docs/adapters/grok.md
Rewrote adapter description to emphasize Responses API, encrypted reasoning defaults, server-side tool support. Added "Chat Route Example" with SSE streaming, "xAI Server-Side Tools" section with examples (web/x search, code execution, file/collections search, MCP), and "Model Options" guidance on store/include overrides. Updated "Supported Models" list and "Features" section. Removed Chat Completions-focused limitations.
Example & Test App Updates
examples/ts-react-chat/src/lib/model-selection.ts, examples/ts-react-chat/src/routes/api.tanchat.ts, examples/ts-react-chat/src/routes/generations.structured-output.tsx, testing/e2e/src/lib/providers.ts, testing/e2e/src/routes/api.summarize.ts, testing/e2e/src/lib/media-providers.ts, testing/panel/src/lib/model-selection.ts, testing/panel/src/routes/*.ts, testing/panel/tests/vendor-config.ts
Model references updated: replaced grok-3, grok-3-mini, grok-4, grok-4-fast-non-reasoning with grok-4.3, grok-4.2, grok-4-2-non-reasoning. Image model updated from grok-2-image-1212 to grok-imagine-image. Default models and route-level configurations updated to use newer model identifiers. summarize route refactored to use adapters directly instead of createSummarizeOptions wrapper.
Code-Mode Evaluation & Configuration
packages/typescript/ai-code-mode/models-eval/eval-config.ts, packages/typescript/ai-code-mode/models-eval/results.json, packages/typescript/ai-code-mode/models-eval/run-eval.ts, packages/typescript/ai-code-mode/README.md, docs/code-mode/code-mode.md
Model evaluation config updated to use grok-4-2-non-reasoning instead of grok-4-1-fast-non-reasoning. Results JSON updated with new model metadata and metrics. Documentation tables and skill references updated to reflect new supported models.
Utilities & Configuration
packages/typescript/ai/skills/ai-core/adapter-configuration/SKILL.md, packages/typescript/ai/skills/ai-core/adapter-configuration/references/grok-adapter.md, .changeset/grok-responses-api-encrypted-reasoning.md, knip.json, scripts/sync-provider-models.ts
Adapter configuration skill and reference docs updated to new model identifiers. Changeset documented breaking option changes and new Responses-API behavior. Live-tests directory added to knip.json ignores. Inline comment in sync script updated.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


Poem

🐰 Hops of joy! The Grok adapter dances,
From Chat Completions to Responses it prances,
Encrypted reasoning whispers through the stream,
New tools and flat schemas fulfill the dream,
Models updated, bright grok-4.3 leads the way,
As TanStack AI flows through every call today!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch grok-responses-api-encrypted-reasoning

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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 win

Stale 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 win

Update 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 win

Add 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() or fileSearchTool() 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 win

Capability-gating uses the passed model, but execution targets a hardcoded one

supportsClientToolCalling(model) on Line 31 correctly gates tool-test.ts etc. based on the model passed to the runner. However, those scripts all hardcode model: 'grok-4.3' internally and ignore the --model flag, so the actual API call always targets grok-4.3 regardless. Similarly, when the runner loops over multiple models (e.g., grok-4.2 and grok-4.3), the streaming/multi-turn/tool tests run twice but both times against grok-4.3.

The fix is for each script to call resolveModel() from helpers (as builtin-tools.ts already does). See the comment on multi-turn.ts for 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 win

Hardcoded model ignores --model flag from run.ts

model: 'grok-4.3' is hardcoded, so when run.ts invokes this script with --model grok-4.2 (or any other model), the test still runs against grok-4.3. This makes the runner's multi-model parameterization a no-op for this script. builtin-tools.ts already does this correctly with resolveModel() 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, and streaming.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 value

Default description to a meaningful string when server_description is missing.

description: toolData.server_description || '' produces an empty Tool.description whenever the caller doesn't pass server_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 win

Use GROK_CHAT_MODELS constant instead of hardcoded model list

The hardcoded list ['grok-4.2', 'grok-4-2-non-reasoning', 'grok-4.3'] duplicates the canonical GROK_CHAT_MODELS constant 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 win

Remove the double-cast as unknown as AsyncIterable<StreamChunk> by making the return type explicit

The chat() function from @tanstack/ai returns an async iterable when stream: true is passed, but TypeScript cannot narrow the return type to AsyncIterable<StreamChunk> because the stream parameter 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

📥 Commits

Reviewing files that changed from the base of the PR and between b0354ad and 30200f8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (56)
  • .changeset/grok-responses-api-encrypted-reasoning.md
  • docs/adapters/grok.md
  • docs/code-mode/code-mode.md
  • docs/community-adapters/cencori.md
  • docs/community-adapters/cloudflare.md
  • examples/ts-react-chat/src/lib/model-selection.ts
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • examples/ts-react-chat/src/routes/generations.structured-output.tsx
  • knip.json
  • packages/typescript/ai-code-mode/README.md
  • packages/typescript/ai-code-mode/models-eval/eval-config.ts
  • packages/typescript/ai-code-mode/models-eval/results.json
  • packages/typescript/ai-code-mode/models-eval/run-eval.ts
  • packages/typescript/ai-grok/README.md
  • packages/typescript/ai-grok/live-tests/README.md
  • packages/typescript/ai-grok/live-tests/builtin-tools.ts
  • packages/typescript/ai-grok/live-tests/helpers.ts
  • packages/typescript/ai-grok/live-tests/multi-turn.ts
  • packages/typescript/ai-grok/live-tests/package.json
  • packages/typescript/ai-grok/live-tests/reasoning.ts
  • packages/typescript/ai-grok/live-tests/run.ts
  • packages/typescript/ai-grok/live-tests/server-tools.ts
  • packages/typescript/ai-grok/live-tests/streaming.ts
  • packages/typescript/ai-grok/live-tests/structured-output.ts
  • packages/typescript/ai-grok/live-tests/tool-test-empty-object.ts
  • packages/typescript/ai-grok/live-tests/tool-test-optional.ts
  • packages/typescript/ai-grok/live-tests/tool-test.ts
  • packages/typescript/ai-grok/src/adapters/image.ts
  • packages/typescript/ai-grok/src/adapters/summarize.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
  • packages/typescript/ai-grok/src/image/image-provider-options.ts
  • packages/typescript/ai-grok/src/model-meta.ts
  • packages/typescript/ai-grok/src/text/text-provider-options.ts
  • packages/typescript/ai-grok/src/tools/code-execution-tool.ts
  • packages/typescript/ai-grok/src/tools/file-search-tool.ts
  • packages/typescript/ai-grok/src/tools/function-tool.ts
  • packages/typescript/ai-grok/src/tools/index.ts
  • packages/typescript/ai-grok/src/tools/mcp-tool.ts
  • packages/typescript/ai-grok/src/tools/tool-converter.ts
  • packages/typescript/ai-grok/src/tools/web-search-tool.ts
  • packages/typescript/ai-grok/src/tools/x-search-tool.ts
  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/tests/tools-per-model-type-safety.test.ts
  • packages/typescript/ai/skills/ai-core/adapter-configuration/SKILL.md
  • packages/typescript/ai/skills/ai-core/adapter-configuration/references/grok-adapter.md
  • scripts/sync-provider-models.ts
  • testing/e2e/src/lib/media-providers.ts
  • testing/e2e/src/lib/providers.ts
  • testing/e2e/src/routes/api.summarize.ts
  • testing/panel/src/lib/model-selection.ts
  • testing/panel/src/routes/api.chat.ts
  • testing/panel/src/routes/api.structured.ts
  • testing/panel/src/routes/api.summarize.ts
  • testing/panel/src/routes/structured.tsx
  • testing/panel/src/routes/summarize.tsx
  • testing/panel/tests/vendor-config.ts

Comment thread docs/adapters/grok.md
Comment on lines 346 to 349
- [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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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 tools

As 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.

Suggested change
- [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.

Comment on lines +23 to +27
envContent.split('\n').forEach((line) => {
const match = line.match(/^([^=]+)=(.*)$/)
if (match) {
process.env[match[1]!.trim()] = match[2]!.trim()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

.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.

Comment on lines +9 to +13
```
XAI_API_KEY=xai-...
# Optional, only needed for file_search / collections_search live tests
XAI_VECTOR_STORE_ID=vs_...
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment on lines +49 to +53
try {
execFileSync('pnpm', ['exec', 'tsx', script, '--model', model], {
stdio: 'inherit',
env: process.env,
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 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 -80

Repository: 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.json

Repository: 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 yml

Repository: 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 -5

Repository: 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/null

Repository: 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 md

Repository: 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 -5

Repository: 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.ts

Repository: 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 -50

Repository: 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.

Suggested change
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).

Comment on lines +58 to +63
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)}`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment on lines 189 to 193
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
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment on lines 167 to +181
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.`,
)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +35 to +36
const metadata = tool.metadata as FileSearchToolConfig
validateVectorStoreIds(metadata.vector_store_ids)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +20 to +25
export function convertXSearchToolToAdapterFormat(
tool: Tool,
): XSearchToolConfig {
const metadata = tool.metadata as XSearchToolConfig
return metadata
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ 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.ts

Repository: TanStack/ai

Length of output: 1295


🏁 Script executed:

rg -i "zod" packages/typescript/ --type ts | head -20

Repository: TanStack/ai

Length of output: 2274


🏁 Script executed:

rg -i "XSearchToolConfig" packages/typescript/ --type ts

Repository: TanStack/ai

Length of output: 942


🏁 Script executed:

rg -A 10 "convertToAdapterFormat|schema\.parse|z\.object" packages/typescript/ai-grok/src/ --type ts | head -40

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

find packages/typescript -name "*tool*.ts" -path "*/src/*" -type f | xargs grep -l "z\." | head -10

Repository: TanStack/ai

Length of output: 438


🏁 Script executed:

rg "Tool\>" packages/typescript/ai-grok/src/tools/ --type ts -B 2 -A 5

Repository: TanStack/ai

Length of output: 132


🏁 Script executed:

fd -t f "*tools*" packages/typescript/ai-grok/src/ --type ts

Repository: TanStack/ai

Length of output: 225


🏁 Script executed:

cat -n packages/typescript/ai-gemini/src/tools/tool-converter.ts

Repository: TanStack/ai

Length of output: 3947


🏁 Script executed:

cat -n packages/typescript/ai-groq/src/tools/function-tool.ts

Repository: TanStack/ai

Length of output: 1685


🏁 Script executed:

cat -n packages/typescript/ai/src/activities/chat/tools/tool-definition.ts | head -60

Repository: TanStack/ai

Length of output: 1961


🏁 Script executed:

rg "packages/typescript/ai-grok" packages/typescript/ai-grok/package.json -A 10

Repository: TanStack/ai

Length of output: 326


🏁 Script executed:

cat packages/typescript/ai-grok/package.json

Repository: TanStack/ai

Length of output: 1400


🏁 Script executed:

rg "import.*zod|from.*zod" packages/typescript/ai-grok/src/ --type ts

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

cat -n packages/typescript/ai-grok/src/tools/x-search-tool.ts | head -35

Repository: 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 -80

Repository: 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 ts

Repository: 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.

Comment on lines 83 to 97
{
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',
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 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:


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.2 should be grok-4.20 or a variant like grok-4.20-reasoning
  • grok-4-2-non-reasoning should be grok-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.

@tombeckenham
Copy link
Copy Markdown
Contributor

Hi @Jaaneek , this is great. I was just about to log an issue around updating grok. However... we're just about to merge PR #409 which changes a tonne of grok. We're basing it off a new OpenAI compatible base. Can you rebase your branch to that one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants