@ai-sdk/xai 4.0.44 → 4.0.48
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 +32 -0
- package/dist/index.d.ts +60 -20
- package/dist/index.js +1183 -635
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +60 -0
- package/package.json +2 -2
- package/src/responses/xai-responses-api.ts +47 -18
- package/src/responses/xai-responses-batch.ts +558 -0
- package/src/responses/xai-responses-language-model.ts +159 -4
- package/src/tool/web-search.ts +38 -15
- package/src/xai-chat-language-model.ts +29 -22
- package/src/xai-provider.ts +6 -5
- package/src/xai-video-model.ts +12 -1
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InvalidArgumentError,
|
|
3
|
+
type Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
|
|
4
|
+
type Experimental_BatchV4Error as BatchV4Error,
|
|
5
|
+
type Experimental_BatchV4ItemResult as BatchV4ItemResult,
|
|
6
|
+
type Experimental_BatchV4OperationOptions as BatchV4OperationOptions,
|
|
7
|
+
type Experimental_BatchV4StartOptions as BatchV4StartOptions,
|
|
8
|
+
type Experimental_BatchV4StartResult as BatchV4StartResult,
|
|
9
|
+
type Experimental_BatchV4Status as BatchV4Status,
|
|
10
|
+
type LanguageModelV4Content,
|
|
11
|
+
type LanguageModelV4GenerateResult,
|
|
12
|
+
type SharedV4ProviderMetadata,
|
|
13
|
+
type SharedV4Warning,
|
|
14
|
+
} from '@ai-sdk/provider';
|
|
15
|
+
import {
|
|
16
|
+
combineHeaders,
|
|
17
|
+
convertAsyncIteratorToReadableStream,
|
|
18
|
+
createJsonResponseHandler,
|
|
19
|
+
createNullLanguageModelUsage,
|
|
20
|
+
getFromApi,
|
|
21
|
+
lazySchema,
|
|
22
|
+
normalizeBatchRequestCounts,
|
|
23
|
+
postFormDataToApi,
|
|
24
|
+
postJsonToApi,
|
|
25
|
+
safeValidateTypes,
|
|
26
|
+
WORKFLOW_DESERIALIZE,
|
|
27
|
+
WORKFLOW_SERIALIZE,
|
|
28
|
+
zodSchema,
|
|
29
|
+
type InferSchema,
|
|
30
|
+
} from '@ai-sdk/provider-utils';
|
|
31
|
+
import { z } from 'zod/v4';
|
|
32
|
+
import { convertXaiChatUsage } from '../convert-xai-chat-usage';
|
|
33
|
+
import { getResponseMetadata } from '../get-response-metadata';
|
|
34
|
+
import { mapXaiFinishReason } from '../map-xai-finish-reason';
|
|
35
|
+
import {
|
|
36
|
+
xaiChatResponseSchema,
|
|
37
|
+
type XaiChatResponse,
|
|
38
|
+
} from '../xai-chat-language-model';
|
|
39
|
+
import { xaiFailedResponseHandler } from '../xai-error';
|
|
40
|
+
import { xaiFilesResponseSchema } from '../files/xai-files-api';
|
|
41
|
+
import {
|
|
42
|
+
XaiResponsesLanguageModel,
|
|
43
|
+
type XaiResponsesConfig,
|
|
44
|
+
} from './xai-responses-language-model';
|
|
45
|
+
import type { XaiResponsesModelId } from './xai-responses-language-model-options';
|
|
46
|
+
|
|
47
|
+
const xaiBatchEndpoint = '/v1/responses';
|
|
48
|
+
const xaiBatchName = 'ai-sdk-text-batch';
|
|
49
|
+
const xaiBatchResultsPageSize = 1000;
|
|
50
|
+
|
|
51
|
+
type XaiBatchRequest = Parameters<
|
|
52
|
+
BatchLanguageModelV4['experimental_doStartBatch']
|
|
53
|
+
>[0]['requests'][number];
|
|
54
|
+
|
|
55
|
+
type XaiBatchPreparedRequest = {
|
|
56
|
+
body: unknown;
|
|
57
|
+
warnings: SharedV4Warning[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
type XaiBatchResponseConversion =
|
|
61
|
+
| { success: true; result: LanguageModelV4GenerateResult }
|
|
62
|
+
| { success: false; error: BatchV4Error };
|
|
63
|
+
|
|
64
|
+
const xaiBatchResponseSchema = lazySchema(() =>
|
|
65
|
+
zodSchema(
|
|
66
|
+
z.object({
|
|
67
|
+
batch_id: z.string(),
|
|
68
|
+
name: z.string().nullish(),
|
|
69
|
+
create_time: z.string().nullish(),
|
|
70
|
+
expire_time: z.string().nullish(),
|
|
71
|
+
cancel_time: z.string().nullish(),
|
|
72
|
+
cancel_by_xai_message: z.string().nullish(),
|
|
73
|
+
state: z
|
|
74
|
+
.object({
|
|
75
|
+
num_requests: z.number().nullish(),
|
|
76
|
+
num_pending: z.number().nullish(),
|
|
77
|
+
num_success: z.number().nullish(),
|
|
78
|
+
num_error: z.number().nullish(),
|
|
79
|
+
num_cancelled: z.number().nullish(),
|
|
80
|
+
})
|
|
81
|
+
.nullish(),
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
type XaiBatchResponse = InferSchema<typeof xaiBatchResponseSchema>;
|
|
87
|
+
|
|
88
|
+
const xaiBatchErrorSchema = z.object({
|
|
89
|
+
code: z.union([z.string(), z.number()]).nullish(),
|
|
90
|
+
message: z.string().nullish(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const xaiBatchResultSchema = z.object({
|
|
94
|
+
batch_request_id: z.string(),
|
|
95
|
+
batch_result: z
|
|
96
|
+
.object({
|
|
97
|
+
response: z
|
|
98
|
+
.object({
|
|
99
|
+
chat_get_completion: z.unknown().nullish(),
|
|
100
|
+
})
|
|
101
|
+
.nullish(),
|
|
102
|
+
error: xaiBatchErrorSchema.nullish(),
|
|
103
|
+
})
|
|
104
|
+
.nullish(),
|
|
105
|
+
error_message: z.string().nullish(),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
type XaiBatchResult = z.infer<typeof xaiBatchResultSchema>;
|
|
109
|
+
|
|
110
|
+
const xaiBatchResultsPageSchema = lazySchema(() =>
|
|
111
|
+
zodSchema(
|
|
112
|
+
z.object({
|
|
113
|
+
results: z.array(xaiBatchResultSchema),
|
|
114
|
+
pagination_token: z.string().nullish(),
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
class XaiResponsesBatch {
|
|
120
|
+
constructor(
|
|
121
|
+
private readonly options: {
|
|
122
|
+
config: XaiResponsesConfig;
|
|
123
|
+
prepareRequest: (
|
|
124
|
+
request: XaiBatchRequest,
|
|
125
|
+
) => PromiseLike<XaiBatchPreparedRequest>;
|
|
126
|
+
},
|
|
127
|
+
) {}
|
|
128
|
+
|
|
129
|
+
async startBatch(
|
|
130
|
+
options: BatchV4StartOptions<XaiBatchRequest>,
|
|
131
|
+
): Promise<BatchV4StartResult> {
|
|
132
|
+
const fileParts: string[] = [];
|
|
133
|
+
const warnings: BatchV4StartResult['warnings'] =
|
|
134
|
+
options.webhookUrl == null
|
|
135
|
+
? []
|
|
136
|
+
: [
|
|
137
|
+
{
|
|
138
|
+
warning: {
|
|
139
|
+
type: 'unsupported',
|
|
140
|
+
feature: 'webhookUrl',
|
|
141
|
+
details:
|
|
142
|
+
'The xAI Batch API does not support per-batch webhook URLs.',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
for (const request of options.requests) {
|
|
148
|
+
const preparedRequest = await this.options.prepareRequest(request);
|
|
149
|
+
|
|
150
|
+
fileParts.push(
|
|
151
|
+
JSON.stringify({
|
|
152
|
+
custom_id: request.id,
|
|
153
|
+
method: 'POST',
|
|
154
|
+
url: xaiBatchEndpoint,
|
|
155
|
+
body: preparedRequest.body,
|
|
156
|
+
}),
|
|
157
|
+
'\n',
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
for (const warning of preparedRequest.warnings) {
|
|
161
|
+
warnings.push({ requestId: request.id, warning });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const filename = 'batch.jsonl';
|
|
166
|
+
const file = new Blob(fileParts, { type: 'application/jsonl' });
|
|
167
|
+
fileParts.length = 0;
|
|
168
|
+
const formData = new FormData();
|
|
169
|
+
formData.append('file', file, filename);
|
|
170
|
+
|
|
171
|
+
const headers = combineHeaders(
|
|
172
|
+
this.options.config.headers?.(),
|
|
173
|
+
options.headers,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const { value: uploadedFile } = await postFormDataToApi({
|
|
177
|
+
url: this.getUrl('/files'),
|
|
178
|
+
headers,
|
|
179
|
+
formData,
|
|
180
|
+
failedResponseHandler: xaiFailedResponseHandler,
|
|
181
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
182
|
+
xaiFilesResponseSchema,
|
|
183
|
+
),
|
|
184
|
+
abortSignal: options.abortSignal,
|
|
185
|
+
fetch: this.options.config.fetch,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const { value: batch } = await postJsonToApi({
|
|
189
|
+
url: this.getUrl('/batches'),
|
|
190
|
+
headers,
|
|
191
|
+
body: {
|
|
192
|
+
name: xaiBatchName,
|
|
193
|
+
input_file_id: uploadedFile.id,
|
|
194
|
+
},
|
|
195
|
+
failedResponseHandler: xaiFailedResponseHandler,
|
|
196
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
197
|
+
xaiBatchResponseSchema,
|
|
198
|
+
),
|
|
199
|
+
abortSignal: options.abortSignal,
|
|
200
|
+
fetch: this.options.config.fetch,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
batchId: batch.batch_id,
|
|
205
|
+
...convertXaiBatchStatus(batch),
|
|
206
|
+
warnings,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async getBatchStatus(
|
|
211
|
+
options: BatchV4OperationOptions,
|
|
212
|
+
): Promise<BatchV4Status> {
|
|
213
|
+
return convertXaiBatchStatus(await this.retrieveBatch(options));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async getBatchResults(
|
|
217
|
+
options: BatchV4OperationOptions,
|
|
218
|
+
): Promise<ReadableStream<BatchV4ItemResult<LanguageModelV4GenerateResult>>> {
|
|
219
|
+
const batch = await this.retrieveBatch(options);
|
|
220
|
+
if (convertXaiBatchStatus(batch).status === 'pending') {
|
|
221
|
+
throw new InvalidArgumentError({
|
|
222
|
+
argument: 'batchId',
|
|
223
|
+
message: `xAI batch "${options.batchId}" is not complete.`,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return convertAsyncIteratorToReadableStream(
|
|
228
|
+
this.iterateBatchResults(options),
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private async retrieveBatch(
|
|
233
|
+
options: BatchV4OperationOptions,
|
|
234
|
+
): Promise<XaiBatchResponse> {
|
|
235
|
+
const { value: batch } = await getFromApi({
|
|
236
|
+
url: this.getUrl(`/batches/${encodeURIComponent(options.batchId)}`),
|
|
237
|
+
headers: combineHeaders(this.options.config.headers?.(), options.headers),
|
|
238
|
+
failedResponseHandler: xaiFailedResponseHandler,
|
|
239
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
240
|
+
xaiBatchResponseSchema,
|
|
241
|
+
),
|
|
242
|
+
abortSignal: options.abortSignal,
|
|
243
|
+
fetch: this.options.config.fetch,
|
|
244
|
+
validateUrl: false,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
return batch;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private async *iterateBatchResults(
|
|
251
|
+
options: BatchV4OperationOptions,
|
|
252
|
+
): AsyncGenerator<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
|
|
253
|
+
let paginationToken: string | undefined;
|
|
254
|
+
|
|
255
|
+
do {
|
|
256
|
+
const query = new URLSearchParams({
|
|
257
|
+
limit: String(xaiBatchResultsPageSize),
|
|
258
|
+
});
|
|
259
|
+
if (paginationToken != null) {
|
|
260
|
+
query.set('pagination_token', paginationToken);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const { value: page } = await getFromApi({
|
|
264
|
+
url: this.getUrl(
|
|
265
|
+
`/batches/${encodeURIComponent(options.batchId)}/results?${query}`,
|
|
266
|
+
),
|
|
267
|
+
headers: combineHeaders(
|
|
268
|
+
this.options.config.headers?.(),
|
|
269
|
+
options.headers,
|
|
270
|
+
),
|
|
271
|
+
failedResponseHandler: xaiFailedResponseHandler,
|
|
272
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
273
|
+
xaiBatchResultsPageSchema,
|
|
274
|
+
),
|
|
275
|
+
abortSignal: options.abortSignal,
|
|
276
|
+
fetch: this.options.config.fetch,
|
|
277
|
+
validateUrl: false,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
for (const result of page.results) {
|
|
281
|
+
yield await this.convertBatchResult(result);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
paginationToken = page.pagination_token ?? undefined;
|
|
285
|
+
} while (paginationToken != null);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private async convertBatchResult(
|
|
289
|
+
result: XaiBatchResult,
|
|
290
|
+
): Promise<BatchV4ItemResult<LanguageModelV4GenerateResult>> {
|
|
291
|
+
const error = result.batch_result?.error;
|
|
292
|
+
if (
|
|
293
|
+
(result.error_message?.length ?? 0) > 0 ||
|
|
294
|
+
(error?.code != null && error.code !== 0 && error.code !== '0') ||
|
|
295
|
+
(error?.code == null && (error?.message?.length ?? 0) > 0)
|
|
296
|
+
) {
|
|
297
|
+
const convertedError = convertXaiBatchError(result);
|
|
298
|
+
return {
|
|
299
|
+
id: result.batch_request_id,
|
|
300
|
+
status: isXaiCancellationError(error?.code) ? 'cancelled' : 'failed',
|
|
301
|
+
error: convertedError,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// xAI returns text batch results in chat completion format, including for
|
|
306
|
+
// requests submitted to the Responses API endpoint.
|
|
307
|
+
const response = result.batch_result?.response;
|
|
308
|
+
if (response?.chat_get_completion != null) {
|
|
309
|
+
const validation = await safeValidateTypes({
|
|
310
|
+
value: response.chat_get_completion,
|
|
311
|
+
schema: xaiChatResponseSchema,
|
|
312
|
+
});
|
|
313
|
+
if (!validation.success) {
|
|
314
|
+
return invalidXaiBatchResult(result.batch_request_id);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const conversion = convertXaiChatBatchResponse(validation.value);
|
|
318
|
+
return conversion.success
|
|
319
|
+
? {
|
|
320
|
+
id: result.batch_request_id,
|
|
321
|
+
status: 'succeeded',
|
|
322
|
+
result: conversion.result,
|
|
323
|
+
}
|
|
324
|
+
: {
|
|
325
|
+
id: result.batch_request_id,
|
|
326
|
+
status: 'failed',
|
|
327
|
+
error: conversion.error,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return invalidXaiBatchResult(result.batch_request_id);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private getUrl(path: string) {
|
|
335
|
+
return `${this.options.config.baseURL ?? 'https://api.x.ai/v1'}${path}`;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export class XaiResponsesBatchLanguageModel
|
|
340
|
+
extends XaiResponsesLanguageModel
|
|
341
|
+
implements BatchLanguageModelV4
|
|
342
|
+
{
|
|
343
|
+
private readonly batch: XaiResponsesBatch;
|
|
344
|
+
|
|
345
|
+
static [WORKFLOW_SERIALIZE](model: XaiResponsesLanguageModel) {
|
|
346
|
+
return XaiResponsesLanguageModel[WORKFLOW_SERIALIZE](model);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
350
|
+
modelId: XaiResponsesModelId;
|
|
351
|
+
config: XaiResponsesConfig;
|
|
352
|
+
}) {
|
|
353
|
+
return new XaiResponsesBatchLanguageModel(options.modelId, options.config);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
constructor(modelId: XaiResponsesModelId, config: XaiResponsesConfig) {
|
|
357
|
+
super(modelId, config);
|
|
358
|
+
this.batch = new XaiResponsesBatch({
|
|
359
|
+
config,
|
|
360
|
+
prepareRequest: async request => {
|
|
361
|
+
const { args: body, warnings } = await this.getArgs(request.options);
|
|
362
|
+
return { body, warnings };
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
experimental_doStartBatch(
|
|
368
|
+
options: Parameters<BatchLanguageModelV4['experimental_doStartBatch']>[0],
|
|
369
|
+
) {
|
|
370
|
+
return this.batch.startBatch(options);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
experimental_doGetBatchStatus(options: BatchV4OperationOptions) {
|
|
374
|
+
return this.batch.getBatchStatus(options);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
experimental_doGetBatchResults(options: BatchV4OperationOptions) {
|
|
378
|
+
return this.batch.getBatchResults(options);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function convertXaiBatchStatus(batch: XaiBatchResponse): BatchV4Status {
|
|
383
|
+
const requestCounts = normalizeBatchRequestCounts({
|
|
384
|
+
total: batch.state?.num_requests,
|
|
385
|
+
pending: batch.state?.num_pending,
|
|
386
|
+
completed: batch.state?.num_success,
|
|
387
|
+
failed:
|
|
388
|
+
batch.state?.num_error != null && batch.state?.num_cancelled != null
|
|
389
|
+
? batch.state.num_error + batch.state.num_cancelled
|
|
390
|
+
: undefined,
|
|
391
|
+
});
|
|
392
|
+
const isCancelled =
|
|
393
|
+
batch.cancel_time != null || batch.cancel_by_xai_message != null;
|
|
394
|
+
const isExpired = isPastDate(batch.expire_time);
|
|
395
|
+
const status: BatchV4Status['status'] =
|
|
396
|
+
isCancelled || isExpired
|
|
397
|
+
? 'failed'
|
|
398
|
+
: requestCounts != null &&
|
|
399
|
+
requestCounts.total > 0 &&
|
|
400
|
+
requestCounts.pending === 0
|
|
401
|
+
? 'completed'
|
|
402
|
+
: 'pending';
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
status,
|
|
406
|
+
...(requestCounts != null ? { requestCounts } : {}),
|
|
407
|
+
...(isCancelled
|
|
408
|
+
? {
|
|
409
|
+
error: {
|
|
410
|
+
message:
|
|
411
|
+
batch.cancel_by_xai_message ??
|
|
412
|
+
`xAI batch "${batch.batch_id}" was cancelled.`,
|
|
413
|
+
code: 'batch_cancelled',
|
|
414
|
+
},
|
|
415
|
+
}
|
|
416
|
+
: isExpired
|
|
417
|
+
? {
|
|
418
|
+
error: {
|
|
419
|
+
message: `xAI batch "${batch.batch_id}" expired.`,
|
|
420
|
+
code: 'batch_expired',
|
|
421
|
+
},
|
|
422
|
+
}
|
|
423
|
+
: {}),
|
|
424
|
+
...(batch.create_time != null ? { createdAt: batch.create_time } : {}),
|
|
425
|
+
...(batch.expire_time != null ? { expiresAt: batch.expire_time } : {}),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function isPastDate(value: string | null | undefined) {
|
|
430
|
+
if (value == null) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
const timestamp = Date.parse(value);
|
|
434
|
+
return !Number.isNaN(timestamp) && timestamp <= Date.now();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function convertXaiBatchError(result: XaiBatchResult): BatchV4Error {
|
|
438
|
+
const error = result.batch_result?.error;
|
|
439
|
+
return {
|
|
440
|
+
message:
|
|
441
|
+
result.error_message || error?.message || 'xAI batch request failed.',
|
|
442
|
+
...(error?.code != null && error.code !== 0 && error.code !== '0'
|
|
443
|
+
? { code: String(error.code) }
|
|
444
|
+
: {}),
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function isXaiCancellationError(code: string | number | null | undefined) {
|
|
449
|
+
const normalizedCode = String(code).toLowerCase();
|
|
450
|
+
return (
|
|
451
|
+
code === 1 ||
|
|
452
|
+
normalizedCode === '1' ||
|
|
453
|
+
normalizedCode === 'cancelled' ||
|
|
454
|
+
normalizedCode === 'batch_cancelled'
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function invalidXaiBatchResult(
|
|
459
|
+
id: string,
|
|
460
|
+
): BatchV4ItemResult<LanguageModelV4GenerateResult> {
|
|
461
|
+
return {
|
|
462
|
+
id,
|
|
463
|
+
status: 'failed',
|
|
464
|
+
error: {
|
|
465
|
+
message: 'xAI returned an invalid Responses batch result.',
|
|
466
|
+
code: 'invalid_response',
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function convertXaiChatBatchResponse(
|
|
472
|
+
response: XaiChatResponse,
|
|
473
|
+
): XaiBatchResponseConversion {
|
|
474
|
+
if (response.error != null) {
|
|
475
|
+
return {
|
|
476
|
+
success: false,
|
|
477
|
+
error: {
|
|
478
|
+
message: response.error,
|
|
479
|
+
...(response.code != null ? { code: response.code } : {}),
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const choice = response.choices?.[0];
|
|
485
|
+
if (choice == null) {
|
|
486
|
+
return {
|
|
487
|
+
success: false,
|
|
488
|
+
error: {
|
|
489
|
+
message: 'xAI returned a batch response without any choices.',
|
|
490
|
+
code: 'invalid_response',
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (choice.message.tool_calls?.length) {
|
|
496
|
+
return unsupportedXaiBatchContent('tool_calls');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const content: LanguageModelV4Content[] = [];
|
|
500
|
+
if (choice.message.content) {
|
|
501
|
+
content.push({ type: 'text', text: choice.message.content });
|
|
502
|
+
}
|
|
503
|
+
if (choice.message.reasoning_content) {
|
|
504
|
+
content.push({
|
|
505
|
+
type: 'reasoning',
|
|
506
|
+
text: choice.message.reasoning_content,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
for (const url of response.citations ?? []) {
|
|
510
|
+
content.push({
|
|
511
|
+
type: 'source',
|
|
512
|
+
sourceType: 'url',
|
|
513
|
+
id: url,
|
|
514
|
+
url,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
success: true,
|
|
520
|
+
result: {
|
|
521
|
+
content,
|
|
522
|
+
finishReason: {
|
|
523
|
+
unified: mapXaiFinishReason(choice.finish_reason),
|
|
524
|
+
raw: choice.finish_reason ?? undefined,
|
|
525
|
+
},
|
|
526
|
+
usage: response.usage
|
|
527
|
+
? convertXaiChatUsage(response.usage)
|
|
528
|
+
: createNullLanguageModelUsage(),
|
|
529
|
+
response: getResponseMetadata(response),
|
|
530
|
+
warnings: [],
|
|
531
|
+
...((response.usage?.cost_in_usd_ticks != null ||
|
|
532
|
+
response.service_tier != null) && {
|
|
533
|
+
providerMetadata: {
|
|
534
|
+
xai: {
|
|
535
|
+
...(response.usage?.cost_in_usd_ticks != null
|
|
536
|
+
? { costInUsdTicks: response.usage.cost_in_usd_ticks }
|
|
537
|
+
: {}),
|
|
538
|
+
...(response.service_tier != null
|
|
539
|
+
? { serviceTier: response.service_tier }
|
|
540
|
+
: {}),
|
|
541
|
+
},
|
|
542
|
+
} satisfies SharedV4ProviderMetadata,
|
|
543
|
+
}),
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function unsupportedXaiBatchContent(type: string): XaiBatchResponseConversion {
|
|
549
|
+
return {
|
|
550
|
+
success: false,
|
|
551
|
+
error: {
|
|
552
|
+
message:
|
|
553
|
+
`xAI returned "${type}" content, but tool content is not supported ` +
|
|
554
|
+
'in AI SDK text batches.',
|
|
555
|
+
code: 'unsupported_content',
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
}
|