Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@ai-sdk/xai

Package Overview
Dependencies
Maintainers
3
Versions
308
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ai-sdk/xai - npm Package Compare versions

Comparing version
3.0.56
to
3.0.57
+302
src/xai-video-model.ts
import {
AISDKError,
type Experimental_VideoModelV3,
type SharedV3Warning,
} from '@ai-sdk/provider';
import {
combineHeaders,
convertUint8ArrayToBase64,
createJsonResponseHandler,
delay,
type FetchFunction,
getFromApi,
parseProviderOptions,
postJsonToApi,
} from '@ai-sdk/provider-utils';
import { z } from 'zod/v4';
import { xaiFailedResponseHandler } from './xai-error';
import {
type XaiVideoModelOptions,
xaiVideoModelOptionsSchema,
} from './xai-video-options';
import type { XaiVideoModelId } from './xai-video-settings';
interface XaiVideoModelConfig {
provider: string;
baseURL: string | undefined;
headers: () => Record<string, string | undefined>;
fetch?: FetchFunction;
_internal?: {
currentDate?: () => Date;
};
}
const RESOLUTION_MAP: Record<string, string> = {
'1280x720': '720p',
'854x480': '480p',
'640x480': '480p',
};
export class XaiVideoModel implements Experimental_VideoModelV3 {
readonly specificationVersion = 'v3';
readonly maxVideosPerCall = 1;
get provider(): string {
return this.config.provider;
}
constructor(
readonly modelId: XaiVideoModelId,
private config: XaiVideoModelConfig,
) {}
async doGenerate(
options: Parameters<Experimental_VideoModelV3['doGenerate']>[0],
): Promise<Awaited<ReturnType<Experimental_VideoModelV3['doGenerate']>>> {
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
const warnings: SharedV3Warning[] = [];
const xaiOptions = (await parseProviderOptions({
provider: 'xai',
providerOptions: options.providerOptions,
schema: xaiVideoModelOptionsSchema,
})) as XaiVideoModelOptions | undefined;
const isEdit = xaiOptions?.videoUrl != null;
if (options.fps != null) {
warnings.push({
type: 'unsupported',
feature: 'fps',
details: 'xAI video models do not support custom FPS.',
});
}
if (options.seed != null) {
warnings.push({
type: 'unsupported',
feature: 'seed',
details: 'xAI video models do not support seed.',
});
}
if (options.n != null && options.n > 1) {
warnings.push({
type: 'unsupported',
feature: 'n',
details:
'xAI video models do not support generating multiple videos per call. ' +
'Only 1 video will be generated.',
});
}
if (isEdit && options.duration != null) {
warnings.push({
type: 'unsupported',
feature: 'duration',
details: 'xAI video editing does not support custom duration.',
});
}
if (isEdit && options.aspectRatio != null) {
warnings.push({
type: 'unsupported',
feature: 'aspectRatio',
details: 'xAI video editing does not support custom aspect ratio.',
});
}
if (
isEdit &&
(xaiOptions?.resolution != null || options.resolution != null)
) {
warnings.push({
type: 'unsupported',
feature: 'resolution',
details: 'xAI video editing does not support custom resolution.',
});
}
const body: Record<string, unknown> = {
model: this.modelId,
prompt: options.prompt,
};
if (!isEdit && options.duration != null) {
body.duration = options.duration;
}
if (!isEdit && options.aspectRatio != null) {
body.aspect_ratio = options.aspectRatio;
}
if (!isEdit && xaiOptions?.resolution != null) {
body.resolution = xaiOptions.resolution;
} else if (!isEdit && options.resolution != null) {
const mapped = RESOLUTION_MAP[options.resolution];
if (mapped != null) {
body.resolution = mapped;
} else {
warnings.push({
type: 'unsupported',
feature: 'resolution',
details:
`Unrecognized resolution "${options.resolution}". ` +
'Use providerOptions.xai.resolution with "480p" or "720p" instead.',
});
}
}
// Video editing: pass source video URL (nested object like image)
if (xaiOptions?.videoUrl != null) {
body.video = { url: xaiOptions.videoUrl };
}
// Image-to-video: convert SDK image to nested image object
if (options.image != null) {
if (options.image.type === 'url') {
body.image = { url: options.image.url };
} else {
const base64Data =
typeof options.image.data === 'string'
? options.image.data
: convertUint8ArrayToBase64(options.image.data);
body.image = {
url: `data:${options.image.mediaType};base64,${base64Data}`,
};
}
}
if (xaiOptions != null) {
for (const [key, value] of Object.entries(xaiOptions)) {
if (
![
'pollIntervalMs',
'pollTimeoutMs',
'resolution',
'videoUrl',
].includes(key)
) {
body[key] = value;
}
}
}
const baseURL = this.config.baseURL ?? 'https://api.x.ai/v1';
// Step 1: Create video generation/edit request
const { value: createResponse } = await postJsonToApi({
url: `${baseURL}/videos/${isEdit ? 'edits' : 'generations'}`,
headers: combineHeaders(this.config.headers(), options.headers),
body,
failedResponseHandler: xaiFailedResponseHandler,
successfulResponseHandler: createJsonResponseHandler(
xaiCreateVideoResponseSchema,
),
abortSignal: options.abortSignal,
fetch: this.config.fetch,
});
const requestId = createResponse.request_id;
if (!requestId) {
throw new AISDKError({
name: 'XAI_VIDEO_GENERATION_ERROR',
message: `No request_id returned from xAI API. Response: ${JSON.stringify(createResponse)}`,
});
}
// Step 2: Poll for completion
const pollIntervalMs = xaiOptions?.pollIntervalMs ?? 5000;
const pollTimeoutMs = xaiOptions?.pollTimeoutMs ?? 600000;
const startTime = Date.now();
let responseHeaders: Record<string, string> | undefined;
while (true) {
await delay(pollIntervalMs, { abortSignal: options.abortSignal });
if (Date.now() - startTime > pollTimeoutMs) {
throw new AISDKError({
name: 'XAI_VIDEO_GENERATION_TIMEOUT',
message: `Video generation timed out after ${pollTimeoutMs}ms`,
});
}
const { value: statusResponse, responseHeaders: pollHeaders } =
await getFromApi({
url: `${baseURL}/videos/${requestId}`,
headers: combineHeaders(this.config.headers(), options.headers),
successfulResponseHandler: createJsonResponseHandler(
xaiVideoStatusResponseSchema,
),
failedResponseHandler: xaiFailedResponseHandler,
abortSignal: options.abortSignal,
fetch: this.config.fetch,
});
responseHeaders = pollHeaders;
if (
statusResponse.status === 'done' ||
(statusResponse.status == null && statusResponse.video?.url)
) {
if (!statusResponse.video?.url) {
throw new AISDKError({
name: 'XAI_VIDEO_GENERATION_ERROR',
message:
'Video generation completed but no video URL was returned.',
});
}
return {
videos: [
{
type: 'url' as const,
url: statusResponse.video.url,
mediaType: 'video/mp4',
},
],
warnings,
response: {
timestamp: currentDate,
modelId: this.modelId,
headers: responseHeaders,
},
providerMetadata: {
xai: {
requestId,
videoUrl: statusResponse.video.url,
...(statusResponse.video.duration != null
? { duration: statusResponse.video.duration }
: {}),
},
},
};
}
if (statusResponse.status === 'expired') {
throw new AISDKError({
name: 'XAI_VIDEO_GENERATION_EXPIRED',
message: 'Video generation request expired.',
});
}
// 'pending' → continue polling
}
}
}
const xaiCreateVideoResponseSchema = z.object({
request_id: z.string().nullish(),
});
const xaiVideoStatusResponseSchema = z.object({
status: z.string().nullish(),
video: z
.object({
url: z.string(),
duration: z.number().nullish(),
respect_moderation: z.boolean().nullish(),
})
.nullish(),
model: z.string().nullish(),
});
import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
import { z } from 'zod/v4';
export type XaiVideoModelOptions = {
pollIntervalMs?: number | null;
pollTimeoutMs?: number | null;
resolution?: '480p' | '720p' | null;
videoUrl?: string | null;
[key: string]: unknown;
};
export const xaiVideoModelOptionsSchema = lazySchema(() =>
zodSchema(
z
.object({
pollIntervalMs: z.number().positive().nullish(),
pollTimeoutMs: z.number().positive().nullish(),
resolution: z.enum(['480p', '720p']).nullish(),
videoUrl: z.string().nullish(),
})
.passthrough(),
),
);
export type XaiVideoModelId = 'grok-imagine-video' | (string & {});
+6
-0
# @ai-sdk/xai
## 3.0.57
### Patch Changes
- 56dfdf6: feat (provider/xai): add video support
## 3.0.56

@@ -4,0 +10,0 @@

+20
-2
import { z } from 'zod/v4';
import { ProviderV3, LanguageModelV3, ImageModelV3 } from '@ai-sdk/provider';
import { ProviderV3, LanguageModelV3, ImageModelV3, Experimental_VideoModelV3 } from '@ai-sdk/provider';
import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';

@@ -84,2 +84,12 @@ import { FetchFunction } from '@ai-sdk/provider-utils';

type XaiVideoModelId = 'grok-imagine-video' | (string & {});
type XaiVideoModelOptions = {
pollIntervalMs?: number | null;
pollTimeoutMs?: number | null;
resolution?: '480p' | '720p' | null;
videoUrl?: string | null;
[key: string]: unknown;
};
type XaiImageModelId = 'grok-2-image' | 'grok-imagine-image' | (string & {});

@@ -312,2 +322,10 @@

/**
* Creates an Xai video model for video generation.
*/
video(modelId: XaiVideoModelId): Experimental_VideoModelV3;
/**
* Creates an Xai video model for video generation.
*/
videoModel(modelId: XaiVideoModelId): Experimental_VideoModelV3;
/**
* Server-side agentic tools for use with the responses API.

@@ -345,2 +363,2 @@ */

export { VERSION, type XaiErrorData, type XaiImageModelOptions, type XaiImageModelOptions as XaiImageProviderOptions, type XaiLanguageModelChatOptions, type XaiLanguageModelResponsesOptions, type XaiProvider, type XaiLanguageModelChatOptions as XaiProviderOptions, type XaiProviderSettings, type XaiLanguageModelResponsesOptions as XaiResponsesProviderOptions, codeExecution, createXai, mcpServer, viewImage, viewXVideo, webSearch, xSearch, xai, xaiTools };
export { VERSION, type XaiErrorData, type XaiImageModelOptions, type XaiImageModelOptions as XaiImageProviderOptions, type XaiLanguageModelChatOptions, type XaiLanguageModelResponsesOptions, type XaiProvider, type XaiLanguageModelChatOptions as XaiProviderOptions, type XaiProviderSettings, type XaiLanguageModelResponsesOptions as XaiResponsesProviderOptions, type XaiVideoModelId, type XaiVideoModelOptions, type XaiVideoModelOptions as XaiVideoProviderOptions, codeExecution, createXai, mcpServer, viewImage, viewXVideo, webSearch, xSearch, xai, xaiTools };
import { z } from 'zod/v4';
import { ProviderV3, LanguageModelV3, ImageModelV3 } from '@ai-sdk/provider';
import { ProviderV3, LanguageModelV3, ImageModelV3, Experimental_VideoModelV3 } from '@ai-sdk/provider';
import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';

@@ -84,2 +84,12 @@ import { FetchFunction } from '@ai-sdk/provider-utils';

type XaiVideoModelId = 'grok-imagine-video' | (string & {});
type XaiVideoModelOptions = {
pollIntervalMs?: number | null;
pollTimeoutMs?: number | null;
resolution?: '480p' | '720p' | null;
videoUrl?: string | null;
[key: string]: unknown;
};
type XaiImageModelId = 'grok-2-image' | 'grok-imagine-image' | (string & {});

@@ -312,2 +322,10 @@

/**
* Creates an Xai video model for video generation.
*/
video(modelId: XaiVideoModelId): Experimental_VideoModelV3;
/**
* Creates an Xai video model for video generation.
*/
videoModel(modelId: XaiVideoModelId): Experimental_VideoModelV3;
/**
* Server-side agentic tools for use with the responses API.

@@ -345,2 +363,2 @@ */

export { VERSION, type XaiErrorData, type XaiImageModelOptions, type XaiImageModelOptions as XaiImageProviderOptions, type XaiLanguageModelChatOptions, type XaiLanguageModelResponsesOptions, type XaiProvider, type XaiLanguageModelChatOptions as XaiProviderOptions, type XaiProviderSettings, type XaiLanguageModelResponsesOptions as XaiResponsesProviderOptions, codeExecution, createXai, mcpServer, viewImage, viewXVideo, webSearch, xSearch, xai, xaiTools };
export { VERSION, type XaiErrorData, type XaiImageModelOptions, type XaiImageModelOptions as XaiImageProviderOptions, type XaiLanguageModelChatOptions, type XaiLanguageModelResponsesOptions, type XaiProvider, type XaiLanguageModelChatOptions as XaiProviderOptions, type XaiProviderSettings, type XaiLanguageModelResponsesOptions as XaiResponsesProviderOptions, type XaiVideoModelId, type XaiVideoModelOptions, type XaiVideoModelOptions as XaiVideoProviderOptions, codeExecution, createXai, mcpServer, viewImage, viewXVideo, webSearch, xSearch, xai, xaiTools };
+2
-2
{
"name": "@ai-sdk/xai",
"version": "3.0.56",
"version": "3.0.57",
"license": "Apache-2.0",

@@ -32,4 +32,4 @@ "sideEffects": false,

"dependencies": {
"@ai-sdk/provider-utils": "4.0.15",
"@ai-sdk/openai-compatible": "2.0.30",
"@ai-sdk/provider-utils": "4.0.15",
"@ai-sdk/provider": "3.0.8"

@@ -36,0 +36,0 @@ },

@@ -17,2 +17,8 @@ export type {

} from './xai-image-options';
export type { XaiVideoModelId } from './xai-video-settings';
export type {
XaiVideoModelOptions,
/** @deprecated Use `XaiVideoModelOptions` instead. */
XaiVideoModelOptions as XaiVideoProviderOptions,
} from './xai-video-options';
export { createXai, xai } from './xai-provider';

@@ -19,0 +25,0 @@ export type { XaiProvider, XaiProviderSettings } from './xai-provider';

import {
type Experimental_VideoModelV3,
ImageModelV3,

@@ -22,2 +23,4 @@ LanguageModelV3,

import { VERSION } from './version';
import { XaiVideoModel } from './xai-video-model';
import { XaiVideoModelId } from './xai-video-settings';

@@ -56,2 +59,12 @@ export interface XaiProvider extends ProviderV3 {

/**
* Creates an Xai video model for video generation.
*/
video(modelId: XaiVideoModelId): Experimental_VideoModelV3;
/**
* Creates an Xai video model for video generation.
*/
videoModel(modelId: XaiVideoModelId): Experimental_VideoModelV3;
/**
* Server-side agentic tools for use with the responses API.

@@ -136,2 +149,11 @@ */

const createVideoModel = (modelId: XaiVideoModelId) => {
return new XaiVideoModel(modelId, {
provider: 'xai.video',
baseURL,
headers: getHeaders,
fetch: options.fetch,
});
};
const provider = (modelId: XaiChatModelId) =>

@@ -150,2 +172,4 @@ createChatLanguageModel(modelId);

provider.image = createImageModel;
provider.videoModel = createVideoModel;
provider.video = createVideoModel;
provider.tools = xaiTools;

@@ -152,0 +176,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet