@ai-sdk/xai 5.0.7 → 5.0.10

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/docs/01-xai.mdx CHANGED
@@ -588,15 +588,39 @@ The following provider options are available:
588
588
 
589
589
  - **logprobs** _boolean_
590
590
 
591
- Return log probabilities for output tokens.
591
+ Return log probabilities for output tokens. This is silently ignored by `grok-4.20` and newer models.
592
592
 
593
593
  - **topLogprobs** _number_
594
594
 
595
- Number of most likely tokens to return per token position (0-8). When set, `logprobs` is automatically enabled.
595
+ Number of most likely tokens to return per token position (0-8). When set, `logprobs` is automatically enabled. This is silently ignored by `grok-4.20` and newer models.
596
596
 
597
- - **include** _Array<'file_search_call.results'>_
597
+ - **minP** _number_
598
+
599
+ Set the min-p sampling threshold between 0 and 1. Tokens whose probability is below `minP` times the most likely token's probability are excluded. Unlike the standard AI SDK `topK` option, `minP` is xAI-specific and must be set in `providerOptions.xai`.
600
+
601
+ - **maxTurns** _number_
602
+
603
+ Set the maximum number of assistant/server-side tool turns xAI may perform within one API request. This limits xAI's internal loop for provider-executed tools such as web search; it is separate from AI SDK's `stopWhen`, which limits the outer loop of AI SDK steps and API requests.
604
+
605
+ - **parallelToolCalls** _boolean_
606
+
607
+ Allow the model to call tools in parallel. Defaults to `true`.
608
+
609
+ - **promptCacheKey** _string_
610
+
611
+ Route requests with shared prompt prefixes to improve cache reuse.
612
+
613
+ - **safetyIdentifier** _string_
614
+
615
+ Provide a stable, preferably hashed end-user identifier for safety monitoring.
598
616
 
599
- Specify additional output data to include in the model response. Use `['file_search_call.results']` to include file search results with scores and content.
617
+ - **user** _string_
618
+
619
+ Provide an end-user identifier for abuse monitoring.
620
+
621
+ - **include** _Array<'file_search_call.results' | 'web_search_call.action.sources' | 'code_interpreter_call.outputs' | 'reasoning.encrypted_content' | 'no_inline_citations'>_
622
+
623
+ Include file-search results, web-search sources, code-interpreter outputs, or encrypted reasoning content, or disable inline citations.
600
624
 
601
625
  - **store** _boolean_
602
626
 
@@ -1062,7 +1086,7 @@ import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
1062
1086
  import { experimental_generateVideo as generateVideo } from 'ai';
1063
1087
 
1064
1088
  const { video } = await generateVideo({
1065
- model: xai.video('grok-imagine-video'),
1089
+ model: xai.video('grok-imagine-video-1.5'),
1066
1090
  prompt: 'A chicken flying into the sunset in the style of 90s anime.',
1067
1091
  aspectRatio: '16:9',
1068
1092
  duration: 5,
@@ -1084,7 +1108,7 @@ import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
1084
1108
  import { experimental_generateVideo as generateVideo } from 'ai';
1085
1109
 
1086
1110
  const { video } = await generateVideo({
1087
- model: xai.video('grok-imagine-video'),
1111
+ model: xai.video('grok-imagine-video-1.5'),
1088
1112
  prompt: {
1089
1113
  image: 'https://example.com/start-frame.png',
1090
1114
  text: 'The cat slowly turns its head and blinks',
@@ -1106,7 +1130,7 @@ import { experimental_generateVideo as generateVideo } from 'ai';
1106
1130
  import fs from 'node:fs';
1107
1131
 
1108
1132
  const { video } = await generateVideo({
1109
- model: xai.video('grok-imagine-video'),
1133
+ model: xai.video('grok-imagine-video-1.5'),
1110
1134
  prompt: 'The cat slowly turns its head and blinks',
1111
1135
  frameImages: [
1112
1136
  {
@@ -1124,9 +1148,9 @@ const { video } = await generateVideo({
1124
1148
  ```
1125
1149
 
1126
1150
  <Note>
1127
- xAI does not support first-last-frame interpolation. A `last_frame` entry in
1128
- `frameImages` is ignored with a warning — use the `extend-video` mode to
1129
- continue from a video's last frame instead.
1151
+ `grok-imagine-video-1.5` supports first-last-frame interpolation. Earlier
1152
+ models ignore a `last_frame` entry with a warning. Use `extend-video` when you
1153
+ want to continue an existing video rather than pin its final frame.
1130
1154
  </Note>
1131
1155
 
1132
1156
  ### Video Editing
@@ -1138,7 +1162,7 @@ import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
1138
1162
  import { experimental_generateVideo as generateVideo } from 'ai';
1139
1163
 
1140
1164
  const { video } = await generateVideo({
1141
- model: xai.video('grok-imagine-video'),
1165
+ model: xai.video('grok-imagine-video-1.5'),
1142
1166
  prompt: 'Give the person sunglasses and a hat',
1143
1167
  providerOptions: {
1144
1168
  xai: {
@@ -1176,7 +1200,7 @@ const providerOptions = {
1176
1200
 
1177
1201
  // Step 1: Apply an initial edit
1178
1202
  const step1 = await generateVideo({
1179
- model: xai.video('grok-imagine-video'),
1203
+ model: xai.video('grok-imagine-video-1.5'),
1180
1204
  prompt: 'Add a party hat to the person',
1181
1205
  providerOptions,
1182
1206
  });
@@ -1187,7 +1211,7 @@ const step1VideoUrl = step1.providerMetadata?.xai?.videoUrl as string;
1187
1211
  // Step 2: Apply two more edits concurrently, building on step 1
1188
1212
  const [withSunglasses, withScarf] = await Promise.all([
1189
1213
  generateVideo({
1190
- model: xai.video('grok-imagine-video'),
1214
+ model: xai.video('grok-imagine-video-1.5'),
1191
1215
  prompt: 'Add sunglasses',
1192
1216
  providerOptions: {
1193
1217
  xai: {
@@ -1198,7 +1222,7 @@ const [withSunglasses, withScarf] = await Promise.all([
1198
1222
  },
1199
1223
  }),
1200
1224
  generateVideo({
1201
- model: xai.video('grok-imagine-video'),
1225
+ model: xai.video('grok-imagine-video-1.5'),
1202
1226
  prompt: 'Add a scarf',
1203
1227
  providerOptions: {
1204
1228
  xai: {
@@ -1221,7 +1245,7 @@ import { experimental_generateVideo as generateVideo } from 'ai';
1221
1245
 
1222
1246
  // Step 1: Generate a source video
1223
1247
  const source = await generateVideo({
1224
- model: xai.video('grok-imagine-video'),
1248
+ model: xai.video('grok-imagine-video-1.5'),
1225
1249
  prompt: 'A cat sitting on a sunlit windowsill, tail gently swishing.',
1226
1250
  duration: 5,
1227
1251
  aspectRatio: '16:9',
@@ -1236,7 +1260,7 @@ const sourceUrl = source.providerMetadata?.xai?.videoUrl as string;
1236
1260
 
1237
1261
  // Step 2: Extend the video with a new scene
1238
1262
  const extended = await generateVideo({
1239
- model: xai.video('grok-imagine-video'),
1263
+ model: xai.video('grok-imagine-video-1.5'),
1240
1264
  prompt: 'The cat turns its head, notices a butterfly, and leaps off.',
1241
1265
  duration: 6,
1242
1266
  providerOptions: {
@@ -1264,7 +1288,7 @@ import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
1264
1288
  import { experimental_generateVideo as generateVideo } from 'ai';
1265
1289
 
1266
1290
  const { video } = await generateVideo({
1267
- model: xai.video('grok-imagine-video'),
1291
+ model: xai.video('grok-imagine-video-1.5'),
1268
1292
  prompt:
1269
1293
  'The comic cat from <IMAGE_1> and the comic dog from <IMAGE_2> ' +
1270
1294
  'are having a playful chase through a sunlit park. ' +
@@ -1294,7 +1318,7 @@ import { experimental_generateVideo as generateVideo } from 'ai';
1294
1318
  import fs from 'node:fs';
1295
1319
 
1296
1320
  const { video } = await generateVideo({
1297
- model: xai.video('grok-imagine-video'),
1321
+ model: xai.video('grok-imagine-video-1.5'),
1298
1322
  prompt:
1299
1323
  'The comic cat and the comic dog are having a playful chase ' +
1300
1324
  'through a sunlit park. Cinematic slow-motion, warm afternoon light.',
@@ -1312,17 +1336,17 @@ const { video } = await generateVideo({
1312
1336
  });
1313
1337
  ```
1314
1338
 
1315
- `inputReferences` accepts image references only. A reference with a non-image
1316
- media type (for example a video or audio clip) is ignored with a warning, and a
1317
- reference with no media type is treated as an image. If no image reference
1318
- remains, reference-to-video is not selected and no reference images are sent.
1339
+ `inputReferences` accepts image and audio references. Audio references must
1340
+ declare an audio media type and can be supplied alone or together with images;
1341
+ they are sent through `reference_audios`. A reference with no media type is
1342
+ treated as an image. Video references are ignored with a warning.
1319
1343
 
1320
1344
  #### Reference Audio
1321
1345
 
1322
- Reference-to-video can also give the subject a voice. `referenceVoiceIds` takes
1323
- up to 3 xAI **preset** voice ids — you cannot upload your own audio clips.
1324
- Reference the voices from the prompt with `<AUDIO_0>`, `<AUDIO_1>`, and
1325
- `<AUDIO_2>`, in the order the voices are passed.
1346
+ Reference-to-video can also give the subject a voice. Use `inputReferences` for
1347
+ caller-supplied audio clips, or `referenceVoiceIds` for up to 3 xAI preset voice
1348
+ ids. Reference them from the prompt with `<AUDIO_0>`, `<AUDIO_1>`, and
1349
+ `<AUDIO_2>`, in the order they are passed.
1326
1350
 
1327
1351
  ```ts
1328
1352
  import { xai, type XaiVideoModelOptions } from '@ai-sdk/xai';
@@ -1357,19 +1381,18 @@ available voices. `referenceVoiceIds` is ignored with a warning when the
1357
1381
  resolved operation is not reference-to-video.
1358
1382
 
1359
1383
  <Note>
1360
- xAI documents reference audio as available in the United States only, for
1361
- trusted partners. Requests from accounts without access are rejected by the
1362
- xAI API.
1384
+ Preset voices are generally available. Caller-supplied voice clips are
1385
+ available to trusted partners on request. xAI accepts at most three audio
1386
+ references per request.
1363
1387
  </Note>
1364
1388
 
1365
1389
  <Note>
1366
1390
  Reference-to-video supports `duration`, `aspectRatio`, and `resolution`. Use
1367
- `mode` to select the operation — each mode is mutually exclusive. When both
1368
- are provided, `frameImages` takes precedence over `inputReferences`, and
1391
+ `mode` to select the operation — each mode is mutually exclusive.
1369
1392
  `inputReferences` takes precedence over the legacy `referenceImageUrls`
1370
- provider option. Reference-to-video is supported by the `grok-imagine-video`
1371
- and `grok-imagine-video-1.5` models; native 1080p requires
1372
- `grok-imagine-video-1.5`.
1393
+ provider option and can be combined with pinned frames. Reference-to-video is
1394
+ supported by the `grok-imagine-video` and `grok-imagine-video-1.5` models;
1395
+ native 1080p requires `grok-imagine-video-1.5`.
1373
1396
  </Note>
1374
1397
 
1375
1398
  ### Video Provider Options
@@ -1435,6 +1458,42 @@ You can validate the provider options using the `XaiVideoModelOptions` type.
1435
1458
  Ignored with a warning outside reference-to-video. Reference audio is
1436
1459
  documented as US-only and limited to trusted partners.
1437
1460
 
1461
+ - **storageOptions** _object_
1462
+
1463
+ Stores the generated video in the xAI Files API. Set `filename` and
1464
+ optionally `expiresAfter` (up to 30 days) and `publicUrl`. Stored-file
1465
+ details and storage errors are returned in `providerMetadata.xai.fileOutput`
1466
+ and `providerMetadata.xai.storageError`.
1467
+
1468
+ - **keyframes** _Array\<\{ imageUrl: string; timestampSeconds: number \}\>_
1469
+
1470
+ Up to four mid-video image anchors. Each timestamp must fall strictly inside
1471
+ the video duration. Keyframes are supported by `grok-imagine-video-1.5` and
1472
+ are ignored with a warning for editing and extension operations.
1473
+
1474
+ ### Audio and Frame Controls
1475
+
1476
+ Use the provider-neutral `generateAudio` option to request or disable generated
1477
+ audio. Use `frameImages` for exact endpoint frames:
1478
+
1479
+ ```ts
1480
+ const { video } = await generateVideo({
1481
+ model: xai.video('grok-imagine-video-1.5'),
1482
+ prompt: 'A paper boat drifting down a rain-soaked street',
1483
+ generateAudio: false,
1484
+ frameImages: [
1485
+ {
1486
+ frameType: 'last_frame',
1487
+ image: 'https://example.com/final-frame.png',
1488
+ },
1489
+ ],
1490
+ });
1491
+ ```
1492
+
1493
+ `last_frame` is supported only by `grok-imagine-video-1.5`. The provider emits
1494
+ an unsupported-feature warning and omits the field for other model IDs. A
1495
+ `first_frame` is sent as xAI's standard `image` input.
1496
+
1438
1497
  <Note>
1439
1498
  Video generation is an asynchronous process that can take several minutes.
1440
1499
  Consider setting `pollTimeoutMs` to at least 10 minutes (600000ms) for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "5.0.7",
3
+ "version": "5.0.10",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -30,10 +30,10 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@ai-sdk/provider": "4.0.18",
33
- "@ai-sdk/provider-utils": "5.0.47"
33
+ "@ai-sdk/provider-utils": "5.0.49"
34
34
  },
35
35
  "devDependencies": {
36
- "@ai-sdk/test-server": "2.0.1",
36
+ "@ai-sdk/test-server": "2.0.2",
37
37
  "@types/node": "22.19.19",
38
38
  "@vercel/ai-tsconfig": "0.0.0",
39
39
  "tsup": "^8.5.1",
@@ -2,7 +2,10 @@ import { z } from 'zod/v4';
2
2
 
3
3
  export type XaiResponsesIncludeValue =
4
4
  | 'file_search_call.results'
5
- | 'reasoning.encrypted_content';
5
+ | 'web_search_call.action.sources'
6
+ | 'code_interpreter_call.outputs'
7
+ | 'reasoning.encrypted_content'
8
+ | 'no_inline_citations';
6
9
 
7
10
  export type XaiResponsesIncludeOptions =
8
11
  | Array<XaiResponsesIncludeValue>
@@ -317,6 +320,8 @@ export const xaiResponsesResponseSchema = z.object({
317
320
  usage: xaiResponsesUsageSchema.nullish(),
318
321
  status: z.string(),
319
322
  service_tier: z.string().nullish(),
323
+ prompt_cache_key: z.string().nullish(),
324
+ safety_identifier: z.string().nullish(),
320
325
  });
321
326
 
322
327
  export const xaiResponsesChunkSchema = z.union([
@@ -602,6 +607,8 @@ export const xaiResponsesChunkSchema = z.union([
602
607
  incomplete_details: z.object({ reason: z.string() }).nullish(),
603
608
  usage: xaiResponsesUsageSchema.nullish(),
604
609
  service_tier: z.string().nullish(),
610
+ prompt_cache_key: z.string().nullish(),
611
+ safety_identifier: z.string().nullish(),
605
612
  }),
606
613
  }),
607
614
  z.object({
@@ -26,9 +26,23 @@ export const xaiLanguageModelResponsesOptions = z.object({
26
26
  reasoningEffort: z
27
27
  .enum(['none', 'low', 'medium', 'high', 'xhigh'])
28
28
  .optional(),
29
+ /**
30
+ * @deprecated xAI ignores the requested summary level and always returns a
31
+ * detailed reasoning summary.
32
+ */
29
33
  reasoningSummary: z.enum(['auto', 'concise', 'detailed']).optional(),
30
34
  logprobs: z.boolean().optional(),
31
35
  topLogprobs: z.number().int().min(0).max(8).optional(),
36
+ /** Min-p sampling threshold between 0 and 1. */
37
+ minP: z.number().min(0).max(1).optional(),
38
+ /** Maximum number of agentic tool-calling turns. */
39
+ maxTurns: z.number().int().optional(),
40
+ /** Whether the model may call tools in parallel. @default true */
41
+ parallelToolCalls: z.boolean().optional(),
42
+ /** Cache key used to route requests with shared prompt prefixes. */
43
+ promptCacheKey: z.string().optional(),
44
+ /** Stable identifier used to attribute policy violations to an end user. */
45
+ safetyIdentifier: z.string().optional(),
32
46
  serviceTier: z.enum(['default', 'priority']).optional(),
33
47
  /**
34
48
  * Whether to store the input message(s) and model response for later retrieval.
@@ -41,11 +55,23 @@ export const xaiLanguageModelResponsesOptions = z.object({
41
55
  * The ID of the previous response from the model.
42
56
  */
43
57
  previousResponseId: z.string().optional(),
58
+ /** A unique identifier for the end user, used for abuse monitoring. */
59
+ user: z.string().optional(),
44
60
  /**
45
61
  * Specify additional output data to include in the model response.
46
62
  * Example values: 'file_search_call.results'.
47
63
  */
48
- include: z.array(z.enum(['file_search_call.results'])).nullish(),
64
+ include: z
65
+ .array(
66
+ z.enum([
67
+ 'file_search_call.results',
68
+ 'web_search_call.action.sources',
69
+ 'code_interpreter_call.outputs',
70
+ 'reasoning.encrypted_content',
71
+ 'no_inline_citations',
72
+ ]),
73
+ )
74
+ .nullish(),
49
75
  });
50
76
 
51
77
  export type XaiLanguageModelResponsesOptions = z.infer<
@@ -211,10 +211,6 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
211
211
  schema: xaiLanguageModelResponsesOptions,
212
212
  })) ?? {};
213
213
 
214
- if (topK != null) {
215
- warnings.push({ type: 'unsupported', feature: 'topK' });
216
- }
217
-
218
214
  if (frequencyPenalty != null) {
219
215
  warnings.push({ type: 'unsupported', feature: 'frequencyPenalty' });
220
216
  }
@@ -318,6 +314,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
318
314
  max_output_tokens: maxOutputTokens,
319
315
  temperature,
320
316
  top_p: topP,
317
+ top_k: topK,
318
+ min_p: options.minP,
321
319
  seed,
322
320
  ...(responseFormat?.type === 'json' && {
323
321
  text: {
@@ -333,15 +331,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
333
331
  : { type: 'json_object' },
334
332
  },
335
333
  }),
336
- ...((resolvedReasoningEffort != null ||
337
- options.reasoningSummary != null) && {
334
+ ...(resolvedReasoningEffort != null && {
338
335
  reasoning: {
339
- ...(resolvedReasoningEffort != null && {
340
- effort: resolvedReasoningEffort,
341
- }),
342
- ...(options.reasoningSummary != null && {
343
- summary: options.reasoningSummary,
344
- }),
336
+ effort: resolvedReasoningEffort,
345
337
  },
346
338
  }),
347
339
  ...(options.store === false && {
@@ -353,9 +345,24 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
353
345
  ...(options.previousResponseId != null && {
354
346
  previous_response_id: options.previousResponseId,
355
347
  }),
348
+ ...(options.maxTurns != null && {
349
+ max_turns: options.maxTurns,
350
+ }),
351
+ ...(options.parallelToolCalls != null && {
352
+ parallel_tool_calls: options.parallelToolCalls,
353
+ }),
354
+ ...(options.promptCacheKey != null && {
355
+ prompt_cache_key: options.promptCacheKey,
356
+ }),
357
+ ...(options.safetyIdentifier != null && {
358
+ safety_identifier: options.safetyIdentifier,
359
+ }),
356
360
  ...(options.serviceTier != null && {
357
361
  service_tier: options.serviceTier,
358
362
  }),
363
+ ...(options.user != null && {
364
+ user: options.user,
365
+ }),
359
366
  };
360
367
 
361
368
  if (xaiTools && xaiTools.length > 0) {
@@ -644,7 +651,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
644
651
  outputTokens: { total: 0, text: 0, reasoning: 0 },
645
652
  },
646
653
  ...((response.usage?.cost_in_usd_ticks != null ||
647
- response.service_tier != null) && {
654
+ response.service_tier != null ||
655
+ response.prompt_cache_key != null ||
656
+ response.safety_identifier != null) && {
648
657
  providerMetadata: {
649
658
  xai: {
650
659
  ...(response.usage?.cost_in_usd_ticks != null && {
@@ -653,6 +662,12 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
653
662
  ...(response.service_tier != null && {
654
663
  serviceTier: response.service_tier,
655
664
  }),
665
+ ...(response.prompt_cache_key != null && {
666
+ promptCacheKey: response.prompt_cache_key,
667
+ }),
668
+ ...(response.safety_identifier != null && {
669
+ safetyIdentifier: response.safety_identifier,
670
+ }),
656
671
  },
657
672
  },
658
673
  }),
@@ -704,6 +719,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
704
719
  let usage: LanguageModelV4Usage | undefined = undefined;
705
720
  let costInUsdTicks: number | undefined = undefined;
706
721
  let serviceTier: string | undefined = undefined;
722
+ let promptCacheKey: string | undefined = undefined;
723
+ let safetyIdentifier: string | undefined = undefined;
707
724
  let isFirstChunk = true;
708
725
  const contentBlocks: Record<string, { type: 'text' }> = {};
709
726
  const seenToolCalls = new Set<string>();
@@ -899,6 +916,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
899
916
  }
900
917
 
901
918
  serviceTier = response.service_tier ?? undefined;
919
+ promptCacheKey = response.prompt_cache_key ?? undefined;
920
+ safetyIdentifier = response.safety_identifier ?? undefined;
902
921
 
903
922
  if (event.type === 'response.incomplete') {
904
923
  const reason =
@@ -1371,11 +1390,16 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
1371
1390
  },
1372
1391
  outputTokens: { total: 0, text: 0, reasoning: 0 },
1373
1392
  },
1374
- ...((costInUsdTicks != null || serviceTier != null) && {
1393
+ ...((costInUsdTicks != null ||
1394
+ serviceTier != null ||
1395
+ promptCacheKey != null ||
1396
+ safetyIdentifier != null) && {
1375
1397
  providerMetadata: {
1376
1398
  xai: {
1377
1399
  ...(costInUsdTicks != null && { costInUsdTicks }),
1378
1400
  ...(serviceTier != null && { serviceTier }),
1401
+ ...(promptCacheKey != null && { promptCacheKey }),
1402
+ ...(safetyIdentifier != null && { safetyIdentifier }),
1379
1403
  },
1380
1404
  },
1381
1405
  }),
@@ -8,10 +8,33 @@ const modeSchema = z.enum(['edit-video', 'extend-video', 'reference-to-video']);
8
8
  export type XaiVideoMode = z.infer<typeof modeSchema>;
9
9
  type XaiVideoResolution = z.infer<typeof resolutionSchema>;
10
10
 
11
+ const keyframeSchema = z.object({
12
+ imageUrl: nonEmptyStringSchema,
13
+ timestampSeconds: z.number().positive(),
14
+ });
15
+
16
+ const storageOptionsSchema = z.object({
17
+ filename: nonEmptyStringSchema,
18
+ expiresAfter: z.number().int().positive().max(2_592_000).optional(),
19
+ publicUrl: z
20
+ .union([
21
+ z.boolean(),
22
+ z.object({
23
+ expiresAfter: z.number().int().min(3_600).max(2_592_000).optional(),
24
+ }),
25
+ ])
26
+ .optional(),
27
+ });
28
+
29
+ export type XaiVideoKeyframe = z.infer<typeof keyframeSchema>;
30
+ export type XaiVideoStorageOptions = z.infer<typeof storageOptionsSchema>;
31
+
11
32
  interface XaiVideoSharedOptions {
12
33
  pollIntervalMs?: number | null;
13
34
  pollTimeoutMs?: number | null;
14
35
  resolution?: XaiVideoResolution | null;
36
+ /** Store the generated video in the xAI Files API. */
37
+ storageOptions?: XaiVideoStorageOptions;
15
38
  }
16
39
 
17
40
  interface XaiVideoUserOptions {
@@ -59,6 +82,8 @@ interface XaiVideoGenerationOptions
59
82
  mode?: undefined;
60
83
  videoUrl?: undefined;
61
84
  referenceImageUrls?: undefined;
85
+ /** Mid-video image anchors (up to 4). */
86
+ keyframes?: XaiVideoKeyframe[];
62
87
  }
63
88
 
64
89
  interface XaiLegacyEditVideoOptions
@@ -119,6 +144,8 @@ const runtimeSchema = z.looseObject({
119
144
  videoUrl: nonEmptyStringSchema.optional(),
120
145
  referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
121
146
  referenceVoiceIds: z.array(nonEmptyStringSchema).max(3).optional(),
147
+ keyframes: z.array(keyframeSchema).max(4).optional(),
148
+ storageOptions: storageOptionsSchema.optional(),
122
149
  user: z.string().optional(),
123
150
  ...baseFields,
124
151
  });