@ai-sdk/xai 4.0.14 → 4.0.16

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
@@ -933,6 +933,7 @@ const { video } = await generateVideo({
933
933
  duration: 5,
934
934
  providerOptions: {
935
935
  xai: {
936
+ user: 'user-123',
936
937
  pollTimeoutMs: 600000, // 10 minutes
937
938
  } satisfies XaiVideoModelOptions,
938
939
  },
@@ -1203,6 +1204,14 @@ You can validate the provider options using the `XaiVideoModelOptions` type.
1203
1204
  `1280x720` maps to `720p` and `854x480` maps to `480p`.
1204
1205
  Use this provider option to pass the native format directly.
1205
1206
 
1207
+ - **user** _string_
1208
+
1209
+ Optional identifier for the end user responsible for the request. xAI uses
1210
+ this value for abuse monitoring. It is sent unchanged for video generation
1211
+ (including reference-to-video) and video editing requests, and is omitted
1212
+ when not configured. It is not sent for video extension requests. Use an
1213
+ opaque stable identifier and avoid sending unnecessary personal information.
1214
+
1206
1215
  - **mode** _'edit-video' | 'extend-video' | 'reference-to-video'_
1207
1216
 
1208
1217
  Selects the explicit video operation. Each mode is mutually exclusive:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "4.0.14",
3
+ "version": "4.0.16",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -29,17 +29,17 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@ai-sdk/openai-compatible": "3.0.11",
33
- "@ai-sdk/provider-utils": "5.0.10",
34
- "@ai-sdk/provider": "4.0.3"
32
+ "@ai-sdk/openai-compatible": "3.0.12",
33
+ "@ai-sdk/provider": "4.0.3",
34
+ "@ai-sdk/provider-utils": "5.0.11"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "22.19.19",
38
38
  "tsup": "^8.5.1",
39
39
  "typescript": "5.8.3",
40
40
  "zod": "3.25.76",
41
- "@vercel/ai-tsconfig": "0.0.0",
42
- "@ai-sdk/test-server": "2.0.0"
41
+ "@ai-sdk/test-server": "2.0.0",
42
+ "@vercel/ai-tsconfig": "0.0.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "zod": "^3.25.76 || ^4.1.8"
@@ -12,6 +12,7 @@ import {
12
12
  } from '@ai-sdk/provider-utils';
13
13
  import { xaiFilePartProviderOptions } from '../xai-file-part-options';
14
14
  import type {
15
+ XaiResponsesFunctionCallOutput,
15
16
  XaiResponsesInput,
16
17
  XaiResponsesUserMessageContentPart,
17
18
  } from './xai-responses-api';
@@ -241,7 +242,7 @@ export async function convertToXaiResponsesInput({
241
242
  }
242
243
  const output = part.output;
243
244
 
244
- let outputValue: string;
245
+ let outputValue: XaiResponsesFunctionCallOutput['output'];
245
246
  switch (output.type) {
246
247
  case 'text':
247
248
  case 'error-text':
@@ -255,14 +256,39 @@ export async function convertToXaiResponsesInput({
255
256
  outputValue = JSON.stringify(output.value);
256
257
  break;
257
258
  case 'content':
258
- outputValue = output.value
259
- .map(item => {
260
- if (item.type === 'text') {
261
- return item.text;
259
+ outputValue = [];
260
+ for (const item of output.value) {
261
+ switch (item.type) {
262
+ case 'text': {
263
+ outputValue.push({
264
+ type: 'input_text',
265
+ text: item.text,
266
+ });
267
+ break;
268
+ }
269
+ case 'file': {
270
+ if (
271
+ getTopLevelMediaType(item.mediaType) === 'image' &&
272
+ (item.data.type === 'data' || item.data.type === 'url')
273
+ ) {
274
+ outputValue.push({
275
+ type: 'input_image',
276
+ image_url:
277
+ item.data.type === 'url'
278
+ ? item.data.url.toString()
279
+ : `data:${resolveFullMediaType({ part: item })};base64,${convertToBase64(item.data.data)}`,
280
+ });
281
+ }
282
+ break;
283
+ }
284
+ case 'custom': {
285
+ break;
262
286
  }
263
- return '';
264
- })
265
- .join('');
287
+ default: {
288
+ const _exhaustiveCheck: never = item;
289
+ }
290
+ }
291
+ }
266
292
  break;
267
293
  default: {
268
294
  const _exhaustiveCheck: never = output;
@@ -48,7 +48,18 @@ export type XaiResponsesAssistantMessage = {
48
48
  export type XaiResponsesFunctionCallOutput = {
49
49
  type: 'function_call_output';
50
50
  call_id: string;
51
- output: string;
51
+ output:
52
+ | string
53
+ | Array<
54
+ | {
55
+ type: 'input_text';
56
+ text: string;
57
+ }
58
+ | {
59
+ type: 'input_image';
60
+ image_url: string;
61
+ }
62
+ >;
52
63
  };
53
64
 
54
65
  export type XaiResponsesReasoning = {
@@ -14,7 +14,15 @@ interface XaiVideoSharedOptions {
14
14
  resolution?: XaiVideoResolution | null;
15
15
  }
16
16
 
17
- interface XaiVideoEditModeOptions extends XaiVideoSharedOptions {
17
+ interface XaiVideoUserOptions {
18
+ /**
19
+ * A unique identifier representing the end user, for abuse monitoring.
20
+ */
21
+ user?: string;
22
+ }
23
+
24
+ interface XaiVideoEditModeOptions
25
+ extends XaiVideoSharedOptions, XaiVideoUserOptions {
18
26
  /**
19
27
  * Select edit-video mode explicitly for best autocomplete and narrowing.
20
28
  */
@@ -32,7 +40,8 @@ interface XaiVideoExtendModeOptions extends XaiVideoSharedOptions {
32
40
  videoUrl: string;
33
41
  }
34
42
 
35
- interface XaiVideoReferenceToVideoOptions extends XaiVideoSharedOptions {
43
+ interface XaiVideoReferenceToVideoOptions
44
+ extends XaiVideoSharedOptions, XaiVideoUserOptions {
36
45
  /**
37
46
  * Select reference-to-video mode explicitly for best autocomplete and narrowing.
38
47
  */
@@ -41,13 +50,15 @@ interface XaiVideoReferenceToVideoOptions extends XaiVideoSharedOptions {
41
50
  referenceImageUrls: string[];
42
51
  }
43
52
 
44
- interface XaiVideoGenerationOptions extends XaiVideoSharedOptions {
53
+ interface XaiVideoGenerationOptions
54
+ extends XaiVideoSharedOptions, XaiVideoUserOptions {
45
55
  mode?: undefined;
46
56
  videoUrl?: undefined;
47
57
  referenceImageUrls?: undefined;
48
58
  }
49
59
 
50
- interface XaiLegacyEditVideoOptions extends XaiVideoSharedOptions {
60
+ interface XaiLegacyEditVideoOptions
61
+ extends XaiVideoSharedOptions, XaiVideoUserOptions {
51
62
  /**
52
63
  * Legacy backward-compatible shape: omitting `mode` while providing
53
64
  * `videoUrl` behaves like edit-video.
@@ -56,7 +67,8 @@ interface XaiLegacyEditVideoOptions extends XaiVideoSharedOptions {
56
67
  videoUrl: string;
57
68
  }
58
69
 
59
- interface XaiLegacyReferenceToVideoOptions extends XaiVideoSharedOptions {
70
+ interface XaiLegacyReferenceToVideoOptions
71
+ extends XaiVideoSharedOptions, XaiVideoUserOptions {
60
72
  /**
61
73
  * Legacy backward-compatible shape: omitting `mode` while providing
62
74
  * `referenceImageUrls` behaves like reference-to-video.
@@ -94,8 +106,13 @@ const baseFields = {
94
106
  resolution: resolutionSchema.nullish(),
95
107
  };
96
108
 
109
+ const userField = {
110
+ user: z.string().optional(),
111
+ };
112
+
97
113
  const editVideoSchema = z.object({
98
114
  ...baseFields,
115
+ ...userField,
99
116
  mode: z.literal('edit-video'),
100
117
  videoUrl: nonEmptyStringSchema,
101
118
  referenceImageUrls: z.undefined().optional(),
@@ -110,6 +127,7 @@ const extendVideoSchema = z.object({
110
127
 
111
128
  const referenceToVideoSchema = z.object({
112
129
  ...baseFields,
130
+ ...userField,
113
131
  mode: z.literal('reference-to-video'),
114
132
  referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7),
115
133
  videoUrl: z.undefined().optional(),
@@ -117,6 +135,7 @@ const referenceToVideoSchema = z.object({
117
135
 
118
136
  const autoDetectSchema = z.object({
119
137
  ...baseFields,
138
+ ...userField,
120
139
  mode: z.undefined().optional(),
121
140
  videoUrl: nonEmptyStringSchema.optional(),
122
141
  referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
@@ -133,6 +152,7 @@ const runtimeSchema = z.looseObject({
133
152
  mode: modeSchema.optional(),
134
153
  videoUrl: nonEmptyStringSchema.optional(),
135
154
  referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
155
+ user: z.string().optional(),
136
156
  ...baseFields,
137
157
  });
138
158
 
@@ -5,15 +5,18 @@ import {
5
5
  type SharedV4Warning,
6
6
  } from '@ai-sdk/provider';
7
7
  import {
8
+ cancelResponseBody,
8
9
  combineHeaders,
9
10
  convertUint8ArrayToBase64,
10
11
  createJsonResponseHandler,
11
12
  delay,
13
+ extractResponseHeaders,
12
14
  getFromApi,
13
15
  getTopLevelMediaType,
14
16
  parseProviderOptions,
15
17
  postJsonToApi,
16
18
  type FetchFunction,
19
+ type ResponseHandler,
17
20
  } from '@ai-sdk/provider-utils';
18
21
  import { z } from 'zod/v4';
19
22
  import { xaiFailedResponseHandler } from './xai-error';
@@ -359,6 +362,10 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
359
362
  });
360
363
  }
361
364
 
365
+ if (!isExtension && xaiOptions?.user !== undefined) {
366
+ body.user = xaiOptions.user;
367
+ }
368
+
362
369
  if (xaiOptions != null) {
363
370
  for (const [key, value] of Object.entries(xaiOptions)) {
364
371
  if (
@@ -369,6 +376,7 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
369
376
  'resolution',
370
377
  'videoUrl',
371
378
  'referenceImageUrls',
379
+ 'user',
372
380
  ].includes(key)
373
381
  ) {
374
382
  body[key] = value;
@@ -430,9 +438,7 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
430
438
  url: `${baseURL}/videos/${requestId}`,
431
439
  validateUrl: false,
432
440
  headers: combineHeaders(this.config.headers(), options.headers),
433
- successfulResponseHandler: createJsonResponseHandler(
434
- xaiVideoStatusResponseSchema,
435
- ),
441
+ successfulResponseHandler: xaiVideoStatusResponseHandler,
436
442
  failedResponseHandler: xaiFailedResponseHandler,
437
443
  abortSignal: options.abortSignal,
438
444
  fetch: this.config.fetch,
@@ -538,3 +544,23 @@ const xaiVideoStatusResponseSchema = z.object({
538
544
  })
539
545
  .nullish(),
540
546
  });
547
+
548
+ const xaiVideoStatusJsonResponseHandler = createJsonResponseHandler(
549
+ xaiVideoStatusResponseSchema,
550
+ );
551
+
552
+ const xaiVideoStatusResponseHandler: ResponseHandler<
553
+ z.infer<typeof xaiVideoStatusResponseSchema>
554
+ > = async options => {
555
+ if (options.response.status === 202) {
556
+ const responseHeaders = extractResponseHeaders(options.response);
557
+ await cancelResponseBody(options.response);
558
+
559
+ return {
560
+ responseHeaders,
561
+ value: { status: 'pending' },
562
+ };
563
+ }
564
+
565
+ return xaiVideoStatusJsonResponseHandler(options);
566
+ };