@ai-sdk/xai 4.0.54 → 4.0.55

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
@@ -641,27 +641,37 @@ import { xai } from '@ai-sdk/xai';
641
641
  import {
642
642
  experimental_getBatchResults as getBatchResults,
643
643
  experimental_getBatchStatus as getBatchStatus,
644
- experimental_startTextBatch as startTextBatch,
644
+ experimental_startBatch as startBatch,
645
645
  } from 'ai';
646
646
  import { setTimeout } from 'node:timers/promises';
647
647
 
648
- const model = xai('grok-4.3');
648
+ const model = 'grok-4.3';
649
649
 
650
- const batch = await startTextBatch({
651
- model,
650
+ const batch = await startBatch({
651
+ provider: xai,
652
652
  requests: [
653
- { id: 'capital-france', prompt: 'What is the capital of France?' },
654
- { id: 'capital-germany', prompt: 'What is the capital of Germany?' },
653
+ {
654
+ id: 'capital-france',
655
+ type: 'text',
656
+ model,
657
+ prompt: 'What is the capital of France?',
658
+ },
659
+ {
660
+ id: 'capital-germany',
661
+ type: 'text',
662
+ model: 'grok-4.20-non-reasoning',
663
+ prompt: 'What is the capital of Germany?',
664
+ },
655
665
  ],
656
666
  });
657
667
 
658
668
  let status = batch.status;
659
669
  while (status === 'pending') {
660
670
  await setTimeout(60_000);
661
- ({ status } = await getBatchStatus({ model, batch }));
671
+ ({ status } = await getBatchStatus({ provider: xai, batch }));
662
672
  }
663
673
 
664
- for await (const item of getBatchResults({ model, batch })) {
674
+ for await (const item of getBatchResults({ provider: xai, batch })) {
665
675
  if (item.status === 'succeeded') {
666
676
  console.log(item.id, item.text);
667
677
  } else {
@@ -670,14 +680,13 @@ for await (const item of getBatchResults({ model, batch })) {
670
680
  }
671
681
  ```
672
682
 
673
- `startTextBatch` returns a serializable batch reference. Persist this reference
683
+ `startBatch` returns a serializable batch reference. Persist this reference
674
684
  to check the batch status or retrieve its results from another process. Results
675
685
  can arrive in a different order from the input requests, so match each result by
676
686
  its `id`.
677
687
 
678
- Text batches are available through `xai(modelId)` and
679
- `xai.responses(modelId)`. The legacy `xai.chat(modelId)` models do not expose
680
- the batch APIs.
688
+ Each request specifies its `type` and `model`. xAI supports using different
689
+ text models within the same batch.
681
690
 
682
691
  <Note>
683
692
  The xAI Batch API does not support per-batch webhooks. When you provide a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "4.0.54",
3
+ "version": "4.0.55",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -29,8 +29,8 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@ai-sdk/provider": "4.0.10",
33
- "@ai-sdk/provider-utils": "5.0.36"
32
+ "@ai-sdk/provider": "4.0.11",
33
+ "@ai-sdk/provider-utils": "5.0.37"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@ai-sdk/test-server": "2.0.1",
@@ -139,6 +139,15 @@ function isRetryableStatusCode(statusCode: number): boolean {
139
139
  );
140
140
  }
141
141
 
142
+ export const xaiResponsesSupportedUrls: Record<string, RegExp[]> = {
143
+ 'image/*': [/^https?:\/\/.*$/],
144
+ // xAI's Responses API accepts non-image documents (PDF, plain text, CSV, etc.) as
145
+ // `{ type: 'input_file', file_url }`. Keeping these URLs intact here lets them pass
146
+ // through to the converter instead of being downloaded to bytes by the SDK.
147
+ 'application/pdf': [/^https?:\/\/.*$/],
148
+ 'text/*': [/^https?:\/\/.*$/],
149
+ };
150
+
142
151
  export class XaiResponsesLanguageModel implements LanguageModelV4 {
143
152
  readonly specificationVersion = 'v4';
144
153
 
@@ -169,31 +178,30 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
169
178
  return this.config.provider;
170
179
  }
171
180
 
172
- readonly supportedUrls: Record<string, RegExp[]> = {
173
- 'image/*': [/^https?:\/\/.*$/],
174
- // xAI's Responses API accepts non-image documents (PDF, plain text, CSV, etc.) as
175
- // `{ type: 'input_file', file_url }`. Keeping these URLs intact here lets them pass
176
- // through to the converter instead of being downloaded to bytes by the SDK.
177
- 'application/pdf': [/^https?:\/\/.*$/],
178
- 'text/*': [/^https?:\/\/.*$/],
179
- };
180
-
181
- protected async getArgs({
182
- prompt,
183
- maxOutputTokens,
184
- temperature,
185
- topP,
186
- topK,
187
- frequencyPenalty,
188
- presencePenalty,
189
- stopSequences,
190
- seed,
191
- responseFormat,
192
- providerOptions,
193
- tools,
194
- toolChoice,
195
- reasoning,
196
- }: LanguageModelV4CallOptions) {
181
+ readonly supportedUrls = xaiResponsesSupportedUrls;
182
+
183
+ static async prepareRequest({
184
+ modelId,
185
+ options: {
186
+ prompt,
187
+ maxOutputTokens,
188
+ temperature,
189
+ topP,
190
+ topK,
191
+ frequencyPenalty,
192
+ presencePenalty,
193
+ stopSequences,
194
+ seed,
195
+ responseFormat,
196
+ providerOptions,
197
+ tools,
198
+ toolChoice,
199
+ reasoning,
200
+ },
201
+ }: {
202
+ modelId: XaiResponsesModelId;
203
+ options: LanguageModelV4CallOptions;
204
+ }) {
197
205
  const warnings: SharedV4Warning[] = [];
198
206
 
199
207
  const options =
@@ -276,7 +284,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
276
284
 
277
285
  let resolvedReasoningEffort = options.reasoningEffort;
278
286
  if (resolvedReasoningEffort == null && isCustomReasoning(reasoning)) {
279
- if (!supportsReasoningEffort(this.modelId)) {
287
+ if (!supportsReasoningEffort(modelId)) {
280
288
  warnings.push({
281
289
  type: 'unsupported',
282
290
  feature: 'reasoning',
@@ -292,7 +300,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
292
300
  low: 'low',
293
301
  medium: 'medium',
294
302
  high: 'high',
295
- xhigh: this.modelId === 'grok-4.6' ? 'xhigh' : 'high',
303
+ xhigh: modelId === 'grok-4.6' ? 'xhigh' : 'high',
296
304
  },
297
305
  warnings,
298
306
  });
@@ -300,7 +308,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
300
308
  }
301
309
 
302
310
  const baseArgs: Record<string, unknown> = {
303
- model: this.modelId,
311
+ model: modelId,
304
312
  input,
305
313
  logprobs:
306
314
  options.logprobs === true || options.topLogprobs != null
@@ -370,6 +378,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
370
378
  };
371
379
  }
372
380
 
381
+ private getArgs(options: LanguageModelV4CallOptions) {
382
+ return XaiResponsesLanguageModel.prepareRequest({
383
+ modelId: this.modelId,
384
+ options,
385
+ });
386
+ }
387
+
373
388
  async doGenerate(
374
389
  options: LanguageModelV4CallOptions,
375
390
  ): Promise<LanguageModelV4GenerateResult> {
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  InvalidArgumentError,
3
- type Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
3
+ type Experimental_BatchV4 as BatchV4,
4
4
  type Experimental_BatchV4Error as BatchV4Error,
5
5
  type Experimental_BatchV4ItemResult as BatchV4ItemResult,
6
6
  type Experimental_BatchV4OperationOptions as BatchV4OperationOptions,
7
- type Experimental_BatchV4StartOptions as BatchV4StartOptions,
8
7
  type Experimental_BatchV4StartResult as BatchV4StartResult,
9
8
  type Experimental_BatchV4Status as BatchV4Status,
9
+ type Experimental_TextBatchV4Request as TextBatchV4Request,
10
+ type Experimental_TextBatchV4ItemResult as TextBatchV4ItemResult,
11
+ type Experimental_BatchV4StartOptions as BatchV4StartOptions,
10
12
  type LanguageModelV4Content,
11
13
  type LanguageModelV4GenerateResult,
12
14
  type SharedV4ProviderMetadata,
@@ -24,26 +26,25 @@ import {
24
26
  postFormDataToApi,
25
27
  postJsonToApi,
26
28
  safeValidateTypes,
27
- WORKFLOW_DESERIALIZE,
28
- WORKFLOW_SERIALIZE,
29
29
  zodSchema,
30
30
  type InferSchema,
31
31
  } from '@ai-sdk/provider-utils';
32
32
  import { z } from 'zod/v4';
33
- import { convertXaiChatUsage } from '../convert-xai-chat-usage';
34
- import { getResponseMetadata } from '../get-response-metadata';
35
- import { mapXaiFinishReason } from '../map-xai-finish-reason';
33
+ import { convertXaiChatUsage } from './convert-xai-chat-usage';
34
+ import { getResponseMetadata } from './get-response-metadata';
35
+ import { mapXaiFinishReason } from './map-xai-finish-reason';
36
36
  import {
37
37
  xaiChatResponseSchema,
38
38
  type XaiChatResponse,
39
- } from '../xai-chat-language-model';
40
- import { xaiFailedResponseHandler } from '../xai-error';
41
- import { xaiFilesResponseSchema } from '../files/xai-files-api';
39
+ } from './xai-chat-language-model';
40
+ import { xaiFailedResponseHandler } from './xai-error';
41
+ import { xaiFilesResponseSchema } from './files/xai-files-api';
42
42
  import {
43
43
  XaiResponsesLanguageModel,
44
+ xaiResponsesSupportedUrls,
44
45
  type XaiResponsesConfig,
45
- } from './xai-responses-language-model';
46
- import type { XaiResponsesModelId } from './xai-responses-language-model-options';
46
+ } from './responses/xai-responses-language-model';
47
+ import type { XaiResponsesModelId } from './responses/xai-responses-language-model-options';
47
48
 
48
49
  const xaiBatchEndpoint = '/v1/responses';
49
50
  const xaiBatchName = 'ai-sdk-text-batch';
@@ -67,9 +68,8 @@ const xaiBatchProviderOptionsSchema = lazySchema(() =>
67
68
  ),
68
69
  );
69
70
 
70
- type XaiBatchRequest = Parameters<
71
- BatchLanguageModelV4['experimental_doStartBatch']
72
- >[0]['requests'][number];
71
+ type XaiBatchModelIds = { readonly text: XaiResponsesModelId };
72
+ type XaiBatchRequest = TextBatchV4Request<XaiResponsesModelId>;
73
73
 
74
74
  type XaiBatchPreparedRequest = {
75
75
  body: unknown;
@@ -135,18 +135,22 @@ const xaiBatchResultsPageSchema = lazySchema(() =>
135
135
  ),
136
136
  );
137
137
 
138
- class XaiResponsesBatch {
138
+ export class XaiBatch implements BatchV4<XaiBatchModelIds> {
139
+ readonly specificationVersion = 'v4' as const;
140
+ readonly provider: string;
141
+ readonly supportedUrls = xaiResponsesSupportedUrls;
142
+
139
143
  constructor(
140
144
  private readonly options: {
145
+ provider: string;
141
146
  config: XaiResponsesConfig;
142
- prepareRequest: (
143
- request: XaiBatchRequest,
144
- ) => PromiseLike<XaiBatchPreparedRequest>;
145
147
  },
146
- ) {}
148
+ ) {
149
+ this.provider = options.provider;
150
+ }
147
151
 
148
- async startBatch(
149
- options: BatchV4StartOptions<XaiBatchRequest>,
152
+ async doStartBatch(
153
+ options: BatchV4StartOptions<XaiBatchModelIds>,
150
154
  ): Promise<BatchV4StartResult> {
151
155
  const fileParts: string[] = [];
152
156
  const warnings: BatchV4StartResult['warnings'] =
@@ -170,7 +174,7 @@ class XaiResponsesBatch {
170
174
  });
171
175
 
172
176
  for (const request of options.requests) {
173
- const preparedRequest = await this.options.prepareRequest(request);
177
+ const preparedRequest = await this.prepareRequest(request);
174
178
 
175
179
  fileParts.push(
176
180
  JSON.stringify({
@@ -252,15 +256,15 @@ class XaiResponsesBatch {
252
256
  };
253
257
  }
254
258
 
255
- async getBatchStatus(
259
+ async doGetBatchStatus(
256
260
  options: BatchV4OperationOptions,
257
261
  ): Promise<BatchV4Status> {
258
262
  return convertXaiBatchStatus(await this.retrieveBatch(options));
259
263
  }
260
264
 
261
- async getBatchResults(
265
+ async doGetBatchResults(
262
266
  options: BatchV4OperationOptions,
263
- ): Promise<ReadableStream<BatchV4ItemResult<LanguageModelV4GenerateResult>>> {
267
+ ): Promise<ReadableStream<BatchV4ItemResult>> {
264
268
  const batch = await this.retrieveBatch(options);
265
269
  if (convertXaiBatchStatus(batch).status === 'pending') {
266
270
  throw new InvalidArgumentError({
@@ -294,7 +298,7 @@ class XaiResponsesBatch {
294
298
 
295
299
  private async *iterateBatchResults(
296
300
  options: BatchV4OperationOptions,
297
- ): AsyncGenerator<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
301
+ ): AsyncGenerator<BatchV4ItemResult> {
298
302
  let paginationToken: string | undefined;
299
303
 
300
304
  do {
@@ -332,7 +336,7 @@ class XaiResponsesBatch {
332
336
 
333
337
  private async convertBatchResult(
334
338
  result: XaiBatchResult,
335
- ): Promise<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
339
+ ): Promise<TextBatchV4ItemResult> {
336
340
  const error = result.batch_result?.error;
337
341
  if (
338
342
  (result.error_message?.length ?? 0) > 0 ||
@@ -341,6 +345,7 @@ class XaiResponsesBatch {
341
345
  ) {
342
346
  const convertedError = convertXaiBatchError(result);
343
347
  return {
348
+ type: 'text',
344
349
  id: result.batch_request_id,
345
350
  status: isXaiCancellationError(error?.code) ? 'cancelled' : 'failed',
346
351
  error: convertedError,
@@ -362,11 +367,13 @@ class XaiResponsesBatch {
362
367
  const conversion = convertXaiChatBatchResponse(validation.value);
363
368
  return conversion.success
364
369
  ? {
370
+ type: 'text',
365
371
  id: result.batch_request_id,
366
372
  status: 'succeeded',
367
373
  result: conversion.result,
368
374
  }
369
375
  : {
376
+ type: 'text',
370
377
  id: result.batch_request_id,
371
378
  status: 'failed',
372
379
  error: conversion.error,
@@ -376,51 +383,19 @@ class XaiResponsesBatch {
376
383
  return invalidXaiBatchResult(result.batch_request_id);
377
384
  }
378
385
 
379
- private getUrl(path: string) {
380
- return `${this.options.config.baseURL ?? 'https://api.x.ai/v1'}${path}`;
381
- }
382
- }
383
-
384
- export class XaiResponsesBatchLanguageModel
385
- extends XaiResponsesLanguageModel
386
- implements BatchLanguageModelV4
387
- {
388
- private readonly batch: XaiResponsesBatch;
389
-
390
- static [WORKFLOW_SERIALIZE](model: XaiResponsesLanguageModel) {
391
- return XaiResponsesLanguageModel[WORKFLOW_SERIALIZE](model);
392
- }
393
-
394
- static [WORKFLOW_DESERIALIZE](options: {
395
- modelId: XaiResponsesModelId;
396
- config: XaiResponsesConfig;
397
- }) {
398
- return new XaiResponsesBatchLanguageModel(options.modelId, options.config);
399
- }
400
-
401
- constructor(modelId: XaiResponsesModelId, config: XaiResponsesConfig) {
402
- super(modelId, config);
403
- this.batch = new XaiResponsesBatch({
404
- config,
405
- prepareRequest: async request => {
406
- const { args: body, warnings } = await this.getArgs(request.options);
407
- return { body, warnings };
408
- },
409
- });
410
- }
411
-
412
- experimental_doStartBatch(
413
- options: Parameters<BatchLanguageModelV4['experimental_doStartBatch']>[0],
414
- ) {
415
- return this.batch.startBatch(options);
416
- }
417
-
418
- experimental_doGetBatchStatus(options: BatchV4OperationOptions) {
419
- return this.batch.getBatchStatus(options);
386
+ private async prepareRequest(
387
+ request: XaiBatchRequest,
388
+ ): Promise<XaiBatchPreparedRequest> {
389
+ const { args: body, warnings } =
390
+ await XaiResponsesLanguageModel.prepareRequest({
391
+ modelId: request.modelId,
392
+ options: request.options,
393
+ });
394
+ return { body, warnings };
420
395
  }
421
396
 
422
- experimental_doGetBatchResults(options: BatchV4OperationOptions) {
423
- return this.batch.getBatchResults(options);
397
+ private getUrl(path: string) {
398
+ return `${this.options.config.baseURL ?? 'https://api.x.ai/v1'}${path}`;
424
399
  }
425
400
  }
426
401
 
@@ -500,10 +475,9 @@ function isXaiCancellationError(code: string | number | null | undefined) {
500
475
  );
501
476
  }
502
477
 
503
- function invalidXaiBatchResult(
504
- id: string,
505
- ): BatchV4ItemResult<LanguageModelV4GenerateResult> {
478
+ function invalidXaiBatchResult(id: string): TextBatchV4ItemResult {
506
479
  return {
480
+ type: 'text',
507
481
  id,
508
482
  status: 'failed',
509
483
  error: {
@@ -2,7 +2,7 @@ import {
2
2
  type Experimental_RealtimeFactoryV4 as RealtimeFactoryV4,
3
3
  type Experimental_RealtimeFactoryV4GetTokenOptions as RealtimeFactoryV4GetTokenOptions,
4
4
  type Experimental_VideoModelV4,
5
- type Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
5
+ type Experimental_BatchV4 as BatchV4,
6
6
  type FilesV4,
7
7
  type ImageModelV4,
8
8
  type LanguageModelV4,
@@ -23,7 +23,8 @@ import { XaiChatLanguageModel } from './xai-chat-language-model';
23
23
  import type { XaiChatModelId } from './xai-chat-language-model-options';
24
24
  import { XaiImageModel } from './xai-image-model';
25
25
  import type { XaiImageModelId } from './xai-image-settings';
26
- import { XaiResponsesBatchLanguageModel } from './responses/xai-responses-batch';
26
+ import { XaiBatch } from './xai-batch';
27
+ import { XaiResponsesLanguageModel } from './responses/xai-responses-language-model';
27
28
  import type { XaiResponsesModelId } from './responses/xai-responses-language-model-options';
28
29
  import { XaiRealtimeModel } from './realtime/xai-realtime-model';
29
30
  import { xaiTools } from './tool';
@@ -35,12 +36,12 @@ import { XaiSpeechModel } from './xai-speech-model';
35
36
  import { XaiTranscriptionModel } from './xai-transcription-model';
36
37
 
37
38
  export interface XaiProvider extends ProviderV4 {
38
- (modelId: XaiResponsesModelId): BatchLanguageModelV4;
39
+ (modelId: XaiResponsesModelId): LanguageModelV4;
39
40
 
40
41
  /**
41
42
  * Creates an Xai language model for text generation.
42
43
  */
43
- languageModel(modelId: XaiResponsesModelId): BatchLanguageModelV4;
44
+ languageModel(modelId: XaiResponsesModelId): LanguageModelV4;
44
45
 
45
46
  /**
46
47
  * Creates an Xai chat model for text generation.
@@ -50,7 +51,12 @@ export interface XaiProvider extends ProviderV4 {
50
51
  /**
51
52
  * Creates an Xai responses model for text generation.
52
53
  */
53
- responses: (modelId: XaiResponsesModelId) => BatchLanguageModelV4;
54
+ responses: (modelId: XaiResponsesModelId) => LanguageModelV4;
55
+
56
+ /**
57
+ * Returns a BatchV4 interface for processing batches with xAI.
58
+ */
59
+ experimental_batch(): BatchV4<{ text: XaiResponsesModelId }>;
54
60
 
55
61
  /**
56
62
  * Creates an Xai image model for image generation.
@@ -167,7 +173,7 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
167
173
  };
168
174
 
169
175
  const createResponsesLanguageModel = (modelId: XaiResponsesModelId) => {
170
- return new XaiResponsesBatchLanguageModel(modelId, {
176
+ return new XaiResponsesLanguageModel(modelId, {
171
177
  provider: 'xai.responses',
172
178
  baseURL,
173
179
  headers: getHeaders,
@@ -249,6 +255,18 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
249
255
  fetch: options.fetch,
250
256
  });
251
257
 
258
+ const createBatch = () =>
259
+ new XaiBatch({
260
+ provider: 'xai.batch',
261
+ config: {
262
+ provider: 'xai.responses',
263
+ baseURL,
264
+ headers: getHeaders,
265
+ generateId,
266
+ fetch: options.fetch,
267
+ },
268
+ });
269
+
252
270
  const provider = (modelId: XaiResponsesModelId) =>
253
271
  createResponsesLanguageModel(modelId);
254
272
 
@@ -270,6 +288,7 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
270
288
  provider.transcriptionModel = createTranscriptionModel;
271
289
  provider.transcription = createTranscriptionModel;
272
290
  provider.files = createFiles;
291
+ provider.experimental_batch = createBatch;
273
292
  provider.tools = xaiTools;
274
293
 
275
294
  return provider;