@ai-sdk/xai 4.0.0-beta.21 → 4.0.0-beta.24
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 +25 -0
- package/dist/index.d.mts +82 -8
- package/dist/index.d.ts +82 -8
- package/dist/index.js +264 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +275 -44
- package/dist/index.mjs.map +1 -1
- package/docs/01-xai.mdx +166 -47
- package/package.json +4 -4
- package/src/convert-to-xai-chat-messages.ts +17 -1
- package/src/files/xai-files-api.ts +16 -0
- package/src/files/xai-files-options.ts +15 -0
- package/src/files/xai-files.ts +93 -0
- package/src/index.ts +1 -0
- package/src/responses/convert-to-xai-responses-input.ts +14 -2
- package/src/responses/xai-responses-api.ts +2 -1
- package/src/xai-chat-prompt.ts +2 -1
- package/src/xai-provider.ts +16 -0
- package/src/xai-video-model.ts +104 -13
- package/src/xai-video-options.ts +136 -14
package/src/xai-provider.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Experimental_VideoModelV4,
|
|
3
|
+
FilesV4,
|
|
3
4
|
ImageModelV4,
|
|
4
5
|
LanguageModelV4,
|
|
5
6
|
NoSuchModelError,
|
|
@@ -20,6 +21,7 @@ import { XaiResponsesLanguageModel } from './responses/xai-responses-language-mo
|
|
|
20
21
|
import { XaiResponsesModelId } from './responses/xai-responses-options';
|
|
21
22
|
import { xaiTools } from './tool';
|
|
22
23
|
import { VERSION } from './version';
|
|
24
|
+
import { XaiFiles } from './files/xai-files';
|
|
23
25
|
import { XaiVideoModel } from './xai-video-model';
|
|
24
26
|
import { XaiVideoModelId } from './xai-video-settings';
|
|
25
27
|
|
|
@@ -61,6 +63,11 @@ export interface XaiProvider extends ProviderV4 {
|
|
|
61
63
|
*/
|
|
62
64
|
videoModel(modelId: XaiVideoModelId): Experimental_VideoModelV4;
|
|
63
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Returns the xAI files interface for uploading files.
|
|
68
|
+
*/
|
|
69
|
+
files(): FilesV4;
|
|
70
|
+
|
|
64
71
|
/**
|
|
65
72
|
* Server-side agentic tools for use with the responses API.
|
|
66
73
|
*/
|
|
@@ -150,6 +157,14 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
|
|
|
150
157
|
});
|
|
151
158
|
};
|
|
152
159
|
|
|
160
|
+
const createFiles = () =>
|
|
161
|
+
new XaiFiles({
|
|
162
|
+
provider: 'xai.files',
|
|
163
|
+
baseURL,
|
|
164
|
+
headers: getHeaders,
|
|
165
|
+
fetch: options.fetch,
|
|
166
|
+
});
|
|
167
|
+
|
|
153
168
|
const provider = (modelId: XaiResponsesModelId) =>
|
|
154
169
|
createResponsesLanguageModel(modelId);
|
|
155
170
|
|
|
@@ -165,6 +180,7 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
|
|
|
165
180
|
provider.image = createImageModel;
|
|
166
181
|
provider.videoModel = createVideoModel;
|
|
167
182
|
provider.video = createVideoModel;
|
|
183
|
+
provider.files = createFiles;
|
|
168
184
|
provider.tools = xaiTools;
|
|
169
185
|
|
|
170
186
|
return provider;
|
package/src/xai-video-model.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { z } from 'zod/v4';
|
|
17
17
|
import { xaiFailedResponseHandler } from './xai-error';
|
|
18
18
|
import {
|
|
19
|
-
type
|
|
19
|
+
type XaiParsedVideoModelOptions,
|
|
20
20
|
xaiVideoModelOptionsSchema,
|
|
21
21
|
} from './xai-video-options';
|
|
22
22
|
import type { XaiVideoModelId } from './xai-video-settings';
|
|
@@ -37,6 +37,27 @@ const RESOLUTION_MAP: Record<string, string> = {
|
|
|
37
37
|
'640x480': '480p',
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
function resolveVideoMode(
|
|
41
|
+
options: XaiParsedVideoModelOptions | undefined,
|
|
42
|
+
): XaiParsedVideoModelOptions['mode'] | undefined {
|
|
43
|
+
if (options?.mode != null) {
|
|
44
|
+
return options.mode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (options?.videoUrl != null) {
|
|
48
|
+
return 'edit-video';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
options?.referenceImageUrls != null &&
|
|
53
|
+
options.referenceImageUrls.length > 0
|
|
54
|
+
) {
|
|
55
|
+
return 'reference-to-video';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
40
61
|
export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
41
62
|
readonly specificationVersion = 'v4';
|
|
42
63
|
readonly maxVideosPerCall = 1;
|
|
@@ -60,9 +81,13 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
60
81
|
provider: 'xai',
|
|
61
82
|
providerOptions: options.providerOptions,
|
|
62
83
|
schema: xaiVideoModelOptionsSchema,
|
|
63
|
-
})) as
|
|
84
|
+
})) as XaiParsedVideoModelOptions | undefined;
|
|
64
85
|
|
|
65
|
-
const
|
|
86
|
+
const effectiveMode = resolveVideoMode(xaiOptions);
|
|
87
|
+
|
|
88
|
+
const isEdit = effectiveMode === 'edit-video';
|
|
89
|
+
const isExtension = effectiveMode === 'extend-video';
|
|
90
|
+
const hasReferenceImages = effectiveMode === 'reference-to-video';
|
|
66
91
|
|
|
67
92
|
if (options.fps != null) {
|
|
68
93
|
warnings.push({
|
|
@@ -90,6 +115,7 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
90
115
|
});
|
|
91
116
|
}
|
|
92
117
|
|
|
118
|
+
// Edit mode: duration, aspectRatio, resolution not supported
|
|
93
119
|
if (isEdit && options.duration != null) {
|
|
94
120
|
warnings.push({
|
|
95
121
|
type: 'unsupported',
|
|
@@ -117,22 +143,46 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
117
143
|
});
|
|
118
144
|
}
|
|
119
145
|
|
|
146
|
+
// Extension mode: aspectRatio and resolution not supported
|
|
147
|
+
if (isExtension && options.aspectRatio != null) {
|
|
148
|
+
warnings.push({
|
|
149
|
+
type: 'unsupported',
|
|
150
|
+
feature: 'aspectRatio',
|
|
151
|
+
details: 'xAI video extension does not support custom aspect ratio.',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (
|
|
156
|
+
isExtension &&
|
|
157
|
+
(xaiOptions?.resolution != null || options.resolution != null)
|
|
158
|
+
) {
|
|
159
|
+
warnings.push({
|
|
160
|
+
type: 'unsupported',
|
|
161
|
+
feature: 'resolution',
|
|
162
|
+
details: 'xAI video extension does not support custom resolution.',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
120
166
|
const body: Record<string, unknown> = {
|
|
121
167
|
model: this.modelId,
|
|
122
168
|
prompt: options.prompt,
|
|
123
169
|
};
|
|
124
170
|
|
|
125
|
-
|
|
171
|
+
const allowDuration = !isEdit;
|
|
172
|
+
const allowAspectRatio = !isEdit && !isExtension;
|
|
173
|
+
const allowResolution = !isEdit && !isExtension;
|
|
174
|
+
|
|
175
|
+
if (allowDuration && options.duration != null) {
|
|
126
176
|
body.duration = options.duration;
|
|
127
177
|
}
|
|
128
178
|
|
|
129
|
-
if (
|
|
179
|
+
if (allowAspectRatio && options.aspectRatio != null) {
|
|
130
180
|
body.aspect_ratio = options.aspectRatio;
|
|
131
181
|
}
|
|
132
182
|
|
|
133
|
-
if (
|
|
183
|
+
if (allowResolution && xaiOptions?.resolution != null) {
|
|
134
184
|
body.resolution = xaiOptions.resolution;
|
|
135
|
-
} else if (
|
|
185
|
+
} else if (allowResolution && options.resolution != null) {
|
|
136
186
|
const mapped = RESOLUTION_MAP[options.resolution];
|
|
137
187
|
if (mapped != null) {
|
|
138
188
|
body.resolution = mapped;
|
|
@@ -147,12 +197,17 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
147
197
|
}
|
|
148
198
|
}
|
|
149
199
|
|
|
150
|
-
// Video editing: pass source video URL (nested object
|
|
151
|
-
if (
|
|
152
|
-
body.video = { url: xaiOptions
|
|
200
|
+
// Video editing: pass source video URL (nested object)
|
|
201
|
+
if (isEdit) {
|
|
202
|
+
body.video = { url: xaiOptions!.videoUrl };
|
|
153
203
|
}
|
|
154
204
|
|
|
155
|
-
//
|
|
205
|
+
// Video extension: pass source video URL (nested object)
|
|
206
|
+
if (isExtension) {
|
|
207
|
+
body.video = { url: xaiOptions!.videoUrl };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Convert SDK image input to the nested xAI request image object
|
|
156
211
|
if (options.image != null) {
|
|
157
212
|
if (options.image.type === 'url') {
|
|
158
213
|
body.image = { url: options.image.url };
|
|
@@ -167,14 +222,23 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
167
222
|
}
|
|
168
223
|
}
|
|
169
224
|
|
|
225
|
+
// Reference images for R2V (reference-to-video) generation
|
|
226
|
+
if (hasReferenceImages) {
|
|
227
|
+
body.reference_images = xaiOptions!.referenceImageUrls!.map(url => ({
|
|
228
|
+
url,
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
|
|
170
232
|
if (xaiOptions != null) {
|
|
171
233
|
for (const [key, value] of Object.entries(xaiOptions)) {
|
|
172
234
|
if (
|
|
173
235
|
![
|
|
236
|
+
'mode',
|
|
174
237
|
'pollIntervalMs',
|
|
175
238
|
'pollTimeoutMs',
|
|
176
239
|
'resolution',
|
|
177
240
|
'videoUrl',
|
|
241
|
+
'referenceImageUrls',
|
|
178
242
|
].includes(key)
|
|
179
243
|
) {
|
|
180
244
|
body[key] = value;
|
|
@@ -184,9 +248,19 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
184
248
|
|
|
185
249
|
const baseURL = this.config.baseURL ?? 'https://api.x.ai/v1';
|
|
186
250
|
|
|
187
|
-
//
|
|
251
|
+
// Determine endpoint based on mode
|
|
252
|
+
let endpoint: string;
|
|
253
|
+
if (isEdit) {
|
|
254
|
+
endpoint = `${baseURL}/videos/edits`;
|
|
255
|
+
} else if (isExtension) {
|
|
256
|
+
endpoint = `${baseURL}/videos/extensions`;
|
|
257
|
+
} else {
|
|
258
|
+
endpoint = `${baseURL}/videos/generations`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Step 1: Create video generation/edit/extension request
|
|
188
262
|
const { value: createResponse } = await postJsonToApi({
|
|
189
|
-
url:
|
|
263
|
+
url: endpoint,
|
|
190
264
|
headers: combineHeaders(this.config.headers(), options.headers),
|
|
191
265
|
body,
|
|
192
266
|
failedResponseHandler: xaiFailedResponseHandler,
|
|
@@ -279,6 +353,9 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
279
353
|
...(statusResponse.usage?.cost_in_usd_ticks != null
|
|
280
354
|
? { costInUsdTicks: statusResponse.usage.cost_in_usd_ticks }
|
|
281
355
|
: {}),
|
|
356
|
+
...(statusResponse.progress != null
|
|
357
|
+
? { progress: statusResponse.progress }
|
|
358
|
+
: {}),
|
|
282
359
|
},
|
|
283
360
|
},
|
|
284
361
|
};
|
|
@@ -291,6 +368,13 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
291
368
|
});
|
|
292
369
|
}
|
|
293
370
|
|
|
371
|
+
if (statusResponse.status === 'failed') {
|
|
372
|
+
throw new AISDKError({
|
|
373
|
+
name: 'XAI_VIDEO_GENERATION_FAILED',
|
|
374
|
+
message: 'Video generation failed.',
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
294
378
|
// 'pending' → continue polling
|
|
295
379
|
}
|
|
296
380
|
}
|
|
@@ -315,4 +399,11 @@ const xaiVideoStatusResponseSchema = z.object({
|
|
|
315
399
|
cost_in_usd_ticks: z.number().nullish(),
|
|
316
400
|
})
|
|
317
401
|
.nullish(),
|
|
402
|
+
progress: z.number().nullish(),
|
|
403
|
+
error: z
|
|
404
|
+
.object({
|
|
405
|
+
code: z.string().nullish(),
|
|
406
|
+
message: z.string().nullish(),
|
|
407
|
+
})
|
|
408
|
+
.nullish(),
|
|
318
409
|
});
|
package/src/xai-video-options.ts
CHANGED
|
@@ -1,23 +1,145 @@
|
|
|
1
1
|
import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const nonEmptyStringSchema = z.string().min(1);
|
|
5
|
+
const resolutionSchema = z.enum(['480p', '720p']);
|
|
6
|
+
const modeSchema = z.enum(['edit-video', 'extend-video', 'reference-to-video']);
|
|
7
|
+
|
|
8
|
+
export type XaiVideoMode = z.infer<typeof modeSchema>;
|
|
9
|
+
type XaiVideoResolution = z.infer<typeof resolutionSchema>;
|
|
10
|
+
|
|
11
|
+
interface XaiVideoSharedOptions {
|
|
5
12
|
pollIntervalMs?: number | null;
|
|
6
13
|
pollTimeoutMs?: number | null;
|
|
7
|
-
resolution?:
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
resolution?: XaiVideoResolution | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface XaiVideoEditModeOptions extends XaiVideoSharedOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Select edit-video mode explicitly for best autocomplete and narrowing.
|
|
20
|
+
*/
|
|
21
|
+
mode: 'edit-video';
|
|
22
|
+
/** Source video URL to edit. */
|
|
23
|
+
videoUrl: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface XaiVideoExtendModeOptions extends XaiVideoSharedOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Select extend-video mode explicitly for best autocomplete and narrowing.
|
|
29
|
+
*/
|
|
30
|
+
mode: 'extend-video';
|
|
31
|
+
/** Source video URL to extend from its last frame. */
|
|
32
|
+
videoUrl: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface XaiVideoReferenceToVideoOptions extends XaiVideoSharedOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Select reference-to-video mode explicitly for best autocomplete and narrowing.
|
|
38
|
+
*/
|
|
39
|
+
mode: 'reference-to-video';
|
|
40
|
+
/** Reference image URLs (1-7) for R2V generation. */
|
|
41
|
+
referenceImageUrls: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface XaiVideoGenerationOptions extends XaiVideoSharedOptions {
|
|
45
|
+
mode?: undefined;
|
|
46
|
+
videoUrl?: undefined;
|
|
47
|
+
referenceImageUrls?: undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface XaiLegacyEditVideoOptions extends XaiVideoSharedOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Legacy backward-compatible shape: omitting `mode` while providing
|
|
53
|
+
* `videoUrl` behaves like edit-video.
|
|
54
|
+
*/
|
|
55
|
+
mode?: undefined;
|
|
56
|
+
videoUrl: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface XaiLegacyReferenceToVideoOptions extends XaiVideoSharedOptions {
|
|
60
|
+
/**
|
|
61
|
+
* Legacy backward-compatible shape: omitting `mode` while providing
|
|
62
|
+
* `referenceImageUrls` behaves like reference-to-video.
|
|
63
|
+
*/
|
|
64
|
+
mode?: undefined;
|
|
65
|
+
referenceImageUrls: string[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Provider options for xAI video generation.
|
|
70
|
+
*
|
|
71
|
+
* Use the `mode` option to select the operation:
|
|
72
|
+
*
|
|
73
|
+
* - `'edit-video'` + `videoUrl` -- video editing (`POST /v1/videos/edits`)
|
|
74
|
+
* - `'extend-video'` + `videoUrl` -- video extension (`POST /v1/videos/extensions`)
|
|
75
|
+
* - `'reference-to-video'` + `referenceImageUrls` -- R2V generation (`POST /v1/videos/generations`)
|
|
76
|
+
* - no `mode` -- standard generation from text prompts or image input
|
|
77
|
+
*
|
|
78
|
+
* Runtime remains backward compatible with legacy auto-detected provider
|
|
79
|
+
* options, but the public TypeScript type is intentionally explicit so editors
|
|
80
|
+
* can suggest valid modes and flag invalid field combinations.
|
|
81
|
+
*/
|
|
82
|
+
export type XaiVideoModelOptions =
|
|
83
|
+
| XaiVideoGenerationOptions
|
|
84
|
+
| XaiVideoEditModeOptions
|
|
85
|
+
| XaiVideoExtendModeOptions
|
|
86
|
+
| XaiVideoReferenceToVideoOptions
|
|
87
|
+
| XaiLegacyEditVideoOptions
|
|
88
|
+
| XaiLegacyReferenceToVideoOptions;
|
|
89
|
+
|
|
90
|
+
// ── Runtime schemas ───────────────────────────────────────────────────
|
|
91
|
+
const baseFields = {
|
|
92
|
+
pollIntervalMs: z.number().positive().nullish(),
|
|
93
|
+
pollTimeoutMs: z.number().positive().nullish(),
|
|
94
|
+
resolution: resolutionSchema.nullish(),
|
|
10
95
|
};
|
|
11
96
|
|
|
97
|
+
const editVideoSchema = z.object({
|
|
98
|
+
...baseFields,
|
|
99
|
+
mode: z.literal('edit-video'),
|
|
100
|
+
videoUrl: nonEmptyStringSchema,
|
|
101
|
+
referenceImageUrls: z.undefined().optional(),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const extendVideoSchema = z.object({
|
|
105
|
+
...baseFields,
|
|
106
|
+
mode: z.literal('extend-video'),
|
|
107
|
+
videoUrl: nonEmptyStringSchema,
|
|
108
|
+
referenceImageUrls: z.undefined().optional(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const referenceToVideoSchema = z.object({
|
|
112
|
+
...baseFields,
|
|
113
|
+
mode: z.literal('reference-to-video'),
|
|
114
|
+
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7),
|
|
115
|
+
videoUrl: z.undefined().optional(),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const autoDetectSchema = z.object({
|
|
119
|
+
...baseFields,
|
|
120
|
+
mode: z.undefined().optional(),
|
|
121
|
+
videoUrl: nonEmptyStringSchema.optional(),
|
|
122
|
+
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export const xaiVideoModelOptions = z.union([
|
|
126
|
+
editVideoSchema,
|
|
127
|
+
extendVideoSchema,
|
|
128
|
+
referenceToVideoSchema,
|
|
129
|
+
autoDetectSchema,
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const runtimeSchema = z
|
|
133
|
+
.object({
|
|
134
|
+
mode: modeSchema.optional(),
|
|
135
|
+
videoUrl: nonEmptyStringSchema.optional(),
|
|
136
|
+
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
|
|
137
|
+
...baseFields,
|
|
138
|
+
})
|
|
139
|
+
.passthrough();
|
|
140
|
+
|
|
141
|
+
export type XaiParsedVideoModelOptions = z.infer<typeof runtimeSchema>;
|
|
142
|
+
|
|
12
143
|
export const xaiVideoModelOptionsSchema = lazySchema(() =>
|
|
13
|
-
zodSchema(
|
|
14
|
-
z
|
|
15
|
-
.object({
|
|
16
|
-
pollIntervalMs: z.number().positive().nullish(),
|
|
17
|
-
pollTimeoutMs: z.number().positive().nullish(),
|
|
18
|
-
resolution: z.enum(['480p', '720p']).nullish(),
|
|
19
|
-
videoUrl: z.string().nullish(),
|
|
20
|
-
})
|
|
21
|
-
.passthrough(),
|
|
22
|
-
),
|
|
144
|
+
zodSchema(runtimeSchema),
|
|
23
145
|
);
|