Skip to content

feat(ai-grok): video generation adapter for the grok-imagine video models#742

Open
tombeckenham wants to merge 3 commits into
mainfrom
705-featai-grok-video-generation-adapter-for-the-grok-imagine-video-models
Open

feat(ai-grok): video generation adapter for the grok-imagine video models#742
tombeckenham wants to merge 3 commits into
mainfrom
705-featai-grok-video-generation-adapter-for-the-grok-imagine-video-models

Conversation

@tombeckenham

@tombeckenham tombeckenham commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

Adds a grokVideo adapter to @tanstack/ai-grok for xAI's Imagine video models, closing #705:

  • Models: grok-imagine-video ($0.05/s) and grok-imagine-video-1.5-preview ($0.08/s), added to model-meta.ts with GROK_VIDEO_MODELS / GrokVideoModel exports.
  • Adapter: GrokVideoAdapter (+ grokVideo / createGrokVideo factories) extends BaseVideoAdapter with the jobs/polling pattern — createVideoJob posts to /v1/videos/generations, getVideoStatus / getVideoUrl read /v1/videos/{request_id}. The Imagine video endpoints are plain JSON and not part of the OpenAI SDK surface, so the adapter issues direct requests with an injectable fetch seam (no global stubs needed in tests).
  • Sizing: aspect-ratio size template consistent with the grok-imagine image models — '16:9_720p'aspect_ratio / resolution. Video accepts 1:1 | 16:9 | 9:16 | 4:3 | 3:4 | 3:2 | 2:3 and 480p | 720p | 1080p (a narrower set than the image models — verified against the live API, which rejects phone ratios and auto).
  • Duration: integer seconds 1–15, validated client-side to match the API's enforcement.
  • Image-to-video: modelOptions.image: { url } passes a starting frame (public URL or base64 data URI).
  • Usage: completed jobs report usage.unitsBilled (billed seconds) and usage.cost (exact USD from the API's cost_in_usd_ticks; 10^10 ticks per dollar).
  • Example: examples/ts-react-media gains "xAI Direct" text-to-video and image-to-video entries using the native grokVideo adapter (alongside the existing fal-hosted grok-imagine entries), with exact USD cost shown on completion.
  • Docs/skill: docs/adapters/grok.md, docs/media/video-generation.md (+ config.json updatedAt), and the media-generation agent skill.

⚠️ Note on #624

The issue assumed the Imagine plumbing from #624 (issue #618 ) had landed; that PR is still open, so this is built directly against main and is independently mergeable. The core imageInputs / metadata.role flow from #618/#624 isn't available on main yet — image-to-video is exposed through typed modelOptions.image for now. If #624 merges before this, this PR can be rebased and the imageInputs / media-prompt flow wired into the adapter here before merging. Otherwise this can be merged as is, and 624 could be updated to rewire this adapter

Testing

  • Unit tests (packages/ai-grok/tests/video-adapter.test.ts, 27 tests) cover request shape, size-template mapping, validation, status mapping, usage, and error paths via the injected fetch seam. aimock doesn't mock the Imagine video endpoints, so coverage is unit-test based (same approach the issue calls out for image-to-image / image-to-video).
  • Live-verified end-to-end against the real xAI API through generateVideo() / getVideoJobStatus(): text-to-video (16:9_480p, 1s, unitsBilled: 1, cost: 0.05) and image-to-video via modelOptions.image (cost: 0.052 — the API bills slightly more with an image input, which is why cost is read from the API rather than computed).
  • API behaviors (status values, error shapes, parameter enums, 404 handling) were probed against the live endpoints before implementation.

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

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Grok Imagine video generation: async create/poll flow, new Grok video models, size/aspect/resolution templates, 1–15s durations, image-to-video support, and per-job usage/cost reporting.
  • Documentation
    • Updated docs, navigation, and API reference to include Grok video generation and provider options; added xAI env sample.
  • Tests
    • New test suite covering adapter behavior, validation, polling, and usage reporting.
  • Examples
    • Example app updated to use Grok videos and display billed cost.

…dels

Adds a grokVideo adapter to @tanstack/ai-grok for xAI's Imagine video
models (grok-imagine-video at $0.05/s, grok-imagine-video-1.5-preview at
$0.08/s) using the experimental generateVideo() jobs/polling
architecture: POST /v1/videos/generations to create, GET
/v1/videos/{request_id} to poll, hosted mp4 URL plus usage (billed
seconds + exact USD cost) on completion.

Sizing follows the grok-imagine aspect-ratio template ('16:9_720p' →
aspect_ratio/resolution); durations are 1-15 integer seconds;
image-to-video starting frames go through modelOptions.image. The
Imagine video endpoints are plain JSON (not in the OpenAI SDK), so the
adapter issues direct requests with an injectable fetch seam.

Closes #705.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Grok Imagine video generation: model metadata and provider option types, a GrokVideoAdapter implementing create/poll/getUrl flows (with usage/cost mapping), factories and exports, tests, docs, and example app wiring.

Changes

Grok Imagine Video Adapter

Layer / File(s) Summary
Video model definitions and provider options
packages/ai-grok/src/model-meta.ts, packages/ai-grok/src/video/video-provider-options.ts
Adds two Grok Imagine video models (grok-imagine-video, grok-imagine-video-1.5-preview) and exports GROK_VIDEO_MODELS/GrokVideoModel. Introduces aspect-ratio/resolution/size union types, duration bounds (1–15s), parseGrokVideoSize, and validation helpers plus GrokVideoProviderOptions interface.
Grok video adapter core
packages/ai-grok/src/adapters/video.ts
Implements GrokVideoAdapter with createVideoJob (POST /v1/videos/generations), retrieveJob/getVideoStatus polling (GET /v1/videos/{id}), getVideoUrl (requires terminal url + maps usage/cost via cost ticks), auth/request helpers, error parsing, status mapping, and factories createGrokVideo / grokVideo.
Public API exports
packages/ai-grok/src/index.ts
Re-exports the adapter, factory helpers, GrokVideoConfig, provider option types, and video model types/constants from the package root.
Comprehensive test coverage
packages/ai-grok/tests/video-adapter.test.ts
Vitest suite covering factory behavior, createVideoJob request construction and pre-request validation, getVideoStatus mapping and error handling (including 404 behavior), getVideoUrl completion and usage/cost translation, and provider option helper tests.
Documentation and release notes
.changeset/grok-imagine-video-adapter.md, docs/adapters/grok.md, docs/media/video-generation.md, docs/config.json, packages/ai/skills/ai-core/media-generation/SKILL.md, examples/*
Adds a changeset for a minor release; updates Grok adapter docs with "Video Generation (Experimental)" and API entry for grokVideo; expands general video-generation docs to include Grok and fal; updates skill docs and doc metadata; updates example app wiring (env, package, UI VideoGenerator state, models, server functions) to use grokVideo and surface billed cost.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • TanStack/ai#723: Related UI/usage plumbing changes—both PRs update VideoGenerator to display unitsBilled/cost and propagate usage into completed-state rendering.

Suggested reviewers

  • crutchcorn
  • AlemTuzlak

Poem

🐰 I hopped through sizes, ticks, and queues,

I parsed the ratios and chased the views,
I polled and waited for a hosted file,
Counted billed seconds, converted each smile,
A rabbit cheering as the preview runs wild.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately and concisely describes the main change: adding a video generation adapter for Grok Imagine video models to the @tanstack/ai-grok package.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description comprehensively covers all template requirements: detailed changes overview, complete testing methodology, and all checklist items properly marked.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 705-featai-grok-video-generation-adapter-for-the-grok-imagine-video-models

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.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

7 package(s) bumped directly, 24 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-event-client 0.5.4 → 1.0.0 Changeset
@tanstack/ai-fal 0.7.23 → 1.0.0 Changeset
@tanstack/ai-gemini 0.15.1 → 1.0.0 Changeset
@tanstack/ai-grok 0.11.2 → 1.0.0 Changeset
@tanstack/ai-openai 0.14.1 → 1.0.0 Changeset
@tanstack/ai-anthropic 0.15.1 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.2.5 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.2.5 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.20 → 1.0.0 Dependent
@tanstack/ai-groq 0.4.2 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.30 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.30 → 1.0.0 Dependent
@tanstack/ai-ollama 0.8.1 → 1.0.0 Dependent
@tanstack/ai-openrouter 0.13.1 → 1.0.0 Dependent
@tanstack/ai-preact 0.9.4 → 1.0.0 Dependent
@tanstack/ai-react 0.15.4 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.6 → 1.0.0 Dependent
@tanstack/ai-solid 0.13.4 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.6 → 1.0.0 Dependent
@tanstack/ai-svelte 0.13.4 → 1.0.0 Dependent
@tanstack/ai-vue 0.13.4 → 1.0.0 Dependent
@tanstack/openai-base 0.8.1 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.28.0 → 0.29.0 Changeset
@tanstack/ai-client 0.16.3 → 0.17.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-devtools-core 0.4.8 → 0.4.9 Dependent
@tanstack/ai-isolate-cloudflare 0.2.21 → 0.2.22 Dependent
@tanstack/ai-mcp 0.1.0 → 0.1.1 Dependent
@tanstack/ai-vue-ui 0.2.16 → 0.2.17 Dependent
@tanstack/preact-ai-devtools 0.1.51 → 0.1.52 Dependent
@tanstack/react-ai-devtools 0.2.51 → 0.2.52 Dependent
@tanstack/solid-ai-devtools 0.2.51 → 0.2.52 Dependent

@nx-cloud

nx-cloud Bot commented Jun 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 49a7c09

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-11 00:28:17 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jun 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-anthropic

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

@tanstack/ai-client

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

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@742

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@742

@tanstack/ai-devtools-core

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

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@742

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@742

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@742

@tanstack/ai-gemini

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

@tanstack/ai-grok

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

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@742

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@742

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@742

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@742

@tanstack/ai-mcp

npm i https://pkg.pr.new/@tanstack/ai-mcp@742

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@742

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@742

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@742

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@742

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

commit: c6e1573

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/media/video-generation.md (1)

315-315: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove as any type assertion from documentation code sample.

The coding guidelines explicitly prohibit as type-assertion casts in documentation code samples (except as const). Examples must type-check without type casts.

Consider properly typing the size parameter in the input validator or using a type guard instead:

.inputValidator((data: { prompt: string; size?: '1280x720' | '720x1280' | '1792x1024' | '1024x1792'; duration?: number }) => data)
🤖 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/media/video-generation.md` at line 315, Remove the "as any" cast on the
size assignment in the docs sample and ensure the sample types-check without
casts: update the .inputValidator signature to type the incoming data.size as
the allowed union ('1280x720' | '720x1280' | '1792x1024' | '1024x1792') or add a
short type guard that validates/normalizes data.size before assigning to size;
reference the .inputValidator callback and the size property so the sample
compiles without using "as any".

Source: Coding guidelines

🧹 Nitpick comments (1)
packages/ai-grok/tests/video-adapter.test.ts (1)

1-1: ⚡ Quick win

Place this unit test alongside the source file per repo rule.

video-adapter.test.ts currently lives under packages/ai-grok/tests/, but the guideline requires *.test.ts files to be colocated with source. Please move it next to the adapter/provider source it validates (for example under packages/ai-grok/src/...).

As per coding guidelines, "**/*.test.ts: Place unit tests alongside source code in *.test.ts files".

🤖 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/ai-grok/tests/video-adapter.test.ts` at line 1, The test file
video-adapter.test.ts must be moved from packages/ai-grok/tests/ to be colocated
with the adapter/provider source it validates (e.g., under packages/ai-grok/src/
alongside the video adapter implementation), update any relative import paths in
video-adapter.test.ts to reflect the new location, and adjust any test-runner or
build config references if they rely on the old tests/ directory; locate the
adapter/provider source by name (video-adapter, VideoAdapter, or similar) and
place the test file next to that module so imports and module resolution remain
correct.

Source: Coding guidelines

🤖 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 `@packages/ai-grok/src/adapters/video.ts`:
- Around line 151-208: The validation calls (validateVideoSize,
validateVideoDuration) in createVideoJob can throw before the existing
try/catch, so wrap those validations and the rest of the function body inside
the same try block (or add an outer try that encloses validations) so any thrown
validation errors are caught; in the catch call logger.errors with
toRunErrorPayload and source `${this.name}.createVideoJob` (same shape as
current catch) and rethrow the error to preserve behavior.

---

Outside diff comments:
In `@docs/media/video-generation.md`:
- Line 315: Remove the "as any" cast on the size assignment in the docs sample
and ensure the sample types-check without casts: update the .inputValidator
signature to type the incoming data.size as the allowed union ('1280x720' |
'720x1280' | '1792x1024' | '1024x1792') or add a short type guard that
validates/normalizes data.size before assigning to size; reference the
.inputValidator callback and the size property so the sample compiles without
using "as any".

---

Nitpick comments:
In `@packages/ai-grok/tests/video-adapter.test.ts`:
- Line 1: The test file video-adapter.test.ts must be moved from
packages/ai-grok/tests/ to be colocated with the adapter/provider source it
validates (e.g., under packages/ai-grok/src/ alongside the video adapter
implementation), update any relative import paths in video-adapter.test.ts to
reflect the new location, and adjust any test-runner or build config references
if they rely on the old tests/ directory; locate the adapter/provider source by
name (video-adapter, VideoAdapter, or similar) and place the test file next to
that module so imports and module resolution remain correct.
🪄 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: b6eef03c-6d23-44d3-b96b-75cc68878c1b

📥 Commits

Reviewing files that changed from the base of the PR and between ff267a5 and 7d63f7a.

📒 Files selected for processing (10)
  • .changeset/grok-imagine-video-adapter.md
  • docs/adapters/grok.md
  • docs/config.json
  • docs/media/video-generation.md
  • packages/ai-grok/src/adapters/video.ts
  • packages/ai-grok/src/index.ts
  • packages/ai-grok/src/model-meta.ts
  • packages/ai-grok/src/video/video-provider-options.ts
  • packages/ai-grok/tests/video-adapter.test.ts
  • packages/ai/skills/ai-core/media-generation/SKILL.md

Comment on lines +151 to +208
async createVideoJob(
options: VideoGenerationOptions<GrokVideoProviderOptions>,
): Promise<VideoJobResult> {
const { model, prompt, size, modelOptions, logger } = options

validateVideoSize(model, size)
validateVideoDuration(model, options.duration)
validateVideoDuration(model, modelOptions?.duration)
const duration = options.duration ?? modelOptions?.duration

// The generic `size` option carries an "aspectRatio_resolution" template
// (e.g. '16:9_720p') and maps to the Imagine API's `aspect_ratio` /
// `resolution` parameters; explicit modelOptions win over the template.
const parsedSize = size !== undefined ? parseGrokVideoSize(size) : undefined
const request = {
model,
prompt,
...(parsedSize && {
aspect_ratio: parsedSize.aspectRatio,
...(parsedSize.resolution !== undefined && {
resolution: parsedSize.resolution,
}),
}),
...(duration !== undefined && { duration }),
...modelOptions,
}

try {
logger.request(
`activity=video.create provider=${this.name} model=${model} size=${size ?? 'default'} duration=${duration ?? 'default'}`,
{ provider: this.name, model },
)

const response = await this.request('/videos/generations', {
method: 'POST',
body: JSON.stringify(request),
})
if (!response.ok) {
throw new Error(
`grok: video generation request failed (${response.status} ${response.statusText}): ${await this.errorMessage(response)}`,
)
}

const result = (await response.json()) as GrokVideoCreateResponse
if (!result.request_id) {
throw new Error(
'grok: video generation response contained no request_id',
)
}
return { jobId: result.request_id, model }
} catch (error: unknown) {
logger.errors(`${this.name}.createVideoJob fatal`, {
error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),
source: `${this.name}.createVideoJob`,
})
throw error
}
}

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

Verify that validation errors before the API call are properly logged.

The validation calls validateVideoSize and validateVideoDuration (lines 156-158) can throw errors before the try block starts at line 178. If validation fails, the error bypasses the catch block's logger, so validation failures won't be logged with the structured error format.

Consider wrapping the entire function body in the try-catch or adding a separate catch wrapper:

🛡️ Proposed fix to ensure validation errors are logged
 async createVideoJob(
   options: VideoGenerationOptions<GrokVideoProviderOptions>,
 ): Promise<VideoJobResult> {
+  try {
     const { model, prompt, size, modelOptions, logger } = options

     validateVideoSize(model, size)
     validateVideoDuration(model, options.duration)
     validateVideoDuration(model, modelOptions?.duration)
     const duration = options.duration ?? modelOptions?.duration

     // The generic `size` option carries an "aspectRatio_resolution" template
     // (e.g. '16:9_720p') and maps to the Imagine API's `aspect_ratio` /
     // `resolution` parameters; explicit modelOptions win over the template.
     const parsedSize = size !== undefined ? parseGrokVideoSize(size) : undefined
     const request = {
       model,
       prompt,
       ...(parsedSize && {
         aspect_ratio: parsedSize.aspectRatio,
         ...(parsedSize.resolution !== undefined && {
           resolution: parsedSize.resolution,
         }),
       }),
       ...(duration !== undefined && { duration }),
       ...modelOptions,
     }

-    try {
       logger.request(
         `activity=video.create provider=${this.name} model=${model} size=${size ?? 'default'} duration=${duration ?? 'default'}`,
         { provider: this.name, model },
       )

       const response = await this.request('/videos/generations', {
         method: 'POST',
         body: JSON.stringify(request),
       })
       if (!response.ok) {
         throw new Error(
           `grok: video generation request failed (${response.status} ${response.statusText}): ${await this.errorMessage(response)}`,
         )
       }

       const result = (await response.json()) as GrokVideoCreateResponse
       if (!result.request_id) {
         throw new Error(
           'grok: video generation response contained no request_id',
         )
       }
       return { jobId: result.request_id, model }
-    } catch (error: unknown) {
+  } catch (error: unknown) {
       logger.errors(`${this.name}.createVideoJob fatal`, {
         error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),
         source: `${this.name}.createVideoJob`,
       })
       throw error
-    }
+  }
 }
📝 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
async createVideoJob(
options: VideoGenerationOptions<GrokVideoProviderOptions>,
): Promise<VideoJobResult> {
const { model, prompt, size, modelOptions, logger } = options
validateVideoSize(model, size)
validateVideoDuration(model, options.duration)
validateVideoDuration(model, modelOptions?.duration)
const duration = options.duration ?? modelOptions?.duration
// The generic `size` option carries an "aspectRatio_resolution" template
// (e.g. '16:9_720p') and maps to the Imagine API's `aspect_ratio` /
// `resolution` parameters; explicit modelOptions win over the template.
const parsedSize = size !== undefined ? parseGrokVideoSize(size) : undefined
const request = {
model,
prompt,
...(parsedSize && {
aspect_ratio: parsedSize.aspectRatio,
...(parsedSize.resolution !== undefined && {
resolution: parsedSize.resolution,
}),
}),
...(duration !== undefined && { duration }),
...modelOptions,
}
try {
logger.request(
`activity=video.create provider=${this.name} model=${model} size=${size ?? 'default'} duration=${duration ?? 'default'}`,
{ provider: this.name, model },
)
const response = await this.request('/videos/generations', {
method: 'POST',
body: JSON.stringify(request),
})
if (!response.ok) {
throw new Error(
`grok: video generation request failed (${response.status} ${response.statusText}): ${await this.errorMessage(response)}`,
)
}
const result = (await response.json()) as GrokVideoCreateResponse
if (!result.request_id) {
throw new Error(
'grok: video generation response contained no request_id',
)
}
return { jobId: result.request_id, model }
} catch (error: unknown) {
logger.errors(`${this.name}.createVideoJob fatal`, {
error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),
source: `${this.name}.createVideoJob`,
})
throw error
}
}
async createVideoJob(
options: VideoGenerationOptions<GrokVideoProviderOptions>,
): Promise<VideoJobResult> {
try {
const { model, prompt, size, modelOptions, logger } = options
validateVideoSize(model, size)
validateVideoDuration(model, options.duration)
validateVideoDuration(model, modelOptions?.duration)
const duration = options.duration ?? modelOptions?.duration
// The generic `size` option carries an "aspectRatio_resolution" template
// (e.g. '16:9_720p') and maps to the Imagine API's `aspect_ratio` /
// `resolution` parameters; explicit modelOptions win over the template.
const parsedSize = size !== undefined ? parseGrokVideoSize(size) : undefined
const request = {
model,
prompt,
...(parsedSize && {
aspect_ratio: parsedSize.aspectRatio,
...(parsedSize.resolution !== undefined && {
resolution: parsedSize.resolution,
}),
}),
...(duration !== undefined && { duration }),
...modelOptions,
}
logger.request(
`activity=video.create provider=${this.name} model=${model} size=${size ?? 'default'} duration=${duration ?? 'default'}`,
{ provider: this.name, model },
)
const response = await this.request('/videos/generations', {
method: 'POST',
body: JSON.stringify(request),
})
if (!response.ok) {
throw new Error(
`grok: video generation request failed (${response.status} ${response.statusText}): ${await this.errorMessage(response)}`,
)
}
const result = (await response.json()) as GrokVideoCreateResponse
if (!result.request_id) {
throw new Error(
'grok: video generation response contained no request_id',
)
}
return { jobId: result.request_id, model }
} catch (error: unknown) {
logger.errors(`${this.name}.createVideoJob fatal`, {
error: toRunErrorPayload(error, `${this.name}.createVideoJob failed`),
source: `${this.name}.createVideoJob`,
})
throw error
}
}
🤖 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/ai-grok/src/adapters/video.ts` around lines 151 - 208, The
validation calls (validateVideoSize, validateVideoDuration) in createVideoJob
can throw before the existing try/catch, so wrap those validations and the rest
of the function body inside the same try block (or add an outer try that
encloses validations) so any thrown validation errors are caught; in the catch
call logger.errors with toRunErrorPayload and source
`${this.name}.createVideoJob` (same shape as current catch) and rethrow the
error to preserve behavior.

tombeckenham and others added 2 commits June 11, 2026 10:14
Adds 'xAI Direct' text-to-video and image-to-video entries to the video
generator that use the native grokVideo adapter against xAI's Imagine
API (XAI_API_KEY), alongside the existing fal-hosted grok-imagine
entries. Polling is now keyed by the UI model id (the direct entries
share one adapter model), and the completed card shows the exact USD
cost when the adapter reports it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…imagine-video-1.5-preview

Groups the video model dropdown by provider (fal.ai / xAI direct) the
same way the image generator does, adds the model version to the
direct-xAI entry names, and switches them to the
grok-imagine-video-1.5-preview model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tombeckenham tombeckenham requested review from a team and AlemTuzlak June 11, 2026 00:51
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.

feat(ai-grok): video generation adapter for the grok-imagine video models

1 participant