@ai-sdk/xai 4.0.2 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/index.js +62 -19
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +68 -3
- package/package.json +4 -4
- package/src/xai-video-model.ts +113 -25
package/docs/01-xai.mdx
CHANGED
|
@@ -79,7 +79,12 @@ first argument is the model id, e.g. `grok-4.20-non-reasoning`.
|
|
|
79
79
|
const model = xai('grok-4.20-non-reasoning');
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
<Note>
|
|
83
|
+
Since AI SDK 7, `xai(modelId)` uses the xAI Responses API by default. To use
|
|
84
|
+
the [Chat Completions
|
|
85
|
+
API](https://docs.x.ai/docs/api-reference#chat-completions) (legacy), use
|
|
86
|
+
`xai.chat(modelId)`.
|
|
87
|
+
</Note>
|
|
83
88
|
|
|
84
89
|
### Example
|
|
85
90
|
|
|
@@ -165,7 +170,7 @@ calling pattern.
|
|
|
165
170
|
|
|
166
171
|
## Responses API (Agentic Tools)
|
|
167
172
|
|
|
168
|
-
The xAI Responses API is the default when using `xai(modelId)
|
|
173
|
+
The xAI Responses API is the default when using `xai(modelId)` (since AI SDK 7). You can also use `xai.responses(modelId)` explicitly. This enables the model to autonomously orchestrate tool calls and research on xAI's servers.
|
|
169
174
|
|
|
170
175
|
```ts
|
|
171
176
|
const model = xai.responses('grok-4.20-non-reasoning');
|
|
@@ -907,6 +912,37 @@ const { video } = await generateVideo({
|
|
|
907
912
|
});
|
|
908
913
|
```
|
|
909
914
|
|
|
915
|
+
You can also pass the starting frame via the top-level `frameImages` option using the `first_frame` role. This is provider-agnostic and accepts file data (e.g. a `Buffer`) or a `{ type: 'url', url }` object:
|
|
916
|
+
|
|
917
|
+
```ts
|
|
918
|
+
import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
|
|
919
|
+
import { experimental_generateVideo as generateVideo } from 'ai';
|
|
920
|
+
import fs from 'node:fs';
|
|
921
|
+
|
|
922
|
+
const { video } = await generateVideo({
|
|
923
|
+
model: xai.video('grok-imagine-video'),
|
|
924
|
+
prompt: 'The cat slowly turns its head and blinks',
|
|
925
|
+
frameImages: [
|
|
926
|
+
{
|
|
927
|
+
image: fs.readFileSync('./start-frame.png'),
|
|
928
|
+
frameType: 'first_frame',
|
|
929
|
+
},
|
|
930
|
+
],
|
|
931
|
+
duration: 5,
|
|
932
|
+
providerOptions: {
|
|
933
|
+
xai: {
|
|
934
|
+
pollTimeoutMs: 600000, // 10 minutes
|
|
935
|
+
} satisfies XaiVideoModelOptions,
|
|
936
|
+
},
|
|
937
|
+
});
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
<Note>
|
|
941
|
+
xAI does not support first-last-frame interpolation. A `last_frame` entry in
|
|
942
|
+
`frameImages` is ignored with a warning — use the `extend-video` mode to
|
|
943
|
+
continue from a video's last frame instead.
|
|
944
|
+
</Note>
|
|
945
|
+
|
|
910
946
|
### Video Editing
|
|
911
947
|
|
|
912
948
|
Edit an existing video using a text prompt by providing a source video URL via provider options:
|
|
@@ -1064,9 +1100,38 @@ const { video } = await generateVideo({
|
|
|
1064
1100
|
|
|
1065
1101
|
Use `<IMAGE_1>`, `<IMAGE_2>`, etc. in your prompt to reference specific images. Up to 7 reference images are supported per request.
|
|
1066
1102
|
|
|
1103
|
+
Alternatively, use the provider-agnostic top-level `inputReferences` option. Providing it automatically selects reference-to-video mode, and it accepts file data (e.g. a `Buffer`) or `{ type: 'url', url }` objects:
|
|
1104
|
+
|
|
1105
|
+
```ts
|
|
1106
|
+
import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
|
|
1107
|
+
import { experimental_generateVideo as generateVideo } from 'ai';
|
|
1108
|
+
import fs from 'node:fs';
|
|
1109
|
+
|
|
1110
|
+
const { video } = await generateVideo({
|
|
1111
|
+
model: xai.video('grok-imagine-video'),
|
|
1112
|
+
prompt:
|
|
1113
|
+
'The comic cat and the comic dog are having a playful chase ' +
|
|
1114
|
+
'through a sunlit park. Cinematic slow-motion, warm afternoon light.',
|
|
1115
|
+
inputReferences: [
|
|
1116
|
+
fs.readFileSync('./comic-cat.png'),
|
|
1117
|
+
fs.readFileSync('./comic-dog.png'),
|
|
1118
|
+
],
|
|
1119
|
+
duration: 8,
|
|
1120
|
+
aspectRatio: '16:9',
|
|
1121
|
+
providerOptions: {
|
|
1122
|
+
xai: {
|
|
1123
|
+
pollTimeoutMs: 600000,
|
|
1124
|
+
} satisfies XaiVideoModelOptions,
|
|
1125
|
+
},
|
|
1126
|
+
});
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1067
1129
|
<Note>
|
|
1068
1130
|
Reference-to-video supports `duration`, `aspectRatio`, and `resolution`. Use
|
|
1069
|
-
`mode` to select the operation — each mode is mutually exclusive.
|
|
1131
|
+
`mode` to select the operation — each mode is mutually exclusive. When both
|
|
1132
|
+
are provided, `frameImages` takes precedence over `inputReferences`, and
|
|
1133
|
+
`inputReferences` takes precedence over the legacy `referenceImageUrls`
|
|
1134
|
+
provider option. Reference-to-video requires the `grok-imagine-video` model.
|
|
1070
1135
|
</Note>
|
|
1071
1136
|
|
|
1072
1137
|
### Video Provider Options
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/xai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@ai-sdk/openai-compatible": "3.0.
|
|
33
|
-
"@ai-sdk/provider": "4.0.
|
|
34
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
32
|
+
"@ai-sdk/openai-compatible": "3.0.3",
|
|
33
|
+
"@ai-sdk/provider": "4.0.1",
|
|
34
|
+
"@ai-sdk/provider-utils": "5.0.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "22.19.19",
|
package/src/xai-video-model.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AISDKError,
|
|
3
3
|
type Experimental_VideoModelV4,
|
|
4
|
+
type Experimental_VideoModelV4File,
|
|
4
5
|
type SharedV4Warning,
|
|
5
6
|
} from '@ai-sdk/provider';
|
|
6
7
|
import {
|
|
@@ -37,21 +38,87 @@ const RESOLUTION_MAP: Record<string, string> = {
|
|
|
37
38
|
'640x480': '480p',
|
|
38
39
|
};
|
|
39
40
|
|
|
41
|
+
type XaiVideoDoGenerateOptions = Parameters<
|
|
42
|
+
Experimental_VideoModelV4['doGenerate']
|
|
43
|
+
>[0];
|
|
44
|
+
|
|
45
|
+
function getFirstFrameImage(
|
|
46
|
+
options: XaiVideoDoGenerateOptions,
|
|
47
|
+
): Experimental_VideoModelV4File | undefined {
|
|
48
|
+
return options.frameImages?.find(frame => frame.frameType === 'first_frame')
|
|
49
|
+
?.image;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getLastFrameImage(
|
|
53
|
+
options: XaiVideoDoGenerateOptions,
|
|
54
|
+
): Experimental_VideoModelV4File | undefined {
|
|
55
|
+
return options.frameImages?.find(frame => frame.frameType === 'last_frame')
|
|
56
|
+
?.image;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveStartImage(
|
|
60
|
+
options: XaiVideoDoGenerateOptions,
|
|
61
|
+
): Experimental_VideoModelV4File | undefined {
|
|
62
|
+
return getFirstFrameImage(options) ?? options.image;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function fileToXaiImageUrl(file: Experimental_VideoModelV4File): string {
|
|
66
|
+
if (file.type === 'url') {
|
|
67
|
+
return file.url;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const base64Data =
|
|
71
|
+
typeof file.data === 'string'
|
|
72
|
+
? file.data
|
|
73
|
+
: convertUint8ArrayToBase64(file.data);
|
|
74
|
+
return `data:${file.mediaType ?? 'image/png'};base64,${base64Data}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Resolves the reference images for R2V generation. First-class
|
|
78
|
+
// `inputReferences` win over the legacy `referenceImageUrls` provider option.
|
|
79
|
+
function resolveReferenceImages(
|
|
80
|
+
options: XaiVideoDoGenerateOptions,
|
|
81
|
+
xaiOptions: XaiParsedVideoModelOptions | undefined,
|
|
82
|
+
): Array<{ url: string }> | undefined {
|
|
83
|
+
if (options.inputReferences != null && options.inputReferences.length > 0) {
|
|
84
|
+
return options.inputReferences.map(reference => ({
|
|
85
|
+
url: fileToXaiImageUrl(reference),
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (
|
|
90
|
+
xaiOptions?.referenceImageUrls != null &&
|
|
91
|
+
xaiOptions.referenceImageUrls.length > 0
|
|
92
|
+
) {
|
|
93
|
+
return xaiOptions.referenceImageUrls.map(url => ({ url }));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
40
99
|
function resolveVideoMode(
|
|
41
|
-
options:
|
|
100
|
+
options: XaiVideoDoGenerateOptions,
|
|
101
|
+
xaiOptions: XaiParsedVideoModelOptions | undefined,
|
|
42
102
|
): XaiParsedVideoModelOptions['mode'] | undefined {
|
|
43
|
-
if (
|
|
44
|
-
return
|
|
103
|
+
if (xaiOptions?.mode != null) {
|
|
104
|
+
return xaiOptions.mode;
|
|
45
105
|
}
|
|
46
106
|
|
|
47
|
-
if (
|
|
107
|
+
if (xaiOptions?.videoUrl != null) {
|
|
48
108
|
return 'edit-video';
|
|
49
109
|
}
|
|
50
110
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
111
|
+
// frameImages (first/last frame) take precedence over reference images, so
|
|
112
|
+
// only auto-select reference-to-video when no frame images are provided.
|
|
113
|
+
const hasFrameImages =
|
|
114
|
+
options.frameImages != null && options.frameImages.length > 0;
|
|
115
|
+
const hasInputReferences =
|
|
116
|
+
options.inputReferences != null && options.inputReferences.length > 0;
|
|
117
|
+
const hasLegacyReferenceUrls =
|
|
118
|
+
xaiOptions?.referenceImageUrls != null &&
|
|
119
|
+
xaiOptions.referenceImageUrls.length > 0;
|
|
120
|
+
|
|
121
|
+
if (!hasFrameImages && (hasInputReferences || hasLegacyReferenceUrls)) {
|
|
55
122
|
return 'reference-to-video';
|
|
56
123
|
}
|
|
57
124
|
|
|
@@ -83,7 +150,7 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
83
150
|
schema: xaiVideoModelOptionsSchema,
|
|
84
151
|
})) as XaiParsedVideoModelOptions | undefined;
|
|
85
152
|
|
|
86
|
-
const effectiveMode = resolveVideoMode(xaiOptions);
|
|
153
|
+
const effectiveMode = resolveVideoMode(options, xaiOptions);
|
|
87
154
|
|
|
88
155
|
const isEdit = effectiveMode === 'edit-video';
|
|
89
156
|
const isExtension = effectiveMode === 'extend-video';
|
|
@@ -207,26 +274,47 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
207
274
|
body.video = { url: xaiOptions!.videoUrl };
|
|
208
275
|
}
|
|
209
276
|
|
|
210
|
-
// Convert
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
277
|
+
// Convert the start image (first_frame or image-to-video input) to the
|
|
278
|
+
// nested xAI request image object.
|
|
279
|
+
const startImage = resolveStartImage(options);
|
|
280
|
+
if (startImage != null) {
|
|
281
|
+
body.image = { url: fileToXaiImageUrl(startImage) };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// xAI has no first-last-frame interpolation; warn and ignore last_frame.
|
|
285
|
+
if (getLastFrameImage(options) != null) {
|
|
286
|
+
warnings.push({
|
|
287
|
+
type: 'unsupported',
|
|
288
|
+
feature: 'frameImages',
|
|
289
|
+
details:
|
|
290
|
+
'xAI video models do not support last_frame. Use ' +
|
|
291
|
+
'providerOptions.xai.mode "extend-video" to continue from a ' +
|
|
292
|
+
"video's last frame. The last frame image was ignored.",
|
|
293
|
+
});
|
|
223
294
|
}
|
|
224
295
|
|
|
225
296
|
// Reference images for R2V (reference-to-video) generation
|
|
226
297
|
if (hasReferenceImages) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
298
|
+
const referenceImages = resolveReferenceImages(options, xaiOptions);
|
|
299
|
+
if (referenceImages != null) {
|
|
300
|
+
body.reference_images = referenceImages;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Warn when reference images were provided but cannot be used in the
|
|
305
|
+
// resolved mode (e.g. alongside frameImages, or in edit/extend modes).
|
|
306
|
+
if (
|
|
307
|
+
options.inputReferences != null &&
|
|
308
|
+
options.inputReferences.length > 0 &&
|
|
309
|
+
!hasReferenceImages
|
|
310
|
+
) {
|
|
311
|
+
warnings.push({
|
|
312
|
+
type: 'unsupported',
|
|
313
|
+
feature: 'inputReferences',
|
|
314
|
+
details:
|
|
315
|
+
'xAI only supports inputReferences for reference-to-video ' +
|
|
316
|
+
'generation. The reference images were ignored.',
|
|
317
|
+
});
|
|
230
318
|
}
|
|
231
319
|
|
|
232
320
|
if (xaiOptions != null) {
|