@ai-sdk/xai 3.0.119 → 3.0.121
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 +13 -0
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +112 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +112 -32
- package/dist/index.mjs.map +1 -1
- package/docs/01-xai.mdx +117 -9
- package/package.json +1 -1
- package/src/responses/xai-responses-api.ts +2 -0
- package/src/responses/xai-responses-language-model.ts +14 -0
- package/src/responses/xai-responses-options.ts +1 -0
- package/src/xai-chat-language-model.ts +19 -0
- package/src/xai-chat-options.ts +2 -0
- package/src/xai-video-model.ts +108 -22
- package/src/xai-video-options.ts +21 -1
- package/src/xai-video-settings.ts +4 -1
package/docs/01-xai.mdx
CHANGED
|
@@ -160,6 +160,37 @@ The following optional provider options are available for xAI chat models:
|
|
|
160
160
|
|
|
161
161
|
Whether to enable parallel function calling during tool use. When true, the model can call multiple functions in parallel. When false, the model will call functions sequentially. Defaults to `true`.
|
|
162
162
|
|
|
163
|
+
### Priority Processing
|
|
164
|
+
|
|
165
|
+
`providerOptions.xai.serviceTier` requests higher scheduling priority, which
|
|
166
|
+
typically lowers time-to-first-token and speeds up inter-token latency. This
|
|
167
|
+
works for both the Responses API (default) and the Chat Completions API
|
|
168
|
+
(`xai.chat()`).
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { xai } from '@ai-sdk/xai';
|
|
172
|
+
import { generateText } from 'ai';
|
|
173
|
+
|
|
174
|
+
const { providerMetadata } = await generateText({
|
|
175
|
+
model: xai('grok-4.6'),
|
|
176
|
+
prompt: 'Explain quantum entanglement.',
|
|
177
|
+
providerOptions: {
|
|
178
|
+
xai: { serviceTier: 'priority' },
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// 'priority' when the request was served at the priority tier,
|
|
183
|
+
// 'default' when priority capacity was unavailable.
|
|
184
|
+
console.log(providerMetadata?.xai?.serviceTier);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Priority requests are billed at a premium per-token rate, and xAI only charges
|
|
188
|
+
that rate when the response confirms the priority tier — so read the applied
|
|
189
|
+
tier back from `providerMetadata.xai.serviceTier` rather than assuming the
|
|
190
|
+
request you sent is the tier you got. Omitting the option is equivalent to
|
|
191
|
+
`'default'`. See xAI's [priority processing
|
|
192
|
+
docs](https://docs.x.ai/developers/advanced-api-usage/priority-processing).
|
|
193
|
+
|
|
163
194
|
## Responses API (Agentic Tools)
|
|
164
195
|
|
|
165
196
|
You can use the xAI Responses API with the `xai.responses(modelId)` factory method for server-side agentic tool calling. This enables the model to autonomously orchestrate tool calls and research on xAI's servers.
|
|
@@ -537,6 +568,10 @@ The following provider options are available:
|
|
|
537
568
|
|
|
538
569
|
The ID of the previous response from the model. You can use it to continue a conversation.
|
|
539
570
|
|
|
571
|
+
- **serviceTier** _'default' | 'priority'_
|
|
572
|
+
|
|
573
|
+
Scheduling priority for the request. `'priority'` buys lower time-to-first-token and faster inter-token latency at a premium per-token price. The tier xAI actually applied comes back on `providerMetadata.xai.serviceTier`, and is `'default'` when priority capacity was unavailable. See [Priority Processing](https://docs.x.ai/developers/advanced-api-usage/priority-processing).
|
|
574
|
+
|
|
540
575
|
<Note>
|
|
541
576
|
The Responses API only supports server-side tools. You cannot mix server-side
|
|
542
577
|
tools with client-side function tools in the same request.
|
|
@@ -1238,12 +1273,64 @@ const { video } = await generateVideo({
|
|
|
1238
1273
|
});
|
|
1239
1274
|
```
|
|
1240
1275
|
|
|
1276
|
+
`inputReferences` accepts image references only. A reference with a non-image
|
|
1277
|
+
media type (for example a video or audio clip) is ignored with a warning, and a
|
|
1278
|
+
reference with no media type is treated as an image. If no image reference
|
|
1279
|
+
remains, reference-to-video is not selected and no reference images are sent.
|
|
1280
|
+
|
|
1281
|
+
#### Reference Audio
|
|
1282
|
+
|
|
1283
|
+
Reference-to-video can also give the subject a voice. `referenceVoiceIds` takes
|
|
1284
|
+
up to 3 xAI **preset** voice ids — you cannot upload your own audio clips.
|
|
1285
|
+
Reference the voices from the prompt with `<AUDIO_0>`, `<AUDIO_1>`, and
|
|
1286
|
+
`<AUDIO_2>`, in the order the voices are passed.
|
|
1287
|
+
|
|
1288
|
+
```ts
|
|
1289
|
+
import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
|
|
1290
|
+
import { experimental_generateVideo as generateVideo } from 'ai';
|
|
1291
|
+
|
|
1292
|
+
const { video } = await generateVideo({
|
|
1293
|
+
model: xai.video('grok-imagine-video-1.5'),
|
|
1294
|
+
prompt:
|
|
1295
|
+
'The person from <IMAGE_1> stands in the room from <IMAGE_2> and speaks ' +
|
|
1296
|
+
'to the camera with the voice from <AUDIO_0>.',
|
|
1297
|
+
aspectRatio: '9:16',
|
|
1298
|
+
duration: 10,
|
|
1299
|
+
providerOptions: {
|
|
1300
|
+
xai: {
|
|
1301
|
+
mode: 'reference-to-video',
|
|
1302
|
+
referenceImageUrls: [
|
|
1303
|
+
'https://example.com/person.png',
|
|
1304
|
+
'https://example.com/room.png',
|
|
1305
|
+
],
|
|
1306
|
+
referenceVoiceIds: ['eve'],
|
|
1307
|
+
resolution: '720p',
|
|
1308
|
+
pollTimeoutMs: 600000,
|
|
1309
|
+
} satisfies XaiVideoModelOptions,
|
|
1310
|
+
},
|
|
1311
|
+
});
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
Valid voice ids come from the xAI
|
|
1315
|
+
[text-to-speech voice roster](https://docs.x.ai/developers/model-capabilities/audio/text-to-speech#voices).
|
|
1316
|
+
Ids are case-insensitive, and an unknown id returns a `400` response listing the
|
|
1317
|
+
available voices. `referenceVoiceIds` is ignored with a warning when the
|
|
1318
|
+
resolved operation is not reference-to-video.
|
|
1319
|
+
|
|
1320
|
+
<Note>
|
|
1321
|
+
xAI documents reference audio as available in the United States only, for
|
|
1322
|
+
trusted partners. Requests from accounts without access are rejected by the
|
|
1323
|
+
xAI API.
|
|
1324
|
+
</Note>
|
|
1325
|
+
|
|
1241
1326
|
<Note>
|
|
1242
1327
|
Reference-to-video supports `duration`, `aspectRatio`, and `resolution`. Use
|
|
1243
1328
|
`mode` to select the operation — each mode is mutually exclusive. When both
|
|
1244
1329
|
are provided, `frameImages` takes precedence over `inputReferences`, and
|
|
1245
1330
|
`inputReferences` takes precedence over the legacy `referenceImageUrls`
|
|
1246
|
-
provider option. Reference-to-video
|
|
1331
|
+
provider option. Reference-to-video is supported by the `grok-imagine-video`
|
|
1332
|
+
and `grok-imagine-video-1.5` models; native 1080p requires
|
|
1333
|
+
`grok-imagine-video-1.5`.
|
|
1247
1334
|
</Note>
|
|
1248
1335
|
|
|
1249
1336
|
### Video Provider Options
|
|
@@ -1259,11 +1346,14 @@ You can validate the provider options using the `XaiVideoModelOptions` type.
|
|
|
1259
1346
|
|
|
1260
1347
|
Maximum wait time in milliseconds for video generation. Defaults to 600000 (10 minutes).
|
|
1261
1348
|
|
|
1262
|
-
- **resolution** _'480p' | '720p'_
|
|
1349
|
+
- **resolution** _'480p' | '720p' | '1080p'_
|
|
1263
1350
|
|
|
1264
1351
|
Video resolution. When using the SDK's standard `resolution` parameter,
|
|
1265
|
-
`1280x720` maps to `720p
|
|
1266
|
-
Use this provider option to pass the native format directly.
|
|
1352
|
+
`1920x1080` maps to `1080p`, `1280x720` maps to `720p`, and `854x480` maps
|
|
1353
|
+
to `480p`. Use this provider option to pass the native format directly.
|
|
1354
|
+
`1080p` requires the `grok-imagine-video-1.5` model and is available for
|
|
1355
|
+
text-to-video and image-to-video; reference-to-video is capped at `720p`
|
|
1356
|
+
(a `1080p` request is downgraded with a warning).
|
|
1267
1357
|
|
|
1268
1358
|
- **user** _string_
|
|
1269
1359
|
|
|
@@ -1295,6 +1385,17 @@ You can validate the provider options using the `XaiVideoModelOptions` type.
|
|
|
1295
1385
|
`<IMAGE_1>`, `<IMAGE_2>`, etc. in the prompt to reference specific
|
|
1296
1386
|
images. Used with `mode: 'reference-to-video'`.
|
|
1297
1387
|
|
|
1388
|
+
- **referenceVoiceIds** _string[]_
|
|
1389
|
+
|
|
1390
|
+
Up to 3 xAI preset voice ids that give the subject a voice in
|
|
1391
|
+
reference-to-video (R2V) generation. Preset voices only — audio clips
|
|
1392
|
+
cannot be uploaded. Ids are case-insensitive and come from the
|
|
1393
|
+
[text-to-speech voice roster](https://docs.x.ai/developers/model-capabilities/audio/text-to-speech#voices);
|
|
1394
|
+
an unknown id returns a `400` listing the available voices. Use `<AUDIO_0>`,
|
|
1395
|
+
`<AUDIO_1>`, and `<AUDIO_2>` tags in the prompt to reference the voices.
|
|
1396
|
+
Ignored with a warning outside reference-to-video. Reference audio is
|
|
1397
|
+
documented as US-only and limited to trusted partners.
|
|
1398
|
+
|
|
1298
1399
|
<Note>
|
|
1299
1400
|
Video generation is an asynchronous process that can take several minutes.
|
|
1300
1401
|
Consider setting `pollTimeoutMs` to at least 10 minutes (600000ms) for
|
|
@@ -1305,7 +1406,9 @@ You can validate the provider options using the `XaiVideoModelOptions` type.
|
|
|
1305
1406
|
### Aspect Ratio and Resolution
|
|
1306
1407
|
|
|
1307
1408
|
For **text-to-video**, you can specify both `aspectRatio` and `resolution`.
|
|
1308
|
-
The default aspect ratio is `16:9` and the default resolution is `480p`.
|
|
1409
|
+
The default aspect ratio is `16:9` and the default resolution is `480p`. The
|
|
1410
|
+
`grok-imagine-video-1.5` model additionally supports native `1080p` for
|
|
1411
|
+
text-to-video and image-to-video.
|
|
1309
1412
|
|
|
1310
1413
|
For **image-to-video**, the output defaults to the input image's aspect ratio.
|
|
1311
1414
|
If you specify `aspectRatio`, it will override this and stretch the image to the
|
|
@@ -1321,13 +1424,18 @@ from the source video. `duration` is supported and controls only the
|
|
|
1321
1424
|
extension length.
|
|
1322
1425
|
|
|
1323
1426
|
For **reference-to-video (R2V)**, you can specify `duration`, `aspectRatio`,
|
|
1324
|
-
and `resolution
|
|
1427
|
+
and `resolution`. Unlike text-to-video, R2V is capped at `720p` — a `1080p`
|
|
1428
|
+
request is downgraded to `720p` with a warning.
|
|
1325
1429
|
|
|
1326
1430
|
### Video Model Capabilities
|
|
1327
1431
|
|
|
1328
|
-
| Model
|
|
1329
|
-
|
|
|
1330
|
-
| `grok-imagine-video`
|
|
1432
|
+
| Model | Duration | Aspect Ratios | Resolution | Image-to-Video | Editing | Extension | R2V |
|
|
1433
|
+
| ------------------------ | -------- | ------------------------------------------------- | ------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
|
|
1434
|
+
| `grok-imagine-video` | 1–15s | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3` | `480p`, `720p` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
|
1435
|
+
| `grok-imagine-video-1.5` | 1–15s | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3` | `480p`, `720p`, `1080p`\* | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
|
1436
|
+
|
|
1437
|
+
\* Native `1080p` applies to text-to-video and image-to-video. Reference-to-video
|
|
1438
|
+
is capped at `720p` — a `1080p` request is downgraded with a warning.
|
|
1331
1439
|
|
|
1332
1440
|
<Note>
|
|
1333
1441
|
You can also pass any available provider model ID as a string if needed.
|
package/package.json
CHANGED
|
@@ -274,6 +274,7 @@ export const xaiResponsesResponseSchema = z.object({
|
|
|
274
274
|
output: z.array(outputItemSchema),
|
|
275
275
|
usage: xaiResponsesUsageSchema.nullish(),
|
|
276
276
|
status: z.string(),
|
|
277
|
+
service_tier: z.string().nullish(),
|
|
277
278
|
});
|
|
278
279
|
|
|
279
280
|
export const xaiResponsesChunkSchema = z.union([
|
|
@@ -543,6 +544,7 @@ export const xaiResponsesChunkSchema = z.union([
|
|
|
543
544
|
response: z.object({
|
|
544
545
|
incomplete_details: z.object({ reason: z.string() }).nullish(),
|
|
545
546
|
usage: xaiResponsesUsageSchema.nullish(),
|
|
547
|
+
service_tier: z.string().nullish(),
|
|
546
548
|
}),
|
|
547
549
|
}),
|
|
548
550
|
z.object({
|
|
@@ -197,6 +197,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
197
197
|
...(options.previousResponseId != null && {
|
|
198
198
|
previous_response_id: options.previousResponseId,
|
|
199
199
|
}),
|
|
200
|
+
...(options.serviceTier != null && {
|
|
201
|
+
service_tier: options.serviceTier,
|
|
202
|
+
}),
|
|
200
203
|
};
|
|
201
204
|
|
|
202
205
|
if (xaiTools && xaiTools.length > 0) {
|
|
@@ -432,6 +435,11 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
432
435
|
inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
|
|
433
436
|
outputTokens: { total: 0, text: 0, reasoning: 0 },
|
|
434
437
|
},
|
|
438
|
+
...(response.service_tier != null && {
|
|
439
|
+
providerMetadata: {
|
|
440
|
+
xai: { serviceTier: response.service_tier },
|
|
441
|
+
},
|
|
442
|
+
}),
|
|
435
443
|
request: { body },
|
|
436
444
|
response: {
|
|
437
445
|
...getResponseMetadata(response),
|
|
@@ -477,6 +485,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
477
485
|
};
|
|
478
486
|
let hasFunctionCall = false;
|
|
479
487
|
let usage: LanguageModelV3Usage | undefined = undefined;
|
|
488
|
+
let serviceTier: string | undefined = undefined;
|
|
480
489
|
let isFirstChunk = true;
|
|
481
490
|
const contentBlocks: Record<string, { type: 'text' }> = {};
|
|
482
491
|
const seenToolCalls = new Set<string>();
|
|
@@ -670,6 +679,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
670
679
|
usage = convertXaiResponsesUsage(response.usage);
|
|
671
680
|
}
|
|
672
681
|
|
|
682
|
+
serviceTier = response.service_tier ?? undefined;
|
|
683
|
+
|
|
673
684
|
if (event.type === 'response.incomplete') {
|
|
674
685
|
const reason =
|
|
675
686
|
'incomplete_details' in response
|
|
@@ -1022,6 +1033,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
1022
1033
|
},
|
|
1023
1034
|
outputTokens: { total: 0, text: 0, reasoning: 0 },
|
|
1024
1035
|
},
|
|
1036
|
+
...(serviceTier != null && {
|
|
1037
|
+
providerMetadata: { xai: { serviceTier } },
|
|
1038
|
+
}),
|
|
1025
1039
|
});
|
|
1026
1040
|
},
|
|
1027
1041
|
}),
|
|
@@ -26,6 +26,7 @@ export const xaiLanguageModelResponsesOptions = z.object({
|
|
|
26
26
|
.optional(),
|
|
27
27
|
logprobs: z.boolean().optional(),
|
|
28
28
|
topLogprobs: z.number().int().min(0).max(8).optional(),
|
|
29
|
+
serviceTier: z.enum(['default', 'priority']).optional(),
|
|
29
30
|
/**
|
|
30
31
|
* Whether to store the input message(s) and model response for later retrieval.
|
|
31
32
|
* Must be set to `false` for teams with Zero Data Retention (ZDR) enabled,
|
|
@@ -135,6 +135,9 @@ export class XaiChatLanguageModel implements LanguageModelV3 {
|
|
|
135
135
|
seed,
|
|
136
136
|
reasoning_effort: options.reasoningEffort,
|
|
137
137
|
|
|
138
|
+
// scheduling priority
|
|
139
|
+
service_tier: options.serviceTier,
|
|
140
|
+
|
|
138
141
|
// parallel function calling
|
|
139
142
|
parallel_function_calling: options.parallel_function_calling,
|
|
140
143
|
|
|
@@ -301,6 +304,11 @@ export class XaiChatLanguageModel implements LanguageModelV3 {
|
|
|
301
304
|
inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
|
|
302
305
|
outputTokens: { total: 0, text: 0, reasoning: 0 },
|
|
303
306
|
},
|
|
307
|
+
...(response.service_tier != null && {
|
|
308
|
+
providerMetadata: {
|
|
309
|
+
xai: { serviceTier: response.service_tier },
|
|
310
|
+
},
|
|
311
|
+
}),
|
|
304
312
|
request: { body },
|
|
305
313
|
response: {
|
|
306
314
|
...getResponseMetadata(response),
|
|
@@ -380,6 +388,7 @@ export class XaiChatLanguageModel implements LanguageModelV3 {
|
|
|
380
388
|
raw: undefined,
|
|
381
389
|
};
|
|
382
390
|
let usage: LanguageModelV3Usage | undefined = undefined;
|
|
391
|
+
let serviceTier: string | undefined = undefined;
|
|
383
392
|
let isFirstChunk = true;
|
|
384
393
|
const contentBlocks: Record<
|
|
385
394
|
string,
|
|
@@ -439,6 +448,11 @@ export class XaiChatLanguageModel implements LanguageModelV3 {
|
|
|
439
448
|
usage = convertXaiChatUsage(value.usage);
|
|
440
449
|
}
|
|
441
450
|
|
|
451
|
+
// the applied tier is repeated on every chunk; keep the latest
|
|
452
|
+
if (value.service_tier != null) {
|
|
453
|
+
serviceTier = value.service_tier;
|
|
454
|
+
}
|
|
455
|
+
|
|
442
456
|
const choice = value.choices[0];
|
|
443
457
|
|
|
444
458
|
// update finish reason if present
|
|
@@ -598,6 +612,9 @@ export class XaiChatLanguageModel implements LanguageModelV3 {
|
|
|
598
612
|
},
|
|
599
613
|
outputTokens: { total: 0, text: 0, reasoning: 0 },
|
|
600
614
|
},
|
|
615
|
+
...(serviceTier != null && {
|
|
616
|
+
providerMetadata: { xai: { serviceTier } },
|
|
617
|
+
}),
|
|
601
618
|
});
|
|
602
619
|
},
|
|
603
620
|
}),
|
|
@@ -665,6 +682,7 @@ const xaiChatResponseSchema = z.object({
|
|
|
665
682
|
object: z.literal('chat.completion').nullish(),
|
|
666
683
|
usage: xaiUsageSchema.nullish(),
|
|
667
684
|
citations: z.array(z.string().url()).nullish(),
|
|
685
|
+
service_tier: z.string().nullish(),
|
|
668
686
|
code: z.string().nullish(),
|
|
669
687
|
error: z.string().nullish(),
|
|
670
688
|
});
|
|
@@ -698,6 +716,7 @@ const xaiChatChunkSchema = z.object({
|
|
|
698
716
|
),
|
|
699
717
|
usage: xaiUsageSchema.nullish(),
|
|
700
718
|
citations: z.array(z.string().url()).nullish(),
|
|
719
|
+
service_tier: z.string().nullish(),
|
|
701
720
|
});
|
|
702
721
|
|
|
703
722
|
const xaiStreamErrorSchema = z.object({
|
package/src/xai-chat-options.ts
CHANGED
|
@@ -59,6 +59,8 @@ export const xaiLanguageModelChatOptions = z.object({
|
|
|
59
59
|
logprobs: z.boolean().optional(),
|
|
60
60
|
topLogprobs: z.number().int().min(0).max(8).optional(),
|
|
61
61
|
|
|
62
|
+
serviceTier: z.enum(['default', 'priority']).optional(),
|
|
63
|
+
|
|
62
64
|
/**
|
|
63
65
|
* Whether to enable parallel function calling during tool use.
|
|
64
66
|
* When true, the model can call multiple functions in parallel.
|
package/src/xai-video-model.ts
CHANGED
|
@@ -37,6 +37,7 @@ interface XaiVideoModelConfig {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const RESOLUTION_MAP: Record<string, string> = {
|
|
40
|
+
'1920x1080': '1080p',
|
|
40
41
|
'1280x720': '720p',
|
|
41
42
|
'854x480': '480p',
|
|
42
43
|
'640x480': '480p',
|
|
@@ -74,6 +75,11 @@ function getTopLevelMediaType(mediaType: string): string {
|
|
|
74
75
|
const isVideoFile = (file: Experimental_VideoModelV3File): boolean =>
|
|
75
76
|
file.mediaType != null && getTopLevelMediaType(file.mediaType) === 'video';
|
|
76
77
|
|
|
78
|
+
// References without a media type (only possible for URLs) are treated as
|
|
79
|
+
// images, matching the legacy `referenceImageUrls` behavior.
|
|
80
|
+
const isImageReference = (file: Experimental_VideoModelV3File): boolean =>
|
|
81
|
+
file.mediaType == null || getTopLevelMediaType(file.mediaType) === 'image';
|
|
82
|
+
|
|
77
83
|
function fileToXaiImageUrl(file: Experimental_VideoModelV3File): string {
|
|
78
84
|
if (file.type === 'url') {
|
|
79
85
|
return file.url;
|
|
@@ -88,32 +94,40 @@ function fileToXaiImageUrl(file: Experimental_VideoModelV3File): string {
|
|
|
88
94
|
|
|
89
95
|
// Resolves the reference images for R2V generation. First-class
|
|
90
96
|
// `inputReferences` win over the legacy `referenceImageUrls` provider option.
|
|
91
|
-
//
|
|
92
|
-
// with a warning.
|
|
97
|
+
// Non-image references (video or audio) are not supported for
|
|
98
|
+
// reference-to-video and are skipped with a warning.
|
|
93
99
|
function resolveReferenceImages(
|
|
94
100
|
options: XaiVideoDoGenerateOptions,
|
|
95
101
|
xaiOptions: XaiParsedVideoModelOptions | undefined,
|
|
96
102
|
warnings: SharedV3Warning[],
|
|
97
103
|
): Array<{ url: string }> | undefined {
|
|
98
104
|
if (options.inputReferences != null && options.inputReferences.length > 0) {
|
|
99
|
-
const
|
|
100
|
-
|
|
105
|
+
const imageFiles: Experimental_VideoModelV3File[] = [];
|
|
106
|
+
|
|
107
|
+
for (const reference of options.inputReferences) {
|
|
108
|
+
if (!isImageReference(reference)) {
|
|
101
109
|
warnings.push({
|
|
102
110
|
type: 'unsupported',
|
|
103
111
|
feature: 'inputReferences',
|
|
104
|
-
details:
|
|
105
|
-
'xAI reference-to-video accepts image references only. The
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
details: isVideoFile(reference)
|
|
113
|
+
? 'xAI reference-to-video accepts image references only. The ' +
|
|
114
|
+
'video reference was ignored. Use providerOptions.xai.mode ' +
|
|
115
|
+
'"extend-video" to continue from a video.'
|
|
116
|
+
: 'xAI reference-to-video accepts image references only. The ' +
|
|
117
|
+
'non-image reference was ignored.',
|
|
108
118
|
});
|
|
109
|
-
|
|
119
|
+
continue;
|
|
110
120
|
}
|
|
111
|
-
return true;
|
|
112
|
-
});
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
imageFiles.push(reference);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Every reference may have been filtered out (audio- or video-only input),
|
|
126
|
+
// so collapse an empty list to undefined rather than sending an empty
|
|
127
|
+
// `reference_images` array.
|
|
128
|
+
return imageFiles.length > 0
|
|
129
|
+
? imageFiles.map(reference => ({ url: fileToXaiImageUrl(reference) }))
|
|
130
|
+
: undefined;
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
if (
|
|
@@ -126,6 +140,11 @@ function resolveReferenceImages(
|
|
|
126
140
|
return undefined;
|
|
127
141
|
}
|
|
128
142
|
|
|
143
|
+
// True when at least one reference would survive as an image.
|
|
144
|
+
function hasImageInputReference(options: XaiVideoDoGenerateOptions): boolean {
|
|
145
|
+
return options.inputReferences?.some(isImageReference) ?? false;
|
|
146
|
+
}
|
|
147
|
+
|
|
129
148
|
function resolveVideoMode(
|
|
130
149
|
options: XaiVideoDoGenerateOptions,
|
|
131
150
|
xaiOptions: XaiParsedVideoModelOptions | undefined,
|
|
@@ -142,13 +161,17 @@ function resolveVideoMode(
|
|
|
142
161
|
// only auto-select reference-to-video when no frame images are provided.
|
|
143
162
|
const hasFrameImages =
|
|
144
163
|
options.frameImages != null && options.frameImages.length > 0;
|
|
145
|
-
const hasInputReferences =
|
|
146
|
-
options.inputReferences != null && options.inputReferences.length > 0;
|
|
147
164
|
const hasLegacyReferenceUrls =
|
|
148
165
|
xaiOptions?.referenceImageUrls != null &&
|
|
149
166
|
xaiOptions.referenceImageUrls.length > 0;
|
|
150
167
|
|
|
151
|
-
|
|
168
|
+
// Reference-to-video needs at least one image reference. An audio-only (or
|
|
169
|
+
// video-only) `inputReferences` array must not flip a text- or
|
|
170
|
+
// image-to-video request into R2V.
|
|
171
|
+
if (
|
|
172
|
+
!hasFrameImages &&
|
|
173
|
+
(hasImageInputReference(options) || hasLegacyReferenceUrls)
|
|
174
|
+
) {
|
|
152
175
|
return 'reference-to-video';
|
|
153
176
|
}
|
|
154
177
|
|
|
@@ -289,7 +312,8 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
289
312
|
feature: 'resolution',
|
|
290
313
|
details:
|
|
291
314
|
`Unrecognized resolution "${options.resolution}". ` +
|
|
292
|
-
'Use providerOptions.xai.resolution with "480p"
|
|
315
|
+
'Use providerOptions.xai.resolution with "480p", "720p", or ' +
|
|
316
|
+
'"1080p" instead.',
|
|
293
317
|
});
|
|
294
318
|
}
|
|
295
319
|
}
|
|
@@ -346,13 +370,57 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
346
370
|
xaiOptions,
|
|
347
371
|
warnings,
|
|
348
372
|
);
|
|
373
|
+
|
|
349
374
|
if (referenceImages != null) {
|
|
350
375
|
body.reference_images = referenceImages;
|
|
376
|
+
} else {
|
|
377
|
+
// Explicit R2V with no usable image references would silently send
|
|
378
|
+
// a plain generations request; tell the user it is no longer R2V.
|
|
379
|
+
warnings.push({
|
|
380
|
+
type: 'unsupported',
|
|
381
|
+
feature: 'referenceImages',
|
|
382
|
+
details:
|
|
383
|
+
'xAI reference-to-video requires at least one image reference. ' +
|
|
384
|
+
'The video will be generated without reference images.',
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const referenceVoiceIds = xaiOptions?.referenceVoiceIds;
|
|
389
|
+
if (referenceVoiceIds != null && referenceVoiceIds.length > 0) {
|
|
390
|
+
body.reference_audios = referenceVoiceIds.map(voiceId => ({
|
|
391
|
+
voice_id: voiceId,
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Reference-to-video is capped at 720p; downgrade a 1080p request.
|
|
396
|
+
if (body.resolution === '1080p') {
|
|
397
|
+
warnings.push({
|
|
398
|
+
type: 'unsupported',
|
|
399
|
+
feature: 'resolution',
|
|
400
|
+
details:
|
|
401
|
+
'xAI reference-to-video is limited to 720p. The request was ' +
|
|
402
|
+
'downgraded from 1080p to 720p.',
|
|
403
|
+
});
|
|
404
|
+
body.resolution = '720p';
|
|
351
405
|
}
|
|
352
406
|
}
|
|
353
407
|
|
|
354
|
-
//
|
|
355
|
-
//
|
|
408
|
+
// 1080p requires grok-imagine-video-1.5; the original grok-imagine-video
|
|
409
|
+
// rejects it. Warn, but send the request as the user asked.
|
|
410
|
+
if (body.resolution === '1080p' && this.modelId === 'grok-imagine-video') {
|
|
411
|
+
warnings.push({
|
|
412
|
+
type: 'unsupported',
|
|
413
|
+
feature: 'resolution',
|
|
414
|
+
details:
|
|
415
|
+
'xAI model "grok-imagine-video" does not support 1080p. Use ' +
|
|
416
|
+
'"grok-imagine-video-1.5" for 1080p, or a lower resolution. The ' +
|
|
417
|
+
'request was sent with 1080p.',
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Warn when references were provided but cannot be used in the resolved
|
|
422
|
+
// mode (e.g. alongside frameImages, in edit/extend modes, or when the
|
|
423
|
+
// references carried no usable image to drive reference-to-video).
|
|
356
424
|
if (
|
|
357
425
|
options.inputReferences != null &&
|
|
358
426
|
options.inputReferences.length > 0 &&
|
|
@@ -361,9 +429,26 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
361
429
|
warnings.push({
|
|
362
430
|
type: 'unsupported',
|
|
363
431
|
feature: 'inputReferences',
|
|
432
|
+
details: hasImageInputReference(options)
|
|
433
|
+
? 'xAI only supports inputReferences for reference-to-video ' +
|
|
434
|
+
'generation. The reference images were ignored.'
|
|
435
|
+
: 'xAI reference-to-video requires at least one image reference. ' +
|
|
436
|
+
'The references were ignored.',
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Preset reference voices only apply to reference-to-video generation.
|
|
441
|
+
if (
|
|
442
|
+
xaiOptions?.referenceVoiceIds != null &&
|
|
443
|
+
xaiOptions.referenceVoiceIds.length > 0 &&
|
|
444
|
+
!hasReferenceImages
|
|
445
|
+
) {
|
|
446
|
+
warnings.push({
|
|
447
|
+
type: 'unsupported',
|
|
448
|
+
feature: 'referenceVoiceIds',
|
|
364
449
|
details:
|
|
365
|
-
'xAI only supports
|
|
366
|
-
'generation. The reference
|
|
450
|
+
'xAI only supports reference voices for reference-to-video ' +
|
|
451
|
+
'generation. The reference voices were ignored.',
|
|
367
452
|
});
|
|
368
453
|
}
|
|
369
454
|
|
|
@@ -381,6 +466,7 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
381
466
|
'resolution',
|
|
382
467
|
'videoUrl',
|
|
383
468
|
'referenceImageUrls',
|
|
469
|
+
'referenceVoiceIds',
|
|
384
470
|
'user',
|
|
385
471
|
].includes(key)
|
|
386
472
|
) {
|
package/src/xai-video-options.ts
CHANGED
|
@@ -2,7 +2,7 @@ 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']);
|
|
5
|
+
const resolutionSchema = z.enum(['480p', '720p', '1080p']);
|
|
6
6
|
const modeSchema = z.enum(['edit-video', 'extend-video', 'reference-to-video']);
|
|
7
7
|
|
|
8
8
|
export type XaiVideoMode = z.infer<typeof modeSchema>;
|
|
@@ -48,6 +48,10 @@ interface XaiVideoReferenceToVideoOptions
|
|
|
48
48
|
mode: 'reference-to-video';
|
|
49
49
|
/** Reference image URLs (1-7) for R2V generation. */
|
|
50
50
|
referenceImageUrls: string[];
|
|
51
|
+
/**
|
|
52
|
+
* Preset voice ids (up to 3) that give the subject a voice.
|
|
53
|
+
*/
|
|
54
|
+
referenceVoiceIds?: string[];
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
interface XaiVideoGenerationOptions
|
|
@@ -75,6 +79,10 @@ interface XaiLegacyReferenceToVideoOptions
|
|
|
75
79
|
*/
|
|
76
80
|
mode?: undefined;
|
|
77
81
|
referenceImageUrls: string[];
|
|
82
|
+
/**
|
|
83
|
+
* Preset voice ids (up to 3) that give the subject a voice.
|
|
84
|
+
*/
|
|
85
|
+
referenceVoiceIds?: string[];
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
/**
|
|
@@ -87,6 +95,9 @@ interface XaiLegacyReferenceToVideoOptions
|
|
|
87
95
|
* - `'reference-to-video'` + `referenceImageUrls` -- R2V generation (`POST /v1/videos/generations`)
|
|
88
96
|
* - no `mode` -- standard generation from text prompts or image input
|
|
89
97
|
*
|
|
98
|
+
* Reference images may also come from the top-level `inputReferences` option
|
|
99
|
+
* instead of `referenceImageUrls`.
|
|
100
|
+
*
|
|
90
101
|
* Runtime remains backward compatible with legacy auto-detected provider
|
|
91
102
|
* options, but the public TypeScript type is intentionally explicit so editors
|
|
92
103
|
* can suggest valid modes and flag invalid field combinations.
|
|
@@ -110,12 +121,17 @@ const userField = {
|
|
|
110
121
|
user: z.string().optional(),
|
|
111
122
|
};
|
|
112
123
|
|
|
124
|
+
const referenceVoiceIdsField = {
|
|
125
|
+
referenceVoiceIds: z.array(nonEmptyStringSchema).max(3).optional(),
|
|
126
|
+
};
|
|
127
|
+
|
|
113
128
|
const editVideoSchema = z.object({
|
|
114
129
|
...baseFields,
|
|
115
130
|
...userField,
|
|
116
131
|
mode: z.literal('edit-video'),
|
|
117
132
|
videoUrl: nonEmptyStringSchema,
|
|
118
133
|
referenceImageUrls: z.undefined().optional(),
|
|
134
|
+
referenceVoiceIds: z.undefined().optional(),
|
|
119
135
|
});
|
|
120
136
|
|
|
121
137
|
const extendVideoSchema = z.object({
|
|
@@ -123,11 +139,13 @@ const extendVideoSchema = z.object({
|
|
|
123
139
|
mode: z.literal('extend-video'),
|
|
124
140
|
videoUrl: nonEmptyStringSchema,
|
|
125
141
|
referenceImageUrls: z.undefined().optional(),
|
|
142
|
+
referenceVoiceIds: z.undefined().optional(),
|
|
126
143
|
});
|
|
127
144
|
|
|
128
145
|
const referenceToVideoSchema = z.object({
|
|
129
146
|
...baseFields,
|
|
130
147
|
...userField,
|
|
148
|
+
...referenceVoiceIdsField,
|
|
131
149
|
mode: z.literal('reference-to-video'),
|
|
132
150
|
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7),
|
|
133
151
|
videoUrl: z.undefined().optional(),
|
|
@@ -136,6 +154,7 @@ const referenceToVideoSchema = z.object({
|
|
|
136
154
|
const autoDetectSchema = z.object({
|
|
137
155
|
...baseFields,
|
|
138
156
|
...userField,
|
|
157
|
+
...referenceVoiceIdsField,
|
|
139
158
|
mode: z.undefined().optional(),
|
|
140
159
|
videoUrl: nonEmptyStringSchema.optional(),
|
|
141
160
|
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
|
|
@@ -153,6 +172,7 @@ const runtimeSchema = z
|
|
|
153
172
|
mode: modeSchema.optional(),
|
|
154
173
|
videoUrl: nonEmptyStringSchema.optional(),
|
|
155
174
|
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
|
|
175
|
+
referenceVoiceIds: z.array(nonEmptyStringSchema).max(3).optional(),
|
|
156
176
|
user: z.string().optional(),
|
|
157
177
|
...baseFields,
|
|
158
178
|
})
|