@ai-sdk/xai 4.0.0-beta.7 → 4.0.0-beta.75

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +660 -9
  2. package/README.md +2 -0
  3. package/dist/index.d.ts +213 -68
  4. package/dist/index.js +2074 -781
  5. package/dist/index.js.map +1 -1
  6. package/docs/01-xai.mdx +445 -54
  7. package/package.json +15 -15
  8. package/src/convert-to-xai-chat-messages.ts +48 -27
  9. package/src/convert-xai-chat-usage.ts +3 -3
  10. package/src/files/xai-files-api.ts +16 -0
  11. package/src/files/xai-files-options.ts +19 -0
  12. package/src/files/xai-files.ts +94 -0
  13. package/src/index.ts +9 -4
  14. package/src/map-xai-finish-reason.ts +2 -2
  15. package/src/realtime/index.ts +2 -0
  16. package/src/realtime/xai-realtime-event-mapper.ts +399 -0
  17. package/src/realtime/xai-realtime-model-options.ts +3 -0
  18. package/src/realtime/xai-realtime-model.ts +101 -0
  19. package/src/remove-additional-properties.ts +24 -0
  20. package/src/responses/convert-to-xai-responses-input.ts +100 -23
  21. package/src/responses/convert-xai-responses-usage.ts +3 -3
  22. package/src/responses/map-xai-responses-finish-reason.ts +3 -2
  23. package/src/responses/xai-responses-api.ts +31 -1
  24. package/src/responses/{xai-responses-options.ts → xai-responses-language-model-options.ts} +12 -7
  25. package/src/responses/xai-responses-language-model.ts +157 -60
  26. package/src/responses/xai-responses-prepare-tools.ts +10 -8
  27. package/src/tool/code-execution.ts +2 -2
  28. package/src/tool/file-search.ts +2 -2
  29. package/src/tool/mcp-server.ts +2 -2
  30. package/src/tool/view-image.ts +2 -2
  31. package/src/tool/view-x-video.ts +2 -2
  32. package/src/tool/web-search.ts +4 -2
  33. package/src/tool/x-search.ts +2 -2
  34. package/src/{xai-chat-options.ts → xai-chat-language-model-options.ts} +28 -13
  35. package/src/xai-chat-language-model.ts +65 -29
  36. package/src/xai-chat-prompt.ts +2 -1
  37. package/src/xai-error.ts +13 -3
  38. package/src/xai-image-model.ts +28 -11
  39. package/src/xai-prepare-tools.ts +9 -8
  40. package/src/xai-provider.ts +115 -19
  41. package/src/xai-speech-model-options.ts +55 -0
  42. package/src/xai-speech-model.ts +167 -0
  43. package/src/xai-transcription-model-options.ts +70 -0
  44. package/src/xai-transcription-model.ts +166 -0
  45. package/src/xai-video-model-options.ts +145 -0
  46. package/src/xai-video-model.ts +129 -22
  47. package/dist/index.d.mts +0 -377
  48. package/dist/index.mjs +0 -3070
  49. package/dist/index.mjs.map +0 -1
  50. package/src/xai-video-options.ts +0 -23
  51. /package/src/{xai-image-options.ts → xai-image-model-options.ts} +0 -0
@@ -1,13 +1,14 @@
1
- import { LanguageModelV3FinishReason } from '@ai-sdk/provider';
1
+ import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapXaiResponsesFinishReason(
4
4
  finishReason: string | null | undefined,
5
- ): LanguageModelV3FinishReason['unified'] {
5
+ ): LanguageModelV4FinishReason['unified'] {
6
6
  switch (finishReason) {
7
7
  case 'stop':
8
8
  case 'completed':
9
9
  return 'stop';
10
10
  case 'length':
11
+ case 'max_output_tokens':
11
12
  return 'length';
12
13
  case 'tool_calls':
13
14
  case 'function_call':
@@ -26,7 +26,9 @@ export type XaiResponsesSystemMessage = {
26
26
 
27
27
  export type XaiResponsesUserMessageContentPart =
28
28
  | { type: 'input_text'; text: string }
29
- | { type: 'input_image'; image_url: string };
29
+ | { type: 'input_image'; image_url: string }
30
+ | { type: 'input_file'; file_id: string }
31
+ | { type: 'input_file'; file_url: string };
30
32
 
31
33
  export type XaiResponsesUserMessage = {
32
34
  role: 'user';
@@ -77,6 +79,7 @@ export type XaiResponsesTool =
77
79
  type: 'web_search';
78
80
  allowed_domains?: string[];
79
81
  excluded_domains?: string[];
82
+ enable_image_search?: boolean;
80
83
  enable_image_understanding?: boolean;
81
84
  }
82
85
  | {
@@ -247,6 +250,7 @@ export const xaiResponsesUsageSchema = z.object({
247
250
  .optional(),
248
251
  num_sources_used: z.number().optional(),
249
252
  num_server_side_tools_used: z.number().optional(),
253
+ cost_in_usd_ticks: z.number().nullish(),
250
254
  });
251
255
 
252
256
  export const xaiResponsesResponseSchema = z.object({
@@ -521,6 +525,32 @@ export const xaiResponsesChunkSchema = z.union([
521
525
  output_index: z.number(),
522
526
  output: z.string().optional(),
523
527
  }),
528
+ z.object({
529
+ type: z.literal('response.incomplete'),
530
+ response: z.object({
531
+ incomplete_details: z.object({ reason: z.string() }).nullish(),
532
+ usage: xaiResponsesUsageSchema.nullish(),
533
+ }),
534
+ }),
535
+ z.object({
536
+ type: z.literal('response.failed'),
537
+ response: z.object({
538
+ error: z
539
+ .object({
540
+ code: z.string().nullish(),
541
+ message: z.string(),
542
+ })
543
+ .nullish(),
544
+ incomplete_details: z.object({ reason: z.string() }).nullish(),
545
+ usage: xaiResponsesUsageSchema.nullish(),
546
+ }),
547
+ }),
548
+ z.object({
549
+ type: z.literal('error'),
550
+ code: z.string().nullish(),
551
+ message: z.string(),
552
+ param: z.string().nullish(),
553
+ }),
524
554
  z.object({
525
555
  type: z.literal('response.done'),
526
556
  response: xaiResponsesResponseSchema,
@@ -1,11 +1,10 @@
1
1
  import { z } from 'zod/v4';
2
2
 
3
3
  export type XaiResponsesModelId =
4
- | 'grok-4-1-fast-reasoning'
5
- | 'grok-4-1-fast-non-reasoning'
6
- | 'grok-4'
7
- | 'grok-4-fast-non-reasoning'
8
- | 'grok-4-fast-reasoning'
4
+ | 'grok-4.20-non-reasoning'
5
+ | 'grok-4.20-reasoning'
6
+ | 'grok-4.3'
7
+ | 'grok-latest'
9
8
  | (string & {});
10
9
 
11
10
  /**
@@ -14,14 +13,20 @@ export type XaiResponsesModelId =
14
13
  export const xaiLanguageModelResponsesOptions = z.object({
15
14
  /**
16
15
  * Constrains how hard a reasoning model thinks before responding.
17
- * Possible values are `low` (uses fewer reasoning tokens), `medium` and `high` (uses more reasoning tokens).
16
+ * Possible values are `none` (disables reasoning entirely; supported by
17
+ * `grok-4.3` and newer reasoning models), `low` (uses fewer reasoning
18
+ * tokens), `medium`, and `high` (uses more reasoning tokens).
19
+ *
20
+ * @see https://docs.x.ai/docs/guides/reasoning
18
21
  */
19
- reasoningEffort: z.enum(['low', 'medium', 'high']).optional(),
22
+ reasoningEffort: z.enum(['none', 'low', 'medium', 'high']).optional(),
20
23
  reasoningSummary: z.enum(['auto', 'concise', 'detailed']).optional(),
21
24
  logprobs: z.boolean().optional(),
22
25
  topLogprobs: z.number().int().min(0).max(8).optional(),
23
26
  /**
24
27
  * Whether to store the input message(s) and model response for later retrieval.
28
+ * Must be set to `false` for teams with Zero Data Retention (ZDR) enabled,
29
+ * otherwise the API will return an error.
25
30
  * @default true
26
31
  */
27
32
  store: z.boolean().optional(),
@@ -1,55 +1,74 @@
1
- import {
2
- LanguageModelV3,
3
- LanguageModelV3CallOptions,
4
- LanguageModelV3Content,
5
- LanguageModelV3FinishReason,
6
- LanguageModelV3GenerateResult,
7
- LanguageModelV3StreamPart,
8
- LanguageModelV3StreamResult,
9
- LanguageModelV3Usage,
10
- SharedV3Warning,
1
+ import type {
2
+ LanguageModelV4,
3
+ LanguageModelV4CallOptions,
4
+ LanguageModelV4Content,
5
+ LanguageModelV4FinishReason,
6
+ LanguageModelV4GenerateResult,
7
+ LanguageModelV4StreamPart,
8
+ LanguageModelV4StreamResult,
9
+ LanguageModelV4Usage,
10
+ SharedV4Warning,
11
11
  } from '@ai-sdk/provider';
12
12
  import {
13
13
  combineHeaders,
14
14
  createEventSourceResponseHandler,
15
15
  createJsonResponseHandler,
16
- FetchFunction,
16
+ isCustomReasoning,
17
+ mapReasoningToProviderEffort,
17
18
  parseProviderOptions,
18
- ParseResult,
19
19
  postJsonToApi,
20
+ serializeModelOptions,
21
+ WORKFLOW_SERIALIZE,
22
+ WORKFLOW_DESERIALIZE,
23
+ type FetchFunction,
24
+ type ParseResult,
20
25
  } from '@ai-sdk/provider-utils';
21
- import { z } from 'zod/v4';
26
+ import type { z } from 'zod/v4';
22
27
  import { getResponseMetadata } from '../get-response-metadata';
23
28
  import { xaiFailedResponseHandler } from '../xai-error';
24
29
  import { convertToXaiResponsesInput } from './convert-to-xai-responses-input';
25
30
  import { convertXaiResponsesUsage } from './convert-xai-responses-usage';
26
31
  import { mapXaiResponsesFinishReason } from './map-xai-responses-finish-reason';
27
32
  import {
28
- XaiResponsesIncludeOptions,
29
33
  xaiResponsesChunkSchema,
30
34
  xaiResponsesResponseSchema,
35
+ type XaiResponsesIncludeOptions,
31
36
  } from './xai-responses-api';
32
37
  import {
33
- XaiResponsesModelId,
34
38
  xaiLanguageModelResponsesOptions,
35
- } from './xai-responses-options';
39
+ type XaiResponsesModelId,
40
+ } from './xai-responses-language-model-options';
36
41
  import { prepareResponsesTools } from './xai-responses-prepare-tools';
37
42
 
38
43
  type XaiResponsesConfig = {
39
44
  provider: string;
40
45
  baseURL: string | undefined;
41
- headers: () => Record<string, string | undefined>;
46
+ headers?: () => Record<string, string | undefined>;
42
47
  generateId: () => string;
43
48
  fetch?: FetchFunction;
44
49
  };
45
50
 
46
- export class XaiResponsesLanguageModel implements LanguageModelV3 {
47
- readonly specificationVersion = 'v3';
51
+ export class XaiResponsesLanguageModel implements LanguageModelV4 {
52
+ readonly specificationVersion = 'v4';
48
53
 
49
54
  readonly modelId: XaiResponsesModelId;
50
55
 
51
56
  private readonly config: XaiResponsesConfig;
52
57
 
58
+ static [WORKFLOW_SERIALIZE](model: XaiResponsesLanguageModel) {
59
+ return serializeModelOptions({
60
+ modelId: model.modelId,
61
+ config: model.config,
62
+ });
63
+ }
64
+
65
+ static [WORKFLOW_DESERIALIZE](options: {
66
+ modelId: XaiResponsesModelId;
67
+ config: XaiResponsesConfig;
68
+ }) {
69
+ return new XaiResponsesLanguageModel(options.modelId, options.config);
70
+ }
71
+
53
72
  constructor(modelId: XaiResponsesModelId, config: XaiResponsesConfig) {
54
73
  this.modelId = modelId;
55
74
  this.config = config;
@@ -61,6 +80,11 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
61
80
 
62
81
  readonly supportedUrls: Record<string, RegExp[]> = {
63
82
  'image/*': [/^https?:\/\/.*$/],
83
+ // xAI's Responses API accepts non-image documents (PDF, plain text, CSV, etc.) as
84
+ // `{ type: 'input_file', file_url }`. Keeping these URLs intact here lets them pass
85
+ // through to the converter instead of being downloaded to bytes by the SDK.
86
+ 'application/pdf': [/^https?:\/\/.*$/],
87
+ 'text/*': [/^https?:\/\/.*$/],
64
88
  };
65
89
 
66
90
  private async getArgs({
@@ -74,8 +98,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
74
98
  providerOptions,
75
99
  tools,
76
100
  toolChoice,
77
- }: LanguageModelV3CallOptions) {
78
- const warnings: SharedV3Warning[] = [];
101
+ reasoning,
102
+ }: LanguageModelV4CallOptions) {
103
+ const warnings: SharedV4Warning[] = [];
79
104
 
80
105
  const options =
81
106
  (await parseProviderOptions({
@@ -110,7 +135,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
110
135
 
111
136
  const { input, inputWarnings } = await convertToXaiResponsesInput({
112
137
  prompt,
113
- store: true,
138
+ store: options.store ?? true,
114
139
  });
115
140
  warnings.push(...inputWarnings);
116
141
 
@@ -139,6 +164,24 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
139
164
  }
140
165
  }
141
166
 
167
+ const resolvedReasoningEffort =
168
+ options.reasoningEffort ??
169
+ (isCustomReasoning(reasoning)
170
+ ? reasoning === 'none'
171
+ ? undefined
172
+ : mapReasoningToProviderEffort({
173
+ reasoning,
174
+ effortMap: {
175
+ minimal: 'low',
176
+ low: 'low',
177
+ medium: 'medium',
178
+ high: 'high',
179
+ xhigh: 'high',
180
+ },
181
+ warnings,
182
+ })
183
+ : undefined);
184
+
142
185
  const baseArgs: Record<string, unknown> = {
143
186
  model: this.modelId,
144
187
  input,
@@ -165,11 +208,11 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
165
208
  : { type: 'json_object' },
166
209
  },
167
210
  }),
168
- ...((options.reasoningEffort != null ||
211
+ ...((resolvedReasoningEffort != null ||
169
212
  options.reasoningSummary != null) && {
170
213
  reasoning: {
171
- ...(options.reasoningEffort != null && {
172
- effort: options.reasoningEffort,
214
+ ...(resolvedReasoningEffort != null && {
215
+ effort: resolvedReasoningEffort,
173
216
  }),
174
217
  ...(options.reasoningSummary != null && {
175
218
  summary: options.reasoningSummary,
@@ -207,8 +250,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
207
250
  }
208
251
 
209
252
  async doGenerate(
210
- options: LanguageModelV3CallOptions,
211
- ): Promise<LanguageModelV3GenerateResult> {
253
+ options: LanguageModelV4CallOptions,
254
+ ): Promise<LanguageModelV4GenerateResult> {
212
255
  const {
213
256
  args: body,
214
257
  warnings,
@@ -225,7 +268,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
225
268
  rawValue: rawResponse,
226
269
  } = await postJsonToApi({
227
270
  url: `${this.config.baseURL ?? 'https://api.x.ai/v1'}/responses`,
228
- headers: combineHeaders(this.config.headers(), options.headers),
271
+ headers: combineHeaders(this.config.headers?.(), options.headers),
229
272
  body,
230
273
  failedResponseHandler: xaiFailedResponseHandler,
231
274
  successfulResponseHandler: createJsonResponseHandler(
@@ -235,7 +278,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
235
278
  fetch: this.config.fetch,
236
279
  });
237
280
 
238
- const content: Array<LanguageModelV3Content> = [];
281
+ const content: Array<LanguageModelV4Content> = [];
282
+ let hasFunctionCall = false;
239
283
 
240
284
  const webSearchSubTools = [
241
285
  'web_search',
@@ -358,6 +402,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
358
402
  }
359
403
 
360
404
  case 'function_call': {
405
+ hasFunctionCall = true;
361
406
  content.push({
362
407
  type: 'tool-call',
363
408
  toolCallId: part.call_id,
@@ -377,11 +422,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
377
422
  .filter(text => text && text.length > 0)
378
423
  .join('');
379
424
 
380
- if (reasoningText) {
381
- if (part.encrypted_content || part.id) {
382
- content.push({
383
- type: 'reasoning',
384
- text: reasoningText,
425
+ // condition changed here since encrypted content can now come with empty reasoning text
426
+ if (reasoningText || part.encrypted_content) {
427
+ const hasMetadata = part.encrypted_content || part.id;
428
+ content.push({
429
+ type: 'reasoning',
430
+ text: reasoningText,
431
+ ...(hasMetadata && {
385
432
  providerMetadata: {
386
433
  xai: {
387
434
  ...(part.encrypted_content && {
@@ -390,13 +437,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
390
437
  ...(part.id && { itemId: part.id }),
391
438
  },
392
439
  },
393
- });
394
- } else {
395
- content.push({
396
- type: 'reasoning',
397
- text: reasoningText,
398
- });
399
- }
440
+ }),
441
+ });
400
442
  }
401
443
  break;
402
444
  }
@@ -410,7 +452,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
410
452
  return {
411
453
  content,
412
454
  finishReason: {
413
- unified: mapXaiResponsesFinishReason(response.status),
455
+ unified: hasFunctionCall
456
+ ? 'tool-calls'
457
+ : mapXaiResponsesFinishReason(response.status),
414
458
  raw: response.status ?? undefined,
415
459
  },
416
460
  usage: response.usage
@@ -419,6 +463,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
419
463
  inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
420
464
  outputTokens: { total: 0, text: 0, reasoning: 0 },
421
465
  },
466
+ ...(response.usage?.cost_in_usd_ticks != null && {
467
+ providerMetadata: {
468
+ xai: {
469
+ costInUsdTicks: response.usage.cost_in_usd_ticks,
470
+ },
471
+ },
472
+ }),
422
473
  request: { body },
423
474
  response: {
424
475
  ...getResponseMetadata(response),
@@ -430,8 +481,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
430
481
  }
431
482
 
432
483
  async doStream(
433
- options: LanguageModelV3CallOptions,
434
- ): Promise<LanguageModelV3StreamResult> {
484
+ options: LanguageModelV4CallOptions,
485
+ ): Promise<LanguageModelV4StreamResult> {
435
486
  const {
436
487
  args,
437
488
  warnings,
@@ -448,7 +499,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
448
499
 
449
500
  const { responseHeaders, value: response } = await postJsonToApi({
450
501
  url: `${this.config.baseURL ?? 'https://api.x.ai/v1'}/responses`,
451
- headers: combineHeaders(this.config.headers(), options.headers),
502
+ headers: combineHeaders(this.config.headers?.(), options.headers),
452
503
  body,
453
504
  failedResponseHandler: xaiFailedResponseHandler,
454
505
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -458,11 +509,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
458
509
  fetch: this.config.fetch,
459
510
  });
460
511
 
461
- let finishReason: LanguageModelV3FinishReason = {
512
+ let finishReason: LanguageModelV4FinishReason = {
462
513
  unified: 'other',
463
514
  raw: undefined,
464
515
  };
465
- let usage: LanguageModelV3Usage | undefined = undefined;
516
+ let hasFunctionCall = false;
517
+ let usage: LanguageModelV4Usage | undefined = undefined;
518
+ let costInUsdTicks: number | undefined = undefined;
466
519
  let isFirstChunk = true;
467
520
  const contentBlocks: Record<string, { type: 'text' }> = {};
468
521
  const seenToolCalls = new Set<string>();
@@ -485,7 +538,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
485
538
  stream: response.pipeThrough(
486
539
  new TransformStream<
487
540
  ParseResult<z.infer<typeof xaiResponsesChunkSchema>>,
488
- LanguageModelV3StreamPart
541
+ LanguageModelV4StreamPart
489
542
  >({
490
543
  start(controller) {
491
544
  controller.enqueue({ type: 'stream-start', warnings });
@@ -520,16 +573,18 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
520
573
  if (event.type === 'response.reasoning_summary_part.added') {
521
574
  const blockId = `reasoning-${event.item_id}`;
522
575
 
523
- activeReasoning[event.item_id] = {};
524
- controller.enqueue({
525
- type: 'reasoning-start',
526
- id: blockId,
527
- providerMetadata: {
528
- xai: {
529
- itemId: event.item_id,
576
+ if (activeReasoning[event.item_id] == null) {
577
+ activeReasoning[event.item_id] = {};
578
+ controller.enqueue({
579
+ type: 'reasoning-start',
580
+ id: blockId,
581
+ providerMetadata: {
582
+ xai: {
583
+ itemId: event.item_id,
584
+ },
530
585
  },
531
- },
532
- });
586
+ });
587
+ }
533
588
  }
534
589
 
535
590
  if (event.type === 'response.reasoning_summary_text.delta') {
@@ -645,17 +700,32 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
645
700
 
646
701
  if (
647
702
  event.type === 'response.done' ||
648
- event.type === 'response.completed'
703
+ event.type === 'response.completed' ||
704
+ event.type === 'response.incomplete'
649
705
  ) {
650
706
  const response = event.response;
651
707
 
652
708
  if (response.usage) {
653
709
  usage = convertXaiResponsesUsage(response.usage);
710
+ costInUsdTicks = response.usage.cost_in_usd_ticks ?? undefined;
654
711
  }
655
712
 
656
- if (response.status) {
713
+ if (event.type === 'response.incomplete') {
714
+ const reason =
715
+ 'incomplete_details' in response
716
+ ? response.incomplete_details?.reason
717
+ : undefined;
657
718
  finishReason = {
658
- unified: mapXaiResponsesFinishReason(response.status),
719
+ unified: reason
720
+ ? mapXaiResponsesFinishReason(reason)
721
+ : 'other',
722
+ raw: reason ?? 'incomplete',
723
+ };
724
+ } else if ('status' in response && response.status) {
725
+ finishReason = {
726
+ unified: hasFunctionCall
727
+ ? 'tool-calls'
728
+ : mapXaiResponsesFinishReason(response.status),
659
729
  raw: response.status,
660
730
  };
661
731
  }
@@ -663,6 +733,25 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
663
733
  return;
664
734
  }
665
735
 
736
+ if (event.type === 'response.failed') {
737
+ const reason = event.response.incomplete_details?.reason;
738
+ finishReason = {
739
+ unified: reason ? mapXaiResponsesFinishReason(reason) : 'error',
740
+ raw: reason ?? 'error',
741
+ };
742
+
743
+ if (event.response.usage) {
744
+ usage = convertXaiResponsesUsage(event.response.usage);
745
+ }
746
+
747
+ return;
748
+ }
749
+
750
+ if (event.type === 'error') {
751
+ controller.enqueue({ type: 'error', error: event });
752
+ return;
753
+ }
754
+
666
755
  // Custom tool call input streaming - already handled by output_item events
667
756
  if (
668
757
  event.type === 'response.custom_tool_call_input.delta' ||
@@ -923,6 +1012,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
923
1012
  toolName: part.name,
924
1013
  });
925
1014
  } else if (event.type === 'response.output_item.done') {
1015
+ hasFunctionCall = true;
926
1016
  ongoingToolCalls[event.output_index] = undefined;
927
1017
 
928
1018
  controller.enqueue({
@@ -963,6 +1053,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
963
1053
  },
964
1054
  outputTokens: { total: 0, text: 0, reasoning: 0 },
965
1055
  },
1056
+ ...(costInUsdTicks != null && {
1057
+ providerMetadata: {
1058
+ xai: {
1059
+ costInUsdTicks,
1060
+ },
1061
+ },
1062
+ }),
966
1063
  });
967
1064
  },
968
1065
  }),
@@ -1,14 +1,15 @@
1
1
  import {
2
- LanguageModelV3CallOptions,
3
- SharedV3Warning,
4
2
  UnsupportedFunctionalityError,
3
+ type LanguageModelV4CallOptions,
4
+ type SharedV4Warning,
5
5
  } from '@ai-sdk/provider';
6
6
  import { validateTypes } from '@ai-sdk/provider-utils';
7
+ import { removeAdditionalPropertiesFalse } from '../remove-additional-properties';
7
8
  import { fileSearchArgsSchema } from '../tool/file-search';
8
9
  import { mcpServerArgsSchema } from '../tool/mcp-server';
9
10
  import { webSearchArgsSchema } from '../tool/web-search';
10
11
  import { xSearchArgsSchema } from '../tool/x-search';
11
- import { XaiResponsesTool } from './xai-responses-api';
12
+ import type { XaiResponsesTool } from './xai-responses-api';
12
13
 
13
14
  type XaiResponsesToolChoice =
14
15
  | 'auto'
@@ -20,16 +21,16 @@ export async function prepareResponsesTools({
20
21
  tools,
21
22
  toolChoice,
22
23
  }: {
23
- tools: LanguageModelV3CallOptions['tools'];
24
- toolChoice?: LanguageModelV3CallOptions['toolChoice'];
24
+ tools: LanguageModelV4CallOptions['tools'];
25
+ toolChoice?: LanguageModelV4CallOptions['toolChoice'];
25
26
  }): Promise<{
26
27
  tools: Array<XaiResponsesTool> | undefined;
27
28
  toolChoice: XaiResponsesToolChoice | undefined;
28
- toolWarnings: SharedV3Warning[];
29
+ toolWarnings: SharedV4Warning[];
29
30
  }> {
30
31
  const normalizedTools = tools?.length ? tools : undefined;
31
32
 
32
- const toolWarnings: SharedV3Warning[] = [];
33
+ const toolWarnings: SharedV4Warning[] = [];
33
34
 
34
35
  if (normalizedTools == null) {
35
36
  return { tools: undefined, toolChoice: undefined, toolWarnings };
@@ -53,6 +54,7 @@ export async function prepareResponsesTools({
53
54
  type: 'web_search',
54
55
  allowed_domains: args.allowedDomains,
55
56
  excluded_domains: args.excludedDomains,
57
+ enable_image_search: args.enableImageSearch,
56
58
  enable_image_understanding: args.enableImageUnderstanding,
57
59
  });
58
60
  break;
@@ -142,7 +144,7 @@ export async function prepareResponsesTools({
142
144
  type: 'function',
143
145
  name: tool.name,
144
146
  description: tool.description,
145
- parameters: tool.inputSchema,
147
+ parameters: removeAdditionalPropertiesFalse(tool.inputSchema),
146
148
  ...(tool.strict != null ? { strict: tool.strict } : {}),
147
149
  });
148
150
  }
@@ -1,4 +1,4 @@
1
- import { createProviderToolFactoryWithOutputSchema } from '@ai-sdk/provider-utils';
1
+ import { createProviderExecutedToolFactory } from '@ai-sdk/provider-utils';
2
2
  import { z } from 'zod/v4';
3
3
 
4
4
  const codeExecutionOutputSchema = z.object({
@@ -6,7 +6,7 @@ const codeExecutionOutputSchema = z.object({
6
6
  error: z.string().optional().describe('any error that occurred'),
7
7
  });
8
8
 
9
- const codeExecutionToolFactory = createProviderToolFactoryWithOutputSchema({
9
+ const codeExecutionToolFactory = createProviderExecutedToolFactory({
10
10
  id: 'xai.code_execution',
11
11
  inputSchema: z.object({}).describe('no input parameters'),
12
12
  outputSchema: codeExecutionOutputSchema,
@@ -1,5 +1,5 @@
1
1
  import {
2
- createProviderToolFactoryWithOutputSchema,
2
+ createProviderExecutedToolFactory,
3
3
  lazySchema,
4
4
  zodSchema,
5
5
  } from '@ai-sdk/provider-utils';
@@ -36,7 +36,7 @@ const fileSearchOutputSchema = lazySchema(() =>
36
36
  ),
37
37
  );
38
38
 
39
- const fileSearchToolFactory = createProviderToolFactoryWithOutputSchema<
39
+ const fileSearchToolFactory = createProviderExecutedToolFactory<
40
40
  {},
41
41
  {
42
42
  /**
@@ -1,5 +1,5 @@
1
1
  import {
2
- createProviderToolFactoryWithOutputSchema,
2
+ createProviderExecutedToolFactory,
3
3
  lazySchema,
4
4
  zodSchema,
5
5
  } from '@ai-sdk/provider-utils';
@@ -41,7 +41,7 @@ const mcpServerOutputSchema = lazySchema(() =>
41
41
  ),
42
42
  );
43
43
 
44
- const mcpServerToolFactory = createProviderToolFactoryWithOutputSchema<
44
+ const mcpServerToolFactory = createProviderExecutedToolFactory<
45
45
  {},
46
46
  {
47
47
  name: string;
@@ -1,4 +1,4 @@
1
- import { createProviderToolFactoryWithOutputSchema } from '@ai-sdk/provider-utils';
1
+ import { createProviderExecutedToolFactory } from '@ai-sdk/provider-utils';
2
2
  import { z } from 'zod/v4';
3
3
 
4
4
  const viewImageOutputSchema = z.object({
@@ -9,7 +9,7 @@ const viewImageOutputSchema = z.object({
9
9
  .describe('objects detected in the image'),
10
10
  });
11
11
 
12
- const viewImageToolFactory = createProviderToolFactoryWithOutputSchema({
12
+ const viewImageToolFactory = createProviderExecutedToolFactory({
13
13
  id: 'xai.view_image',
14
14
  inputSchema: z.object({}).describe('no input parameters'),
15
15
  outputSchema: viewImageOutputSchema,
@@ -1,4 +1,4 @@
1
- import { createProviderToolFactoryWithOutputSchema } from '@ai-sdk/provider-utils';
1
+ import { createProviderExecutedToolFactory } from '@ai-sdk/provider-utils';
2
2
  import { z } from 'zod/v4';
3
3
 
4
4
  const viewXVideoOutputSchema = z.object({
@@ -7,7 +7,7 @@ const viewXVideoOutputSchema = z.object({
7
7
  duration: z.number().optional().describe('duration in seconds'),
8
8
  });
9
9
 
10
- const viewXVideoToolFactory = createProviderToolFactoryWithOutputSchema({
10
+ const viewXVideoToolFactory = createProviderExecutedToolFactory({
11
11
  id: 'xai.view_x_video',
12
12
  inputSchema: z.object({}).describe('no input parameters'),
13
13
  outputSchema: viewXVideoOutputSchema,