Skip to content

Conversation

@jherr
Copy link
Contributor

@jherr jherr commented Dec 23, 2025

🎯 Changes

Adding Grok to the adapter set.

βœ… Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

πŸš€ 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

  • New Features

    • Added Grok (xAI) adapter with support for chat completions, image generation, and text summarization.
    • Supports multiple Grok models including Grok 4.1, 4, 3, and 3-mini variants.
    • Enables streaming responses and structured output capabilities.
    • Integrated into example applications for immediate use.
  • Documentation

    • Added comprehensive Grok adapter documentation covering installation, setup, configuration, and usage examples.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Important

Review skipped

Review was skipped due to path filters

β›” Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

πŸ“ Walkthrough

Walkthrough

This PR introduces comprehensive support for xAI's Grok models by adding a new @tanstack/ai-grok package to the TanStack AI framework. The package provides three adapters (text/chat, summarization, image generation), complete with model metadata, provider configuration, utility helpers for schema transformation and API client setup, and integration examples across documentation, demo apps, and test suites.

Changes

Cohort / File(s) Summary
Grok Adapter Implementations
packages/typescript/ai-grok/src/adapters/text.ts, image.ts, summarize.ts
Introduces GrokTextAdapter, GrokImageAdapter, and GrokSummarizeAdapter classes extending base adapters. Text adapter handles streaming chat and structured output with message/schema conversion; image adapter validates and transforms image generation requests; summarize adapter wraps text adapter with custom prompting. Factory functions (createGrok*, grok*) provide explicit API key or environment-based initialization.
Model & Metadata System
packages/typescript/ai-grok/src/model-meta.ts, message-types.ts
Defines 10 Grok model constants (Grok 4.1, 4, 3, 2) with detailed metadata (context window, pricing, modality support), exports GROK_CHAT_MODELS and GROK_IMAGE_MODELS arrays, and provides conditional types for resolving model-specific provider options and input modalities. Introduces modality metadata interfaces (image, audio, video, document, text).
Provider Options & Validation
packages/typescript/ai-grok/src/text/text-provider-options.ts, image/image-provider-options.ts
Defines typed configuration interfaces (GrokTextProviderOptions, GrokImageProviderOptions) with fields like temperature, quality, response format, and image sizes. Exports validation functions for image size, number of images, and prompt length constraints.
Utilities & Schema Transformation
packages/typescript/ai-grok/src/utils/client.ts, schema-converter.ts, tools/function-tool.ts, tools/tool-converter.ts
Provides createGrokClient (OpenAI SDK wrapper), getGrokApiKeyFromEnv (XAI_API_KEY retrieval), generateId (unique ID generation), makeGrokStructuredOutputCompatible (recursive schema transformation for JSON mode), transformNullsToUndefined (null→undefined conversion), and convertToolsToProviderFormat (tool standardization).
Package Infrastructure
packages/typescript/ai-grok/package.json, tsconfig.json, vite.config.ts, src/index.ts
Package manifest with peerDeps on @tanstack/ai and zod, OpenAI SDK dependency, TypeScript/Vite build config, and central barrel export consolidating all adapters, types, and utilities for tree-shaking.
Tests & Validation
packages/typescript/ai-grok/tests/grok-adapter.test.ts
Unit tests verifying explicit API key usage, environment variable detection, error handling for missing credentials, and adapter property correctness.
Documentation & Release Notes
.changeset/add-grok-adapter.md, docs/adapters/grok.md, docs/guides/migration.md
Adds comprehensive Grok adapter documentation covering installation, usage patterns, model options, and API reference; creates changeset for minor version bump; trailing whitespace cleanup in migration guide.
Example Application Integration
examples/ts-react-chat/package.json, src/lib/model-selection.ts, src/routes/api.tanchat.ts
Adds @tanstack/ai-grok workspace dependency, extends Provider type to include 'grok', introduces Grok model options (grok-3, grok-3-mini, grok-2-vision-1212), and registers Grok in chat route with default model fallback.
Testing & Smoke Test Integration
packages/typescript/smoke-tests/adapters/package.json, src/adapters/index.ts, testing/panel/package.json, src/lib/model-selection.ts, src/routes/api.chat.ts
Adds Grok workspace dependency to smoke tests and panel, extends adapter registry with Grok entry (id: 'grok', envKey: 'XAI_API_KEY'), adds Grok models to model selection, and registers Grok in panel chat route with grokText adapter.

Sequence Diagrams

sequenceDiagram
    actor User
    participant App as Chat Application
    participant TextAdapter as GrokTextAdapter
    participant Client as OpenAI SDK Client
    participant GrokAPI as xAI Grok API

    User->>App: Send chat message
    App->>TextAdapter: chatStream(options)
    TextAdapter->>TextAdapter: mapTextOptionsToGrok()
    TextAdapter->>TextAdapter: convertMessageToGrok()
    TextAdapter->>Client: createChatCompletion(stream: true)
    Client->>GrokAPI: POST /chat/completions
    
    rect rgb(200, 220, 240)
    Note over GrokAPI,TextAdapter: Streaming Response Loop
    GrokAPI-->>Client: stream chunk (content/tool_call/done)
    Client-->>TextAdapter: raw chunk
    TextAdapter->>TextAdapter: processGrokStreamChunks()
    TextAdapter-->>App: emit StreamChunk (content/tool_call)
    end
    
    App-->>User: Display streamed response
Loading
sequenceDiagram
    actor User
    participant App as Image Generation App
    participant ImageAdapter as GrokImageAdapter
    participant Validator as Validation Layer
    participant Client as OpenAI SDK Client
    participant GrokAPI as xAI Grok API

    User->>App: Request image generation
    App->>ImageAdapter: generateImages(options)
    
    rect rgb(240, 220, 200)
    Note over Validator,ImageAdapter: Input Validation
    ImageAdapter->>Validator: validatePrompt()
    ImageAdapter->>Validator: validateImageSize()
    ImageAdapter->>Validator: validateNumberOfImages()
    Validator-->>ImageAdapter: βœ“ Valid
    end
    
    ImageAdapter->>ImageAdapter: buildRequest()
    ImageAdapter->>Client: images.generate()
    Client->>GrokAPI: POST /images/generations
    GrokAPI-->>Client: image URLs + usage tokens
    Client-->>ImageAdapter: response
    ImageAdapter->>ImageAdapter: transformResponse()
    ImageAdapter-->>App: GeneratedImage[] + usage
    App-->>User: Display generated images
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

This PR introduces a substantial new package with interconnected adapters, dense type system logic for model metadata and conditional type resolution, complex schema transformation utilities, and integration across multiple example applications. The effort is elevated by the density of new type definitions, streaming logic in the text adapter, recursive schema conversion, and the need to verify consistency across the adapter hierarchy and factory function patterns.

Possibly related PRs

Suggested reviewers

  • AlemTuzlak
  • harry-whorlow

Poem

🐰 A hop through Grok's door,
Models four and more to explore,
Chat and images bloom,
Tree-shaken in the room,
xAI's power, forever in store! πŸš€

Pre-merge checks and finishing touches

βœ… Passed checks (3 passed)
Check name Status Explanation
Title check βœ… Passed The title 'feat: add Grok support' clearly and concisely summarizes the main changeβ€”adding Grok adapter support to the codebase.
Description check βœ… Passed The PR description includes the required sections (Changes, Checklist, Release Impact) and notes that a changeset was created, satisfying the template requirements.
Docstring Coverage βœ… Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Dec 23, 2025

View your CI Pipeline Execution β†— for commit 12fa430

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... βœ… Succeeded 2m 50s View β†—
nx run-many --targets=build --exclude=examples/** βœ… Succeeded 1m 6s View β†—

☁️ Nx Cloud last updated this comment at 2026-01-07 09:22:04 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 23, 2025

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@183

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@183

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@183

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@183

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@183

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@183

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@183

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@183

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@183

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@183

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@183

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@183

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@183

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@183

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@183

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@183

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@183

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@183

commit: 12fa430

@jherr jherr marked this pull request as ready for review January 7, 2026 01:16
@jherr jherr requested a review from a team January 7, 2026 01:16
@jherr jherr changed the title [WIP] feat: add Grok support feat: add Grok support Jan 7, 2026
Copy link
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: 8

πŸ€– Fix all issues with AI agents
In @packages/typescript/ai-grok/package.json:
- Around line 49-51: The peer dependency for "@tanstack/ai" in package.json uses
the incorrect specifier "workspace:^"; update the version specifier to
"workspace:*" so it follows monorepo convention. Locate the "peerDependencies"
block in packages/typescript/ai-grok/package.json and replace the
"@tanstack/ai": "workspace:^" entry with "@tanstack/ai": "workspace:*".
- Around line 45-48: The package.json is missing vitest in devDependencies even
though the "test:lib" script runs "vitest run"; add "vitest": "^4.0.14" to the
"devDependencies" object in packages/typescript/ai-grok/package.json so the test
tool is installed consistently with other adapter packages (match the version
used by ai-vue, ai, ai-solid, ai-gemini, ai-anthropic, ai-client).

In @packages/typescript/ai-grok/README.md:
- Around line 27-42: The example uses the old pattern of passing model to
generate(); update it so the model is passed into the adapter factory (grokText)
instead and remove the model field from generate() call: call grokText('grok-3')
(or equivalent factory signature) to create the adapter and then call generate({
adapter, messages }) without a separate model parameter, ensuring the adapter
instance carries the model info.
- Around line 62-79: The example uses the old pattern of passing model to
generateImages; update it to pass the model into the adapter factory instead:
call grokImage with the model option (e.g., grokImage({ model:
'grok-2-image-1212' })) and remove the model property from the generateImages()
call so generateImages is invoked with adapter, prompt, numberOfImages, size,
etc.; update the README example to reflect grokImage(...) and generateImages({
adapter, prompt, numberOfImages, size }).
- Around line 44-60: The README example uses the old pattern of passing model to
summarize(); update it to the new adapter architecture by creating the adapter
with the model (call grokSummarize with the model name, e.g.
grokSummarize('grok-3')) and then call summarize() without the model option,
keeping text and style as before; locate grokSummarize and summarize in the
snippet and move the model argument into the grokSummarize(...) adapter factory
call and remove the model property from the summarize(...) invocation.
- Around line 81-87: The README example for createGrokText is missing the
required model positional parameter; update the example call to pass the model
name as the first argument and the API key as the second argument (e.g., call
createGrokText with a model identifier like "grok-1" followed by the API key) so
the function signature (createGrokText(model, apiKey)) is satisfied.

In @packages/typescript/ai-grok/src/adapters/text.ts:
- Around line 397-406: The image branch only treats part.source.value as a URL;
update the block that builds the Grok image part (the code that pushes { type:
'image_url', image_url: { url: ..., detail: ... } }) to handle both
ContentPartSource types by checking part.source.type: if 'url' use
part.source.value directly, if 'data' convert the base64 payload into a data URI
(e.g., "data:<mime>;base64,<value>") before assigning to image_url.url; derive
the MIME from part.metadata or part.source (fall back to "image/jpeg") and
preserve the existing GrokImageMetadata.detail logic when creating the final
parts entry.
🧹 Nitpick comments (13)
docs/adapters/grok.md (1)

1-232: Documentation looks comprehensive and well-structured.

The documentation covers all essential aspects including installation, usage patterns, configuration, API reference, and limitations. The implementation notes section explaining the Chat Completions vs Responses API choice is particularly helpful.

Minor grammar note: On line 163, consider hyphenating "tool-calling" for consistency with standard technical writing conventions (per static analysis hint).

packages/typescript/ai-grok/src/tools/function-tool.ts (1)

19-44: Implementation looks correct for Grok strict mode compatibility.

The conversion logic properly handles schema transformations for Grok's strict mode requirements. The default empty schema fallback is a good defensive pattern.

Minor note: additionalProperties = false on line 34 is redundant since makeGrokStructuredOutputCompatible already sets this property (line 98 in schema-converter.ts). Consider removing for clarity, though it doesn't cause harm.

packages/typescript/ai-grok/src/utils/client.ts (1)

40-45: Consider using slice(2) for more consistent random suffix length.

substring(7) on Math.random().toString(36) can occasionally produce short strings. Using slice(2) would skip the "0." prefix and give a more consistent length.

πŸ”Ž Proposed fix
 export function generateId(prefix: string): string {
-  return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`
+  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`
 }
testing/panel/src/lib/model-selection.ts (1)

1-1: Consider extracting the Provider type to eliminate duplication.

The Provider type is duplicated in examples/ts-react-chat/src/lib/model-selection.ts (line 1). Consider extracting this to a shared location to maintain a single source of truth and prevent the types from diverging during future updates.

testing/panel/src/routes/api.chat.ts (1)

55-55: Consider extracting the Provider type to eliminate duplication.

The Provider type is defined in multiple locations (testing/panel/src/lib/model-selection.ts and examples/ts-react-chat/src/lib/model-selection.ts). Extracting this to a shared type definition would ensure consistency across the codebase and reduce maintenance burden.

packages/typescript/ai-grok/src/message-types.ts (1)

43-55: Consider using type aliases for empty metadata types.

Biome flags these empty interfaces. Since they currently have no properties and TypeScript interfaces with no members are equivalent to {}, consider converting to type aliases. However, if you anticipate extending these via declaration merging in the future, keeping them as interfaces is reasonable.

πŸ”Ž Proposed fix using type aliases
-/**
- * Metadata for Grok video content parts.
- * Note: Video support in Grok is limited; check current API capabilities.
- */
-export interface GrokVideoMetadata {}
+/**
+ * Metadata for Grok video content parts.
+ * Note: Video support in Grok is limited; check current API capabilities.
+ */
+export type GrokVideoMetadata = Record<string, never>

-/**
- * Metadata for Grok document content parts.
- * Note: Direct document support may vary; PDFs often need to be converted to images.
- */
-export interface GrokDocumentMetadata {}
+/**
+ * Metadata for Grok document content parts.
+ * Note: Direct document support may vary; PDFs often need to be converted to images.
+ */
+export type GrokDocumentMetadata = Record<string, never>

-/**
- * Metadata for Grok text content parts.
- * Currently no specific metadata options for text in Grok.
- */
-export interface GrokTextMetadata {}
+/**
+ * Metadata for Grok text content parts.
+ * Currently no specific metadata options for text in Grok.
+ */
+export type GrokTextMetadata = Record<string, never>
packages/typescript/ai-grok/src/image/image-provider-options.ts (1)

94-106: Consider removing or documenting the unused _model parameter.

The _model parameter is unused. If it's reserved for future model-specific validation, consider adding a brief comment. Otherwise, it could be removed.

πŸ”Ž Option: Add documentation comment
 /**
  * Validates that the number of images is within bounds for the model.
+ * @param _model - Reserved for future model-specific validation
  */
 export function validateNumberOfImages(
   _model: string,
packages/typescript/ai-grok/src/model-meta.ts (1)

216-226: Consider maintaining model order consistency.

The array includes GROK_3.name before GROK_3_MINI.name (lines 223-224), but the constants are defined in reverse order (GROK_3_MINI at line 140, GROK_3 at line 159). While functionally correct, aligning the array order with the constant definitions or sorting by capability/release date would improve maintainability.

packages/typescript/ai-grok/src/adapters/text.ts (5)

80-87: Replace console.error with structured logging or remove verbose error output.

The error handling logs the full error details including stack traces to console.error. In production, this could expose sensitive information and creates noise. Consider using a structured logger or removing the verbose logging since the error is re-thrown anyway.

πŸ”Ž Proposed fix
     } catch (error: unknown) {
-      const err = error as Error
-      console.error('>>> chatStream: Fatal error during response creation <<<')
-      console.error('>>> Error message:', err.message)
-      console.error('>>> Error stack:', err.stack)
-      console.error('>>> Full error:', err)
       throw error
     }

129-139: Consider handling empty response content gracefully.

If response.choices[0]?.message.content is null or empty, rawText becomes an empty string, which will fail JSON parsing with a less informative error. Consider adding explicit validation.

πŸ”Ž Proposed fix
       // Extract text content from the response
       const rawText = response.choices[0]?.message.content || ''
 
+      if (!rawText) {
+        throw new Error('Structured output response contained no content')
+      }
+
       // Parse the JSON response
       let parsed: unknown
       try {
         parsed = JSON.parse(rawText)
       } catch {

149-154: Inconsistent error logging verbosity.

The structuredOutput method logs less detail than chatStream (only message, not stack/full error). Consider making error handling consistent across methods, preferably by removing the console.error calls entirely as suggested earlier.


267-272: Minor: finishReason logic could be simplified.

The condition toolCallsInProgress.size > 0 is checked twice (lines 234 and 269). Consider extracting to a variable for clarity.

πŸ”Ž Proposed fix
         // Handle finish reason
         if (choice.finish_reason) {
+          const hasToolCalls = choice.finish_reason === 'tool_calls' || toolCallsInProgress.size > 0
+
           // Emit all completed tool calls
-          if (
-            choice.finish_reason === 'tool_calls' ||
-            toolCallsInProgress.size > 0
-          ) {
+          if (hasToolCalls) {
             for (const [index, toolCall] of toolCallsInProgress) {
               // ... existing code ...
             }
           }

           yield {
             type: 'done',
             // ...
-            finishReason:
-              choice.finish_reason === 'tool_calls' ||
-              toolCallsInProgress.size > 0
-                ? 'tool_calls'
-                : 'stop',
+            finishReason: hasToolCalls ? 'tool_calls' : 'stop',
           }
         }

275-288: Stream error handling uses console.log instead of console.error.

Line 277 uses console.log for error output while the catch blocks in chatStream and structuredOutput use console.error. For consistency and proper log levels, errors should use console.error or be removed entirely.

πŸ”Ž Proposed fix
     } catch (error: unknown) {
       const err = error as Error & { code?: string }
-      console.log('[Grok Adapter] Stream ended with error:', err.message)
       yield {
         type: 'error',
πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 9cb56b9 and 9038ecd.

β›” Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
πŸ“’ Files selected for processing (31)
  • .changeset/add-grok-adapter.md
  • docs/adapters/grok.md
  • docs/guides/migration.md
  • examples/ts-react-chat/package.json
  • examples/ts-react-chat/src/lib/model-selection.ts
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai-grok/CHANGELOG.md
  • packages/typescript/ai-grok/README.md
  • packages/typescript/ai-grok/package.json
  • 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/index.ts
  • packages/typescript/ai-grok/src/message-types.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/function-tool.ts
  • packages/typescript/ai-grok/src/tools/index.ts
  • packages/typescript/ai-grok/src/tools/tool-converter.ts
  • packages/typescript/ai-grok/src/utils/client.ts
  • packages/typescript/ai-grok/src/utils/index.ts
  • packages/typescript/ai-grok/src/utils/schema-converter.ts
  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/tsconfig.json
  • packages/typescript/ai-grok/vite.config.ts
  • packages/typescript/smoke-tests/adapters/package.json
  • packages/typescript/smoke-tests/adapters/src/adapters/index.ts
  • testing/panel/package.json
  • testing/panel/src/lib/model-selection.ts
  • testing/panel/src/routes/api.chat.ts
🧰 Additional context used
πŸ““ Path-based instructions (7)
**/*.{ts,tsx}

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from /adapters subpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with toolDefinition() and Zod schema inference
Implement isomorphic tool system using toolDefinition() with .server() and .client() implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Files:

  • packages/typescript/ai-grok/src/tools/index.ts
  • testing/panel/src/routes/api.chat.ts
  • packages/typescript/ai-grok/src/text/text-provider-options.ts
  • packages/typescript/ai-grok/src/utils/client.ts
  • packages/typescript/ai-grok/src/utils/schema-converter.ts
  • examples/ts-react-chat/src/lib/model-selection.ts
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/src/message-types.ts
  • packages/typescript/ai-grok/src/tools/function-tool.ts
  • packages/typescript/ai-grok/src/utils/index.ts
  • packages/typescript/ai-grok/src/tools/tool-converter.ts
  • packages/typescript/ai-grok/src/adapters/image.ts
  • packages/typescript/ai-grok/src/adapters/summarize.ts
  • packages/typescript/ai-grok/vite.config.ts
  • packages/typescript/smoke-tests/adapters/src/adapters/index.ts
  • packages/typescript/ai-grok/src/model-meta.ts
  • packages/typescript/ai-grok/src/index.ts
  • testing/panel/src/lib/model-selection.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
  • packages/typescript/ai-grok/src/image/image-provider-options.ts
**/*.{ts,tsx,js,jsx}

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

Use camelCase for function and variable names throughout the codebase

Files:

  • packages/typescript/ai-grok/src/tools/index.ts
  • testing/panel/src/routes/api.chat.ts
  • packages/typescript/ai-grok/src/text/text-provider-options.ts
  • packages/typescript/ai-grok/src/utils/client.ts
  • packages/typescript/ai-grok/src/utils/schema-converter.ts
  • examples/ts-react-chat/src/lib/model-selection.ts
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/src/message-types.ts
  • packages/typescript/ai-grok/src/tools/function-tool.ts
  • packages/typescript/ai-grok/src/utils/index.ts
  • packages/typescript/ai-grok/src/tools/tool-converter.ts
  • packages/typescript/ai-grok/src/adapters/image.ts
  • packages/typescript/ai-grok/src/adapters/summarize.ts
  • packages/typescript/ai-grok/vite.config.ts
  • packages/typescript/smoke-tests/adapters/src/adapters/index.ts
  • packages/typescript/ai-grok/src/model-meta.ts
  • packages/typescript/ai-grok/src/index.ts
  • testing/panel/src/lib/model-selection.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
  • packages/typescript/ai-grok/src/image/image-provider-options.ts
examples/**

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

Examples are not built by Nx and should be run independently from their directories with pnpm dev or pnpm install && pnpm dev

Files:

  • examples/ts-react-chat/package.json
  • examples/ts-react-chat/src/lib/model-selection.ts
  • examples/ts-react-chat/src/routes/api.tanchat.ts
**/*.test.ts

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

Write unit tests using Vitest alongside source files with .test.ts naming convention

Files:

  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
packages/typescript/*/src/adapters/*.ts

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking

Files:

  • 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/*/src/model-meta.ts

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Files:

  • packages/typescript/ai-grok/src/model-meta.ts
packages/typescript/*/src/index.ts

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

Export tree-shakeable adapters with clear subpath exports in package.json (e.g., @tanstack/ai/adapters, @tanstack/ai-openai/adapters) to minimize bundle size

Files:

  • packages/typescript/ai-grok/src/index.ts
🧠 Learnings (17)
πŸ““ Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size

Applied to files:

  • .changeset/add-grok-adapter.md
  • packages/typescript/ai-grok/src/tools/index.ts
  • testing/panel/src/routes/api.chat.ts
  • examples/ts-react-chat/package.json
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/README.md
  • packages/typescript/ai-grok/src/tools/function-tool.ts
  • packages/typescript/ai-grok/src/utils/index.ts
  • packages/typescript/smoke-tests/adapters/package.json
  • packages/typescript/ai-grok/CHANGELOG.md
  • packages/typescript/ai-grok/package.json
  • packages/typescript/ai-grok/src/adapters/image.ts
  • packages/typescript/ai-grok/tsconfig.json
  • packages/typescript/ai-grok/vite.config.ts
  • packages/typescript/smoke-tests/adapters/src/adapters/index.ts
  • packages/typescript/ai-grok/src/index.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking

Applied to files:

  • .changeset/add-grok-adapter.md
  • packages/typescript/ai-grok/src/tools/index.ts
  • docs/adapters/grok.md
  • testing/panel/src/routes/api.chat.ts
  • packages/typescript/ai-grok/src/text/text-provider-options.ts
  • examples/ts-react-chat/src/lib/model-selection.ts
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/README.md
  • packages/typescript/ai-grok/src/tools/function-tool.ts
  • packages/typescript/ai-grok/src/utils/index.ts
  • packages/typescript/smoke-tests/adapters/package.json
  • packages/typescript/ai-grok/src/tools/tool-converter.ts
  • packages/typescript/ai-grok/package.json
  • packages/typescript/ai-grok/src/adapters/image.ts
  • packages/typescript/ai-grok/tsconfig.json
  • packages/typescript/ai-grok/src/adapters/summarize.ts
  • packages/typescript/smoke-tests/adapters/src/adapters/index.ts
  • packages/typescript/ai-grok/src/index.ts
  • testing/panel/src/lib/model-selection.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
  • packages/typescript/ai-grok/src/image/image-provider-options.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters

Applied to files:

  • packages/typescript/ai-grok/src/tools/index.ts
  • testing/panel/src/routes/api.chat.ts
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/README.md
  • packages/typescript/ai-grok/package.json
  • packages/typescript/ai-grok/src/adapters/image.ts
  • packages/typescript/ai-grok/src/adapters/summarize.ts
  • packages/typescript/smoke-tests/adapters/src/adapters/index.ts
  • packages/typescript/ai-grok/src/index.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement isomorphic tool system using `toolDefinition()` with `.server()` and `.client()` implementations for dual-environment execution

Applied to files:

  • packages/typescript/ai-grok/src/tools/index.ts
  • packages/typescript/ai-grok/src/tools/function-tool.ts
  • packages/typescript/ai-grok/src/tools/tool-converter.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/model-meta.ts : Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Applied to files:

  • testing/panel/src/routes/api.chat.ts
  • packages/typescript/ai-grok/src/text/text-provider-options.ts
  • examples/ts-react-chat/src/lib/model-selection.ts
  • packages/typescript/ai-grok/src/message-types.ts
  • packages/typescript/ai-grok/package.json
  • packages/typescript/ai-grok/src/adapters/image.ts
  • packages/typescript/ai-grok/tsconfig.json
  • packages/typescript/ai-grok/src/model-meta.ts
  • packages/typescript/ai-grok/src/index.ts
  • testing/panel/src/lib/model-selection.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
  • packages/typescript/ai-grok/src/image/image-provider-options.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Applied to files:

  • testing/panel/src/routes/api.chat.ts
  • packages/typescript/ai-grok/src/adapters/text.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety

Applied to files:

  • packages/typescript/ai-grok/src/text/text-provider-options.ts
  • examples/ts-react-chat/src/lib/model-selection.ts
  • packages/typescript/ai-grok/src/model-meta.ts
  • testing/panel/src/lib/model-selection.ts
  • packages/typescript/ai-grok/src/image/image-provider-options.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/*/package.json : Use `workspace:*` protocol for internal package dependencies in package.json (e.g., `"tanstack/ai": "workspace:*"`)

Applied to files:

  • examples/ts-react-chat/package.json
  • packages/typescript/smoke-tests/adapters/package.json
  • packages/typescript/ai-grok/package.json
  • testing/panel/package.json
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with `toolDefinition()` and Zod schema inference

Applied to files:

  • packages/typescript/ai-grok/src/utils/schema-converter.ts
  • packages/typescript/ai-grok/src/tools/function-tool.ts
  • packages/typescript/ai-grok/tsconfig.json
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.test.ts : Write unit tests using Vitest alongside source files with `.test.ts` naming convention

Applied to files:

  • packages/typescript/ai-grok/tests/grok-adapter.test.ts
  • packages/typescript/ai-grok/tsconfig.json
  • packages/typescript/ai-grok/vite.config.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Maintain type safety through multimodal content support (image, audio, video, document) with model capability awareness

Applied to files:

  • packages/typescript/ai-grok/src/message-types.ts
πŸ“š Learning: 2025-12-27T20:22:51.232Z
Learnt from: harry-whorlow
Repo: TanStack/ai PR: 117
File: packages/typescript/ai-ollama/src/meta/model-meta-gpt-oss.ts:92-97
Timestamp: 2025-12-27T20:22:51.232Z
Learning: In the ai-ollama package's model-meta files (packages/typescript/ai-ollama/src/meta/model-meta-*.ts), capability-related comments follow a standard template format across all files for consistency, even if the comment text doesn't precisely match individual model capabilities. This is an intentional design choice to maintain uniformity across the codebase.

Applied to files:

  • packages/typescript/ai-grok/README.md
  • packages/typescript/ai-grok/src/model-meta.ts
  • testing/panel/src/lib/model-selection.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top

Applied to files:

  • packages/typescript/ai-grok/README.md
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to .eslintrc* : Use ESLint with custom TanStack config for linting all TypeScript and JavaScript files

Applied to files:

  • packages/typescript/ai-grok/package.json
  • packages/typescript/ai-grok/tsconfig.json
  • packages/typescript/ai-grok/vite.config.ts
πŸ“š Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Use Nx workspace with affected commands to optimize testing and building only changed packages and their dependents

Applied to files:

  • testing/panel/package.json
πŸ“š Learning: 2025-12-27T21:39:29.563Z
Learnt from: harry-whorlow
Repo: TanStack/ai PR: 117
File: packages/typescript/ai-ollama/src/meta/model-meta-llama-guard3.ts:70-75
Timestamp: 2025-12-27T21:39:29.563Z
Learning: The standard template comments in ai-ollama model-meta files (like "Models with text, image, audio, video (no document)") should not be modified to match individual model capabilities, as they are intentionally kept uniform across all model-meta-*.ts files for consistency, regardless of what each specific model actually supports.

Applied to files:

  • packages/typescript/ai-grok/src/model-meta.ts
🧬 Code graph analysis (4)
examples/ts-react-chat/src/lib/model-selection.ts (1)
testing/panel/src/lib/model-selection.ts (1)
  • Provider (1-1)
packages/typescript/ai-grok/tests/grok-adapter.test.ts (4)
packages/typescript/ai-grok/src/adapters/text.ts (2)
  • createGrokText (466-474)
  • grokText (500-506)
packages/typescript/ai-grok/src/index.ts (6)
  • createGrokText (8-8)
  • grokText (9-9)
  • createGrokImage (27-27)
  • grokImage (28-28)
  • createGrokSummarize (17-17)
  • grokSummarize (18-18)
packages/typescript/ai-grok/src/adapters/image.ts (2)
  • createGrokImage (138-144)
  • grokImage (170-176)
packages/typescript/ai-grok/src/adapters/summarize.ts (2)
  • createGrokSummarize (136-142)
  • grokSummarize (168-174)
testing/panel/src/lib/model-selection.ts (1)
examples/ts-react-chat/src/lib/model-selection.ts (1)
  • Provider (1-1)
packages/typescript/ai-grok/src/image/image-provider-options.ts (1)
packages/typescript/ai-grok/src/index.ts (2)
  • GrokImageProviderOptions (33-33)
  • GrokImageModelProviderOptionsByName (34-34)
πŸͺ› Biome (2.1.2)
packages/typescript/ai-grok/src/message-types.ts

[error] 43-43: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


[error] 49-49: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


[error] 55-55: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

πŸͺ› LanguageTool
docs/adapters/grok.md

[grammar] ~163-~163: Use a hyphen to join words.
Context: ...hich is essential for TanStack AI's tool calling functionality. The Responses API...

(QB_NEW_EN_HYPHEN)

πŸ”‡ Additional comments (59)
docs/guides/migration.md (1)

438-438: LGTM!

Adding a trailing blank line is a standard formatting convention.

packages/typescript/smoke-tests/adapters/package.json (1)

17-17: LGTM!

The dependency addition follows the correct workspace:* protocol and maintains alphabetical ordering.

packages/typescript/ai-grok/tsconfig.json (1)

1-9: LGTM!

The TypeScript configuration follows the standard pattern for TanStack packages and correctly extends the root configuration.

packages/typescript/ai-grok/CHANGELOG.md (1)

1-7: The changelog correctly documents version 0.0.3 as shown in package.json. While 0.0.3 is unconventional for a typical initial release, it's the intentional version for this package and is consistent across all configuration files.

packages/typescript/ai-grok/vite.config.ts (1)

1-36: LGTM!

The Vite/Vitest configuration follows TanStack conventions with appropriate test setup, coverage configuration, and ESM-only build output.

packages/typescript/ai-grok/src/utils/schema-converter.ts (1)

12-35: transformNullsToUndefined implementation looks correct.

The recursive null-to-undefined transformation with key omission handles the Grok structured output response normalization appropriately.

packages/typescript/ai-grok/src/utils/client.ts (2)

11-16: LGTM!

The client creation correctly uses the OpenAI SDK with xAI's base URL, providing a clean abstraction for Grok API access.


22-38: Environment detection looks correct for isomorphic usage.

The implementation properly handles both browser (globalThis.window.env) and Node.js (process.env) environments, with a clear error message when the key is missing.

packages/typescript/ai-grok/src/text/text-provider-options.ts (2)

13-52: Well-documented provider options types.

The interfaces are cleanly structured with appropriate JSDoc documentation explaining each option's purpose and valid ranges.


72-77: Placeholder validation is acceptable for initial implementation.

The no-op validation function is fine for now since Grok API will handle validation. Consider adding client-side validation for common errors (e.g., temperature range) in a follow-up to provide faster feedback.

testing/panel/package.json (1)

16-16: LGTM!

The Grok package dependency follows the workspace:* protocol consistently with other internal packages. Based on learnings, this is the correct approach for internal package dependencies.

packages/typescript/ai-grok/src/tools/index.ts (1)

1-5: LGTM!

The barrel export pattern enables tree-shaking by re-exporting individual utilities. This aligns with the tree-shakeable adapter architecture as per coding guidelines.

examples/ts-react-chat/package.json (1)

17-17: LGTM!

The Grok package dependency correctly uses the workspace:* protocol, consistent with other internal dependencies. Based on learnings, this is the correct approach.

examples/ts-react-chat/src/lib/model-selection.ts (2)

1-1: LGTM!

The Provider union type extension follows the established pattern and correctly includes 'grok' as a new provider option.


70-86: No changes required β€” Grok model identifiers are correct.

All three model names (grok-3, grok-3-mini, grok-2-vision-1212) match xAI's official API documentation as of 2025.

.changeset/add-grok-adapter.md (1)

5-5: Update changeset description and implementation to reflect actual available Grok models as of the PR date.

The changeset claims "Grok 4.1, Grok 4, Grok 3" support, but as of January 2025, xAI's official API only provides Grok 2 family models. Grok 3 had not been released and missed its end-of-2024 deadline. The implementation references grok-3 and grok-3-mini model IDs that do not exist in the actual xAI API, which will cause failures when users attempt to use them. Either remove references to unreleased models or ensure the changeset and implementation match what is actually available in xAI's current API.

β›” Skipped due to learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking
packages/typescript/ai-grok/src/tools/tool-converter.ts (1)

1-17: LGTM!

The tool conversion logic is clean and straightforward. The function correctly maps standard Tools to Grok's OpenAI-compatible function tool format using the delegated converter.

packages/typescript/ai-grok/tests/grok-adapter.test.ts (1)

1-99: LGTM!

The test coverage appropriately validates adapter creation for all three Grok adapter types. Each test suite confirms:

  • Explicit API key configuration works correctly
  • Environment variable-based API key resolution functions as expected
  • Proper error handling when API keys are missing
  • Adapter properties (kind, name, model) are set correctly

The cleanup via afterEach ensures test isolation.

testing/panel/src/lib/model-selection.ts (1)

71-86: LGTM!

The Grok model options follow the existing pattern and provide a reasonable selection of Grok models (standard, mini, and vision variants).

testing/panel/src/routes/api.chat.ts (1)

176-179: LGTM!

The Grok adapter configuration follows the established pattern used by other providers (Anthropic, Gemini, Ollama, OpenAI) and correctly uses createChatOptions with the grokText adapter.

packages/typescript/ai-grok/src/utils/index.ts (1)

1-10: LGTM!

The utility exports are well-organized and follow the tree-shakeable architecture. Grouping client utilities and schema converters separately makes the module structure clear and maintainable.

packages/typescript/smoke-tests/adapters/src/adapters/index.ts (4)

8-8: LGTM!

The import follows the established tree-shakeable pattern, importing individual adapters (grokImage, grokSummarize, grokText) rather than a monolithic adapter. This aligns with the coding guidelines.


81-84: LGTM!

The Grok model constants follow the same pattern as other providers with environment variable overrides and sensible defaults.


168-186: LGTM!

The createGrokAdapters function follows the established factory pattern used by other providers, correctly checking for the XAI_API_KEY and returning null when unavailable.


217-222: LGTM!

The Grok adapter registration is consistent with other providers in the registry.

examples/ts-react-chat/src/routes/api.tanchat.ts (3)

12-12: LGTM!

The import follows the tree-shakeable pattern for the Grok text adapter.


22-22: LGTM!

The Provider type correctly extended to include 'grok'.


94-97: LGTM!

The Grok adapter configuration follows the established pattern. The simpler config (without additional modelOptions or temperature) is appropriate for the "thin shim approach" mentioned in the PR commits.

packages/typescript/ai-grok/src/message-types.ts (3)

14-24: LGTM!

The GrokImageMetadata interface properly defines the detail option for image processing control, consistent with the OpenAI-compatible API approach.


30-37: LGTM!

The GrokAudioMetadata interface correctly defines supported audio formats.


61-67: LGTM!

The composite GrokMessageMetadataByModality interface properly maps modality types to their metadata types for type inference.

packages/typescript/ai-grok/src/adapters/image.ts (5)

41-57: LGTM!

The GrokImageAdapter class properly extends BaseImageAdapter with correct type parameters. The constructor correctly initializes the OpenAI-compatible client.


59-78: LGTM!

The generateImages method follows a good pattern: validate inputs first, then build and execute the request. The validation calls ensure early failure with descriptive errors.


80-92: LGTM!

The buildRequest method correctly constructs the API payload, spreading modelOptions last to allow user overrides of default values.


94-116: LGTM!

The transformResponse method properly handles the response transformation, including optional usage data with null-safe access.


138-176: LGTM!

Both factory functions follow the established pattern: createGrokImage for explicit API key and grokImage for environment-based key detection. The JSDoc examples are helpful.

packages/typescript/ai-grok/src/adapters/summarize.ts (5)

36-47: LGTM!

The thin wrapper approach delegating to GrokTextAdapter is a clean design that maximizes code reuse.


78-91: LGTM!

The summarizeStream method correctly delegates to the text adapter's streaming interface.


93-119: LGTM!

The buildSummarizationPrompt method properly handles all summarization style options and builds a well-structured system prompt.


136-174: LGTM!

Both factory functions follow the established pattern for explicit and environment-based API key handling.


49-76: The code is correct as written β€” chunk.content provides cumulative text, not deltas.

The text adapter accumulates content deltas internally (line 188 in processGrokStreamChunks: accumulatedContent += deltaContent) and yields chunks with the full accumulated text in the content field (line 195). The delta field is also available for reference if needed. Assigning summary = chunk.content on each iteration is the correct approach.

packages/typescript/ai-grok/src/index.ts (1)

1-55: LGTM!

The index barrel properly exports all public API surface with tree-shakeable structure. Types are correctly exported with the type keyword, and runtime constants are exported separately. This follows the coding guidelines for clear subpath exports.

packages/typescript/ai-grok/src/image/image-provider-options.ts (3)

11-54: LGTM!

The type definitions are well-structured with clear JSDoc documentation. The type maps enable per-model type safety as recommended in the coding guidelines.


68-89: LGTM!

The validateImageSize function properly validates sizes against model-specific constraints with descriptive error messages.


108-117: LGTM!

The validatePrompt function properly validates prompt constraints with clear error messages.

packages/typescript/ai-grok/src/model-meta.ts (6)

1-24: LGTM! Well-structured interface for model metadata.

The ModelMeta interface provides comprehensive type safety for model definitions with appropriate optional fields for capabilities that vary across models.


237-247: Type mapping follows the established pattern correctly.

The GrokModelInputModalitiesByName type properly maps each model name to its input modalities using computed property types, enabling type-safe multimodal message construction. Based on learnings, this aligns with the standard template approach for model metadata files.


253-255: Consider extending provider options for models with unique capabilities.

Currently all chat models map to the same GrokProviderOptions type. If any Grok models support unique parameters (e.g., reasoning-specific options for reasoning models), you may want to differentiate the options per model for stricter type safety. This is a future consideration as xAI's API evolves.


261-276: LGTM! Provider options follow OpenAI-compatible conventions.

The GrokProviderOptions interface correctly documents the OpenAI-compatible parameters with appropriate JSDoc comments for developer guidance.


286-298: LGTM! Type resolution helpers are well-designed.

The ResolveProviderOptions and ResolveInputModalities conditional types provide proper fallback behavior for unknown models while maintaining type safety for known models. This enables both strict typing for known models and graceful handling of custom/new models.


26-211: Grok model metadata is accurate and verified against current xAI documentation.

All model constants, context windows, pricing, and capabilities match the current xAI API specifications as of January 2026. The structured approach with as const satisfies ModelMeta is appropriate for maintainability.

packages/typescript/ai-grok/src/adapters/text.ts (8)

1-32: LGTM! Imports are well-organized and tree-shakeable.

The imports correctly separate type-only imports (using import type) from value imports, enabling proper tree-shaking. This follows the coding guidelines for tree-shakeable adapter architecture.


50-66: LGTM! Class structure follows the adapter pattern correctly.

The GrokTextAdapter class properly extends BaseTextAdapter with correct generic type parameters for model-specific type safety. The kind and name properties use as const for literal typing.


157-289: Stream chunk processing implementation is thorough.

The processGrokStreamChunks method correctly handles:

  • Content delta accumulation
  • Tool call streaming with argument chunking
  • Finish reason handling with proper tool_calls detection
  • Error recovery with graceful error chunk emission

The use of a Map to track in-progress tool calls by index is appropriate for handling interleaved tool call deltas.


294-342: LGTM! Options mapping is well-implemented.

The mapTextOptionsToGrok method correctly:

  • Validates provider options
  • Converts tools to provider format
  • Handles system prompts by joining them
  • Maps common options (temperature, max_tokens, top_p) to OpenAI-compatible format
  • Enables usage reporting via stream_options

344-413: LGTM! Message conversion handles all message types correctly.

The convertMessageToGrok method properly handles:

  • Tool messages with tool_call_id
  • Assistant messages with optional tool_calls
  • User messages with multimodal content (text and image)
  • Content serialization for non-string tool results

The optimization to use simple string format for text-only user messages is a good practice.


415-448: LGTM! Content normalization helpers are clean and correct.

Both normalizeContent and extractTextContent properly handle all three content variants (null, string, ContentPart array) with appropriate type narrowing.


466-506: LGTM! Factory functions provide good DX with proper type inference.

The createGrokText and grokText factory functions:

  • Preserve generic type parameter for model-specific typing
  • Provide clear JSDoc documentation with examples
  • Support both explicit API key and environment variable patterns

This follows the tree-shakeable adapter architecture guidelines.


109-112: Potential issue: outputSchema.required may be undefined.

The code passes outputSchema.required || [] but makeGrokStructuredOutputCompatible receives it as the second parameter. If outputSchema doesn't have a required property defined, the fallback to empty array [] will make all properties optional in the transformed schema, which may not match the intended behavior.

#!/bin/bash
# Check the signature and implementation of makeGrokStructuredOutputCompatible
ast-grep --pattern 'function makeGrokStructuredOutputCompatible($_) {
  $$$
}'

# Also search with export
rg -n "makeGrokStructuredOutputCompatible" --type ts -A 10

Comment on lines +45 to +48
"devDependencies": {
"@vitest/coverage-v8": "4.0.14",
"vite": "^7.2.7"
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

# Read the full package.json for ai-grok to see the test:lib script
cat packages/typescript/ai-grok/package.json

Repository: TanStack/ai

Length of output: 1187


🏁 Script executed:

# Check if vitest is listed as a devDependency in other packages in typescript folder
fd -t f 'package.json' packages/typescript --exec grep -l '"vitest"' {} \;

# Check root package.json
grep '"vitest"' package.json | head -3

Repository: TanStack/ai

Length of output: 328


🏁 Script executed:

# Check the actual structure - is there a monorepo setup with hoisted dependencies?
head -30 package.json

Repository: TanStack/ai

Length of output: 1366


Add vitest to devDependencies.

The test:lib script runs vitest run, but vitest is not listed in devDependencies. All other adapter packages in the workspace (ai-vue, ai, ai-solid, ai-gemini, ai-anthropic, ai-client) explicitly include vitest (^4.0.14). Add it to maintain consistency and ensure reliable tooling availability during local development.

πŸ€– Prompt for AI Agents
In @packages/typescript/ai-grok/package.json around lines 45 - 48, The
package.json is missing vitest in devDependencies even though the "test:lib"
script runs "vitest run"; add "vitest": "^4.0.14" to the "devDependencies"
object in packages/typescript/ai-grok/package.json so the test tool is installed
consistently with other adapter packages (match the version used by ai-vue, ai,
ai-solid, ai-gemini, ai-anthropic, ai-client).

Comment on lines +49 to +51
"peerDependencies": {
"@tanstack/ai": "workspace:^",
"zod": "^4.0.0"
Copy link
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

🏁 Script executed:

#!/bin/bash
# Check peer dependency patterns in all ai-* packages
echo "=== Checking @tanstack/ai peer dependencies across ai-* packages ==="
fd -t f 'package.json' packages/typescript -x grep -H '@tanstack/ai' {} \; | grep -E 'workspace:' | sort

echo -e "\n=== Checking all workspace: patterns across packages/typescript ==="
fd -t f 'package.json' packages/typescript -x grep -H 'workspace:' {} \; | sort

Repository: TanStack/ai

Length of output: 166


Change workspace:^ to workspace:* for the @tanstack/ai peer dependency.

Per project convention, internal package dependencies in the monorepo use workspace:* (not workspace:^). Update the peer dependency version specifier to match the pattern used across the monorepo.

πŸ€– Prompt for AI Agents
In @packages/typescript/ai-grok/package.json around lines 49 - 51, The peer
dependency for "@tanstack/ai" in package.json uses the incorrect specifier
"workspace:^"; update the version specifier to "workspace:*" so it follows
monorepo convention. Locate the "peerDependencies" block in
packages/typescript/ai-grok/package.json and replace the "@tanstack/ai":
"workspace:^" entry with "@tanstack/ai": "workspace:*".

Comment on lines +27 to +42
```typescript
import { grokText } from '@tanstack/ai-grok'
import { generate } from '@tanstack/ai'

const adapter = grokText()

const result = await generate({
adapter,
model: 'grok-3',
messages: [
{ role: 'user', content: 'Explain quantum computing in simple terms' },
],
})

console.log(result.text)
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Correct the API usage pattern to match the new adapter architecture.

The example shows the old API pattern where the model is passed separately to generate(). According to the migration guide, the model should be passed to the adapter factory function, not as a separate parameter.

πŸ”Ž Corrected example
 import { grokText } from '@tanstack/ai-grok'
 import { generate } from '@tanstack/ai'
 
-const adapter = grokText()
+const adapter = grokText('grok-3')
 
 const result = await generate({
   adapter,
-  model: 'grok-3',
   messages: [
     { role: 'user', content: 'Explain quantum computing in simple terms' },
   ],
 })

Based on the migration guide: "Model is passed to adapter factory - The model name is now passed directly to the adapter function" and "No separate model parameter - The model is stored on the adapter, so you don't need to pass it separately."

πŸ“ 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
```typescript
import { grokText } from '@tanstack/ai-grok'
import { generate } from '@tanstack/ai'
const adapter = grokText()
const result = await generate({
adapter,
model: 'grok-3',
messages: [
{ role: 'user', content: 'Explain quantum computing in simple terms' },
],
})
console.log(result.text)
```
import { grokText } from '@tanstack/ai-grok'
import { generate } from '@tanstack/ai'
const adapter = grokText('grok-3')
const result = await generate({
adapter,
messages: [
{ role: 'user', content: 'Explain quantum computing in simple terms' },
],
})
console.log(result.text)
πŸ€– Prompt for AI Agents
In @packages/typescript/ai-grok/README.md around lines 27 - 42, The example uses
the old pattern of passing model to generate(); update it so the model is passed
into the adapter factory (grokText) instead and remove the model field from
generate() call: call grokText('grok-3') (or equivalent factory signature) to
create the adapter and then call generate({ adapter, messages }) without a
separate model parameter, ensuring the adapter instance carries the model info.

Comment on lines +44 to +60
### Summarization Adapter

```typescript
import { grokSummarize } from '@tanstack/ai-grok'
import { summarize } from '@tanstack/ai'

const adapter = grokSummarize()

const result = await summarize({
adapter,
model: 'grok-3',
text: 'Long article text...',
style: 'bullet-points',
})

console.log(result.summary)
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Correct the API usage pattern to match the new adapter architecture.

The example shows the old API pattern where the model is passed separately to summarize(). The model should be passed to the adapter factory function.

πŸ”Ž Corrected example
 import { grokSummarize } from '@tanstack/ai-grok'
 import { summarize } from '@tanstack/ai'
 
-const adapter = grokSummarize()
+const adapter = grokSummarize('grok-3')
 
 const result = await summarize({
   adapter,
-  model: 'grok-3',
   text: 'Long article text...',
   style: 'bullet-points',
 })
πŸ€– Prompt for AI Agents
In @packages/typescript/ai-grok/README.md around lines 44 - 60, The README
example uses the old pattern of passing model to summarize(); update it to the
new adapter architecture by creating the adapter with the model (call
grokSummarize with the model name, e.g. grokSummarize('grok-3')) and then call
summarize() without the model option, keeping text and style as before; locate
grokSummarize and summarize in the snippet and move the model argument into the
grokSummarize(...) adapter factory call and remove the model property from the
summarize(...) invocation.

Comment on lines +62 to +79
### Image Generation Adapter

```typescript
import { grokImage } from '@tanstack/ai-grok'
import { generateImages } from '@tanstack/ai'

const adapter = grokImage()

const result = await generateImages({
adapter,
model: 'grok-2-image-1212',
prompt: 'A beautiful sunset over mountains',
numberOfImages: 1,
size: '1024x1024',
})

console.log(result.images[0].url)
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Correct the API usage pattern to match the new adapter architecture.

The example shows the old API pattern where the model is passed separately to generateImages(). The model should be passed to the adapter factory function.

πŸ”Ž Corrected example
 import { grokImage } from '@tanstack/ai-grok'
 import { generateImages } from '@tanstack/ai'
 
-const adapter = grokImage()
+const adapter = grokImage('grok-2-image-1212')
 
 const result = await generateImages({
   adapter,
-  model: 'grok-2-image-1212',
   prompt: 'A beautiful sunset over mountains',
   numberOfImages: 1,
   size: '1024x1024',
 })
πŸ“ 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
### Image Generation Adapter
```typescript
import { grokImage } from '@tanstack/ai-grok'
import { generateImages } from '@tanstack/ai'
const adapter = grokImage()
const result = await generateImages({
adapter,
model: 'grok-2-image-1212',
prompt: 'A beautiful sunset over mountains',
numberOfImages: 1,
size: '1024x1024',
})
console.log(result.images[0].url)
```
import { grokImage } from '@tanstack/ai-grok'
import { generateImages } from '@tanstack/ai'
const adapter = grokImage('grok-2-image-1212')
const result = await generateImages({
adapter,
prompt: 'A beautiful sunset over mountains',
numberOfImages: 1,
size: '1024x1024',
})
console.log(result.images[0].url)
πŸ€– Prompt for AI Agents
In @packages/typescript/ai-grok/README.md around lines 62 - 79, The example uses
the old pattern of passing model to generateImages; update it to pass the model
into the adapter factory instead: call grokImage with the model option (e.g.,
grokImage({ model: 'grok-2-image-1212' })) and remove the model property from
the generateImages() call so generateImages is invoked with adapter, prompt,
numberOfImages, size, etc.; update the README example to reflect grokImage(...)
and generateImages({ adapter, prompt, numberOfImages, size }).

Comment on lines +81 to +87
### With Explicit API Key

```typescript
import { createGrokText } from '@tanstack/ai-grok'

const adapter = createGrokText('xai-your-api-key-here')
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for createGrokText function definition
ast-grep --pattern 'export function createGrokText($$$) {
  $$$
}'

Repository: TanStack/ai

Length of output: 166


🏁 Script executed:

#!/bin/bash
# Also search for any export of createGrokText with different patterns
rg -A 10 'createGrokText' packages/typescript/ai-grok --type ts --type tsx -B 2

Repository: TanStack/ai

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Check the README file to see the full context and other examples
cat -n packages/typescript/ai-grok/README.md | sed -n '70,100p'

Repository: TanStack/ai

Length of output: 964


🏁 Script executed:

#!/bin/bash
# Find TypeScript files in ai-grok package
fd -e ts -e js packages/typescript/ai-grok/src --type f

Repository: TanStack/ai

Length of output: 484


🏁 Script executed:

#!/bin/bash
# Search for createGrokText in the codebase
rg 'createGrokText' packages/typescript/ai-grok --type ts --type js -B 3 -A 10

Repository: TanStack/ai

Length of output: 5897


🏁 Script executed:

#!/bin/bash
# Look for index or main export files
find packages/typescript/ai-grok -name 'index.ts' -o -name 'index.js' -o -name '*.ts' | head -20

Repository: TanStack/ai

Length of output: 878


🏁 Script executed:

#!/bin/bash
# Read the createGrokText function implementation
cat -n packages/typescript/ai-grok/src/adapters/text.ts | sed -n '30,80p'

Repository: TanStack/ai

Length of output: 1817


🏁 Script executed:

#!/bin/bash
# Find the createGrokText function definition - search further down
cat -n packages/typescript/ai-grok/src/adapters/text.ts | sed -n '100,160p'

Repository: TanStack/ai

Length of output: 2474


🏁 Script executed:

#!/bin/bash
# Find the createGrokText export function - search further down in the file
cat -n packages/typescript/ai-grok/src/adapters/text.ts | sed -n '220,280p'

Repository: TanStack/ai

Length of output: 2423


🏁 Script executed:

#!/bin/bash
# Find the createGrokText export function - search near the end of the file
wc -l packages/typescript/ai-grok/src/adapters/text.ts

Repository: TanStack/ai

Length of output: 107


🏁 Script executed:

#!/bin/bash
# Read the last part of the file where createGrokText should be defined
tail -100 packages/typescript/ai-grok/src/adapters/text.ts | head -80

Repository: TanStack/ai

Length of output: 2230


Correct the incomplete API key example by adding the required model parameter.

The example passes only the API key when the function requires both a model and an API key as positional parameters.

πŸ”Ž Corrected example
 import { createGrokText } from '@tanstack/ai-grok'
 
-const adapter = createGrokText('xai-your-api-key-here')
+const adapter = createGrokText('grok-3', 'xai-your-api-key-here')
πŸ€– Prompt for AI Agents
In @packages/typescript/ai-grok/README.md around lines 81 - 87, The README
example for createGrokText is missing the required model positional parameter;
update the example call to pass the model name as the first argument and the API
key as the second argument (e.g., call createGrokText with a model identifier
like "grok-1" followed by the API key) so the function signature
(createGrokText(model, apiKey)) is satisfied.

Comment on lines +397 to +406
} else if (part.type === 'image') {
const imageMetadata = part.metadata as GrokImageMetadata | undefined
parts.push({
type: 'image_url',
image_url: {
url: part.source.value,
detail: imageMetadata?.detail || 'auto',
},
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for ContentPart type definition
rg -n "type ContentPart\|interface ContentPart" --type ts -A 20

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Search for image source type definitions
rg -n "type.*Source\|interface.*Source" --type ts -B 2 -A 10 | head -100

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Check the text.ts file to understand context better
cat -n packages/typescript/ai-grok/src/adapters/text.ts | sed -n '390,415p'

Repository: TanStack/ai

Length of output: 922


🏁 Script executed:

#!/bin/bash
# Search for GrokImageMetadata and related types
rg -n "GrokImageMetadata\|ImageMetadata" --type ts -B 2 -A 10

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Look for imports in text.ts to see where ContentPart comes from
head -50 packages/typescript/ai-grok/src/adapters/text.ts

Repository: TanStack/ai

Length of output: 1443


🏁 Script executed:

#!/bin/bash
# Search for ContentPart in the codebase (case-insensitive, broader search)
rg "ContentPart" --type ts | head -20

Repository: TanStack/ai

Length of output: 1824


🏁 Script executed:

#!/bin/bash
# Look for source type definitions in the codebase
rg "type.*Source\|source.*:" --type ts -A 3 | head -50

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Check if there are other image handling patterns in other adapters
find packages/typescript -name "*.ts" -path "*/adapters/*" -type f | head -10

Repository: TanStack/ai

Length of output: 591


🏁 Script executed:

#!/bin/bash
# Find and display ContentPartSource type definition
rg -n "export interface ContentPartSource|export type ContentPartSource" --type ts -A 15 packages/typescript/ai/src/types.ts

Repository: TanStack/ai

Length of output: 461


🏁 Script executed:

#!/bin/bash
# Find and display ContentPart type definition for image
rg -n "source: ContentPartSource" --type ts -B 5 -A 2 packages/typescript/ai/src/types.ts

Repository: TanStack/ai

Length of output: 1399


🏁 Script executed:

#!/bin/bash
# Get more context around ContentPart type definitions
sed -n '1,500p' packages/typescript/ai/src/types.ts | tail -200

Repository: TanStack/ai

Length of output: 6817


🏁 Script executed:

#!/bin/bash
# Check OpenAI adapter's image handling for comparison
find packages/typescript -name "text.ts" -path "*/adapters/*" -exec grep -l "image" {} \;

Repository: TanStack/ai

Length of output: 310


🏁 Script executed:

#!/bin/bash
# Look at OpenAI adapter's text handling for images
rg -n "part.type === 'image'" --type ts -B 3 -A 10 packages/typescript/ai-openai/src/adapters/text.ts

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Search for image_url handling in OpenAI adapter
rg -n "image_url\|image" --type ts -A 5 -B 2 packages/typescript/ai-openai/src/adapters/text.ts | head -60

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Check if there's any base64 or data URI handling in the codebase
rg -n "base64\|data:" --type ts | grep -i image

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Look for ContentPartSource handling patterns
rg -n "source.type" --type ts -B 2 -A 5 | head -80

Repository: TanStack/ai

Length of output: 5181


Add support for base64 image sources in addition to URL sources.

The ContentPartSource interface supports both type: 'data' (base64-encoded) and type: 'url'. The current implementation only handles URL sources directly. When part.source.type === 'data', the value is base64-encoded and must be converted to a data URI (e.g., data:image/jpeg;base64,<value>) before passing to OpenAI's image_url field.

See how Anthropic and Gemini adapters handle this by checking part.source.type and converting accordingly.

πŸ€– Prompt for AI Agents
In @packages/typescript/ai-grok/src/adapters/text.ts around lines 397 - 406, The
image branch only treats part.source.value as a URL; update the block that
builds the Grok image part (the code that pushes { type: 'image_url', image_url:
{ url: ..., detail: ... } }) to handle both ContentPartSource types by checking
part.source.type: if 'url' use part.source.value directly, if 'data' convert the
base64 payload into a data URI (e.g., "data:<mime>;base64,<value>") before
assigning to image_url.url; derive the MIME from part.metadata or part.source
(fall back to "image/jpeg") and preserve the existing GrokImageMetadata.detail
logic when creating the final parts entry.

Comment on lines +64 to +91
// Recursively transform nested objects/arrays
if (prop.type === 'object' && prop.properties) {
properties[propName] = makeGrokStructuredOutputCompatible(
prop,
prop.required || [],
)
} else if (prop.type === 'array' && prop.items) {
properties[propName] = {
...prop,
items: makeGrokStructuredOutputCompatible(
prop.items,
prop.items.required || [],
),
}
} else if (wasOptional) {
// Make optional fields nullable by adding null to the type
if (prop.type && !Array.isArray(prop.type)) {
properties[propName] = {
...prop,
type: [prop.type, 'null'],
}
} else if (Array.isArray(prop.type) && !prop.type.includes('null')) {
properties[propName] = {
...prop,
type: [...prop.type, 'null'],
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Optional nested objects/arrays won't be made nullable.

The else if chain means that if a property is an object or array, the wasOptional check on line 78 is never reached. This results in optional nested objects/arrays not having null added to their type union, which could cause issues when Grok returns null for an optional nested field.

πŸ”Ž Proposed fix to handle nullable nested types
       // Recursively transform nested objects/arrays
       if (prop.type === 'object' && prop.properties) {
-        properties[propName] = makeGrokStructuredOutputCompatible(
+        let transformed = makeGrokStructuredOutputCompatible(
           prop,
           prop.required || [],
         )
+        if (wasOptional) {
+          transformed = { ...transformed, type: ['object', 'null'] }
+        }
+        properties[propName] = transformed
       } else if (prop.type === 'array' && prop.items) {
-        properties[propName] = {
+        let transformed = {
           ...prop,
           items: makeGrokStructuredOutputCompatible(
             prop.items,
             prop.items.required || [],
           ),
         }
+        if (wasOptional) {
+          transformed = { ...transformed, type: ['array', 'null'] }
+        }
+        properties[propName] = transformed
       } else if (wasOptional) {

@AlemTuzlak AlemTuzlak merged commit 7c40941 into main Jan 7, 2026
6 checks passed
@AlemTuzlak AlemTuzlak deleted the feat/add-grok branch January 7, 2026 09:56
@github-actions github-actions bot mentioned this pull request Jan 7, 2026
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.

4 participants