@ai-sdk/xai 4.0.50 → 4.0.52

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @ai-sdk/xai
2
2
 
3
+ ## 4.0.52
4
+
5
+ ### Patch Changes
6
+
7
+ - f1513f0: feat(xai): implement `getFileMetadata`, `downloadFile` (streaming), and `deleteFile` on the xAI files interface, support `expiresAfter` upload TTLs (integer 3600–2592000 seconds, emitted before the file part as xAI requires) and streaming uploads via `{ type: 'stream' }` data, expose `byteSize`/`createdAt`/`expiresAt` on upload results, and thread `abortSignal`/`headers` through all file operations; blank and dot-segment file ids are rejected/encoded so they cannot retarget request paths
8
+ - Updated dependencies [5190b67]
9
+ - @ai-sdk/provider@4.0.10
10
+ - @ai-sdk/provider-utils@5.0.35
11
+
12
+ ## 4.0.51
13
+
14
+ ### Patch Changes
15
+
16
+ - e07b577: feat: add tool calling support to batch
17
+
3
18
  ## 4.0.50
4
19
 
5
20
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -248,6 +248,7 @@ declare const xaiFilesOptionsSchema: _ai_sdk_provider_utils.LazySchema<{
248
248
  [x: string]: unknown;
249
249
  teamId?: string | undefined;
250
250
  filePath?: string | undefined;
251
+ expiresAfter?: number | undefined;
251
252
  }>;
252
253
  type XaiFilesOptions = InferSchema<typeof xaiFilesOptionsSchema>;
253
254
 
package/dist/index.js CHANGED
@@ -990,7 +990,7 @@ var xaiChatResponseSchema = z4.object({
990
990
  choices: z4.array(
991
991
  z4.object({
992
992
  message: z4.object({
993
- role: z4.literal("assistant"),
993
+ role: z4.enum(["assistant", "tool"]),
994
994
  content: z4.string().nullish(),
995
995
  reasoning_content: z4.string().nullish(),
996
996
  tool_calls: z4.array(
@@ -1270,12 +1270,22 @@ var xaiFilesResponseSchema = lazySchema(
1270
1270
  object: z7.string().nullish(),
1271
1271
  bytes: z7.number().nullish(),
1272
1272
  created_at: z7.number().nullish(),
1273
+ expires_at: z7.number().nullish(),
1273
1274
  filename: z7.string().nullish(),
1274
1275
  purpose: z7.string().nullish(),
1275
1276
  status: z7.string().nullish()
1276
1277
  })
1277
1278
  )
1278
1279
  );
1280
+ var xaiFileDeleteResponseSchema = lazySchema(
1281
+ () => zodSchema(
1282
+ z7.object({
1283
+ id: z7.string(),
1284
+ object: z7.string().nullish(),
1285
+ deleted: z7.boolean()
1286
+ })
1287
+ )
1288
+ );
1279
1289
 
1280
1290
  // src/responses/xai-responses-language-model.ts
1281
1291
  import {
@@ -3808,8 +3818,8 @@ function convertXaiChatBatchResponse(response) {
3808
3818
  }
3809
3819
  };
3810
3820
  }
3811
- const choice = (_a = response.choices) == null ? void 0 : _a[0];
3812
- if (choice == null) {
3821
+ const choices = response.choices;
3822
+ if (choices == null || choices.length === 0) {
3813
3823
  return {
3814
3824
  success: false,
3815
3825
  error: {
@@ -3818,18 +3828,50 @@ function convertXaiChatBatchResponse(response) {
3818
3828
  }
3819
3829
  };
3820
3830
  }
3821
- if ((_b = choice.message.tool_calls) == null ? void 0 : _b.length) {
3822
- return unsupportedXaiBatchContent("tool_calls");
3823
- }
3824
3831
  const content = [];
3825
- if (choice.message.content) {
3826
- content.push({ type: "text", text: choice.message.content });
3827
- }
3828
- if (choice.message.reasoning_content) {
3829
- content.push({
3830
- type: "reasoning",
3831
- text: choice.message.reasoning_content
3832
- });
3832
+ const providerExecutedToolCallIds = new Set(
3833
+ choices.filter((choice) => choice.message.role === "tool").flatMap(
3834
+ (choice) => {
3835
+ var _a2;
3836
+ return ((_a2 = choice.message.tool_calls) != null ? _a2 : []).map((toolCall) => toolCall.id);
3837
+ }
3838
+ )
3839
+ );
3840
+ let lastAssistantChoice;
3841
+ for (const choice of choices) {
3842
+ if (choice.message.role === "tool") {
3843
+ if (choice.message.content != null) {
3844
+ for (const toolCall of (_a = choice.message.tool_calls) != null ? _a : []) {
3845
+ content.push({
3846
+ type: "tool-result",
3847
+ toolCallId: toolCall.id,
3848
+ toolName: toolCall.function.name,
3849
+ result: choice.message.content,
3850
+ dynamic: true
3851
+ });
3852
+ }
3853
+ }
3854
+ continue;
3855
+ }
3856
+ lastAssistantChoice = choice;
3857
+ if (choice.message.content) {
3858
+ content.push({ type: "text", text: choice.message.content });
3859
+ }
3860
+ if (choice.message.reasoning_content) {
3861
+ content.push({
3862
+ type: "reasoning",
3863
+ text: choice.message.reasoning_content
3864
+ });
3865
+ }
3866
+ for (const toolCall of (_b = choice.message.tool_calls) != null ? _b : []) {
3867
+ content.push({
3868
+ type: "tool-call",
3869
+ toolCallId: toolCall.id,
3870
+ toolName: toolCall.function.name,
3871
+ input: toolCall.function.arguments,
3872
+ ...providerExecutedToolCallIds.has(toolCall.id) ? { providerExecuted: true, dynamic: true } : {}
3873
+ });
3874
+ }
3833
3875
  }
3834
3876
  for (const url of (_c = response.citations) != null ? _c : []) {
3835
3877
  content.push({
@@ -3844,8 +3886,8 @@ function convertXaiChatBatchResponse(response) {
3844
3886
  result: {
3845
3887
  content,
3846
3888
  finishReason: {
3847
- unified: mapXaiFinishReason(choice.finish_reason),
3848
- raw: (_d = choice.finish_reason) != null ? _d : void 0
3889
+ unified: mapXaiFinishReason(lastAssistantChoice == null ? void 0 : lastAssistantChoice.finish_reason),
3890
+ raw: (_d = lastAssistantChoice == null ? void 0 : lastAssistantChoice.finish_reason) != null ? _d : void 0
3849
3891
  },
3850
3892
  usage: response.usage ? convertXaiChatUsage(response.usage) : createNullLanguageModelUsage(),
3851
3893
  response: getResponseMetadata(response),
@@ -3861,15 +3903,6 @@ function convertXaiChatBatchResponse(response) {
3861
3903
  }
3862
3904
  };
3863
3905
  }
3864
- function unsupportedXaiBatchContent(type) {
3865
- return {
3866
- success: false,
3867
- error: {
3868
- message: `xAI returned "${type}" content, but tool content is not supported in AI SDK text batches.`,
3869
- code: "unsupported_content"
3870
- }
3871
- };
3872
- }
3873
3906
 
3874
3907
  // src/realtime/xai-realtime-event-mapper.ts
3875
3908
  function parseXaiRealtimeServerEvent(raw) {
@@ -4296,15 +4329,22 @@ var xaiTools = {
4296
4329
  };
4297
4330
 
4298
4331
  // src/version.ts
4299
- var VERSION = true ? "4.0.50" : "0.0.0-test";
4332
+ var VERSION = true ? "4.0.52" : "0.0.0-test";
4300
4333
 
4301
4334
  // src/files/xai-files.ts
4335
+ import {
4336
+ InvalidArgumentError as InvalidArgumentError2
4337
+ } from "@ai-sdk/provider";
4302
4338
  import {
4303
4339
  combineHeaders as combineHeaders5,
4304
4340
  convertInlineFileDataToUint8Array,
4341
+ createBinaryStreamResponseHandler,
4305
4342
  createJsonResponseHandler as createJsonResponseHandler5,
4343
+ deleteFromApi,
4344
+ getFromApi as getFromApi3,
4306
4345
  parseProviderOptions as parseProviderOptions6,
4307
- postFormDataToApi as postFormDataToApi2
4346
+ postFormDataToApi as postFormDataToApi2,
4347
+ postMultipartStreamToApi
4308
4348
  } from "@ai-sdk/provider-utils";
4309
4349
 
4310
4350
  // src/files/xai-files-options.ts
@@ -4317,12 +4357,22 @@ var xaiFilesOptionsSchema = lazySchema8(
4317
4357
  () => zodSchema8(
4318
4358
  z19.looseObject({
4319
4359
  teamId: z19.string().optional(),
4320
- filePath: z19.string().optional()
4360
+ filePath: z19.string().optional(),
4361
+ /**
4362
+ * TTL in seconds measured from upload time; xAI accepts integers
4363
+ * between 3600 (1 hour) and 2592000 (30 days) inclusive.
4364
+ * Omit to keep the file until it is deleted.
4365
+ */
4366
+ expiresAfter: z19.number().int().min(3600).max(2592e3).optional()
4321
4367
  })
4322
4368
  )
4323
4369
  );
4324
4370
 
4325
4371
  // src/files/xai-files.ts
4372
+ function encodePathSegment(value) {
4373
+ const encodedValue = encodeURIComponent(value);
4374
+ return encodedValue === "." ? "%252E" : encodedValue === ".." ? "%252E%252E" : encodedValue;
4375
+ }
4326
4376
  var XaiFiles = class {
4327
4377
  constructor(config) {
4328
4378
  this.config = config;
@@ -4331,55 +4381,205 @@ var XaiFiles = class {
4331
4381
  get provider() {
4332
4382
  return this.config.provider;
4333
4383
  }
4384
+ getFileId(file) {
4385
+ const fileId = file.xai;
4386
+ if (fileId == null || fileId.trim() === "") {
4387
+ throw new InvalidArgumentError2({
4388
+ argument: "file",
4389
+ message: "file reference is missing an 'xai' file id."
4390
+ });
4391
+ }
4392
+ return fileId;
4393
+ }
4394
+ getHeaders(headers) {
4395
+ return combineHeaders5(this.config.headers(), headers);
4396
+ }
4334
4397
  async uploadFile({
4335
4398
  data,
4336
4399
  mediaType,
4337
4400
  filename,
4401
+ abortSignal,
4402
+ headers,
4338
4403
  providerOptions
4339
4404
  }) {
4340
4405
  var _a, _b;
4341
- const xaiOptions = await parseProviderOptions6({
4342
- provider: "xai",
4343
- providerOptions,
4344
- schema: xaiFilesOptionsSchema
4345
- });
4346
- const fileBytes = convertInlineFileDataToUint8Array(data);
4347
- const blob = new Blob([fileBytes], {
4348
- type: mediaType
4349
- });
4350
- const formData = new FormData();
4351
- if (filename != null) {
4352
- formData.append("file", blob, filename);
4353
- } else {
4354
- formData.append("file", blob);
4406
+ let xaiOptions;
4407
+ try {
4408
+ xaiOptions = await parseProviderOptions6({
4409
+ provider: "xai",
4410
+ providerOptions,
4411
+ schema: xaiFilesOptionsSchema
4412
+ });
4413
+ } catch (error) {
4414
+ if (data.type === "stream") {
4415
+ await data.stream.cancel(error).catch(() => {
4416
+ });
4417
+ }
4418
+ throw error;
4355
4419
  }
4356
- if ((xaiOptions == null ? void 0 : xaiOptions.teamId) != null) {
4357
- formData.append("team_id", xaiOptions.teamId);
4420
+ const requestHeaders = this.getHeaders(headers);
4421
+ const url = `${this.config.baseURL}/files`;
4422
+ let response;
4423
+ if (data.type === "stream") {
4424
+ const parts = [];
4425
+ if ((xaiOptions == null ? void 0 : xaiOptions.expiresAfter) != null) {
4426
+ parts.push({
4427
+ type: "field",
4428
+ name: "expires_after",
4429
+ value: String(xaiOptions.expiresAfter)
4430
+ });
4431
+ }
4432
+ if ((xaiOptions == null ? void 0 : xaiOptions.teamId) != null) {
4433
+ parts.push({
4434
+ type: "field",
4435
+ name: "team_id",
4436
+ value: xaiOptions.teamId
4437
+ });
4438
+ }
4439
+ parts.push({
4440
+ type: "file",
4441
+ name: "file",
4442
+ filename,
4443
+ mediaType,
4444
+ content: data.stream
4445
+ });
4446
+ ({ value: response } = await postMultipartStreamToApi({
4447
+ url,
4448
+ headers: requestHeaders,
4449
+ parts,
4450
+ failedResponseHandler: xaiFailedResponseHandler,
4451
+ successfulResponseHandler: createJsonResponseHandler5(
4452
+ xaiFilesResponseSchema
4453
+ ),
4454
+ abortSignal,
4455
+ fetch: this.config.fetch
4456
+ }));
4457
+ } else {
4458
+ const fileBytes = convertInlineFileDataToUint8Array(data);
4459
+ const blob = new Blob([fileBytes], {
4460
+ type: mediaType
4461
+ });
4462
+ const formData = new FormData();
4463
+ if ((xaiOptions == null ? void 0 : xaiOptions.expiresAfter) != null) {
4464
+ formData.append("expires_after", String(xaiOptions.expiresAfter));
4465
+ }
4466
+ if ((xaiOptions == null ? void 0 : xaiOptions.teamId) != null) {
4467
+ formData.append("team_id", xaiOptions.teamId);
4468
+ }
4469
+ if (filename != null) {
4470
+ formData.append("file", blob, filename);
4471
+ } else {
4472
+ formData.append("file", blob);
4473
+ }
4474
+ ({ value: response } = await postFormDataToApi2({
4475
+ url,
4476
+ headers: requestHeaders,
4477
+ formData,
4478
+ failedResponseHandler: xaiFailedResponseHandler,
4479
+ successfulResponseHandler: createJsonResponseHandler5(
4480
+ xaiFilesResponseSchema
4481
+ ),
4482
+ abortSignal,
4483
+ fetch: this.config.fetch
4484
+ }));
4358
4485
  }
4359
- const { value: response } = await postFormDataToApi2({
4360
- url: `${this.config.baseURL}/files`,
4361
- headers: combineHeaders5(this.config.headers()),
4362
- formData,
4486
+ return {
4487
+ warnings: [],
4488
+ providerReference: { xai: response.id },
4489
+ ...((_a = response.filename) != null ? _a : filename) ? { filename: (_b = response.filename) != null ? _b : filename } : {},
4490
+ ...mediaType != null ? { mediaType } : {},
4491
+ ...response.bytes != null ? { byteSize: response.bytes } : {},
4492
+ ...response.created_at != null ? { createdAt: new Date(response.created_at * 1e3) } : {},
4493
+ ...response.expires_at != null ? { expiresAt: new Date(response.expires_at * 1e3) } : {},
4494
+ providerMetadata: {
4495
+ xai: this.toFileMetadata(response)
4496
+ }
4497
+ };
4498
+ }
4499
+ async getFileMetadata({
4500
+ file,
4501
+ abortSignal,
4502
+ headers
4503
+ }) {
4504
+ const fileId = this.getFileId(file);
4505
+ const { value: response } = await getFromApi3({
4506
+ url: `${this.config.baseURL}/files/${encodePathSegment(fileId)}`,
4507
+ headers: this.getHeaders(headers),
4363
4508
  failedResponseHandler: xaiFailedResponseHandler,
4364
4509
  successfulResponseHandler: createJsonResponseHandler5(
4365
4510
  xaiFilesResponseSchema
4366
4511
  ),
4367
- fetch: this.config.fetch
4512
+ abortSignal,
4513
+ fetch: this.config.fetch,
4514
+ validateUrl: false
4368
4515
  });
4369
4516
  return {
4370
4517
  warnings: [],
4371
4518
  providerReference: { xai: response.id },
4372
- ...((_a = response.filename) != null ? _a : filename) ? { filename: (_b = response.filename) != null ? _b : filename } : {},
4373
- ...mediaType != null ? { mediaType } : {},
4519
+ ...response.filename != null ? { filename: response.filename } : {},
4520
+ ...response.bytes != null ? { byteSize: response.bytes } : {},
4521
+ ...response.created_at != null ? { createdAt: new Date(response.created_at * 1e3) } : {},
4522
+ ...response.expires_at != null ? { expiresAt: new Date(response.expires_at * 1e3) } : {},
4374
4523
  providerMetadata: {
4375
- xai: {
4376
- ...response.filename != null ? { filename: response.filename } : {},
4377
- ...response.bytes != null ? { bytes: response.bytes } : {},
4378
- ...response.created_at != null ? { createdAt: response.created_at } : {}
4379
- }
4524
+ xai: this.toFileMetadata(response)
4380
4525
  }
4381
4526
  };
4382
4527
  }
4528
+ async downloadFile({
4529
+ file,
4530
+ abortSignal,
4531
+ headers
4532
+ }) {
4533
+ var _a;
4534
+ const fileId = this.getFileId(file);
4535
+ const { value: content, responseHeaders } = await getFromApi3({
4536
+ url: `${this.config.baseURL}/files/${encodePathSegment(fileId)}/content`,
4537
+ headers: this.getHeaders(headers),
4538
+ failedResponseHandler: xaiFailedResponseHandler,
4539
+ successfulResponseHandler: createBinaryStreamResponseHandler(),
4540
+ abortSignal,
4541
+ fetch: this.config.fetch,
4542
+ validateUrl: false
4543
+ });
4544
+ const mediaType = (_a = responseHeaders == null ? void 0 : responseHeaders["content-type"]) == null ? void 0 : _a.split(";")[0].trim();
4545
+ return {
4546
+ warnings: [],
4547
+ content,
4548
+ ...mediaType ? { mediaType } : {}
4549
+ };
4550
+ }
4551
+ async deleteFile({
4552
+ file,
4553
+ abortSignal,
4554
+ headers
4555
+ }) {
4556
+ const fileId = this.getFileId(file);
4557
+ const { value: response } = await deleteFromApi({
4558
+ url: `${this.config.baseURL}/files/${encodePathSegment(fileId)}`,
4559
+ headers: this.getHeaders(headers),
4560
+ failedResponseHandler: xaiFailedResponseHandler,
4561
+ successfulResponseHandler: createJsonResponseHandler5(
4562
+ xaiFileDeleteResponseSchema
4563
+ ),
4564
+ abortSignal,
4565
+ fetch: this.config.fetch
4566
+ });
4567
+ return {
4568
+ warnings: [],
4569
+ providerReference: { xai: response.id },
4570
+ deleted: response.deleted
4571
+ };
4572
+ }
4573
+ toFileMetadata(response) {
4574
+ return {
4575
+ ...response.filename != null ? { filename: response.filename } : {},
4576
+ ...response.purpose != null ? { purpose: response.purpose } : {},
4577
+ ...response.bytes != null ? { bytes: response.bytes } : {},
4578
+ ...response.created_at != null ? { createdAt: response.created_at } : {},
4579
+ ...response.status != null ? { status: response.status } : {},
4580
+ ...response.expires_at != null ? { expiresAt: response.expires_at } : {}
4581
+ };
4582
+ }
4383
4583
  };
4384
4584
 
4385
4585
  // src/xai-video-model.ts
@@ -4392,7 +4592,7 @@ import {
4392
4592
  convertUint8ArrayToBase64,
4393
4593
  createJsonResponseHandler as createJsonResponseHandler6,
4394
4594
  extractResponseHeaders as extractResponseHeaders2,
4395
- getFromApi as getFromApi3,
4595
+ getFromApi as getFromApi4,
4396
4596
  getTopLevelMediaType as getTopLevelMediaType3,
4397
4597
  parseProviderOptions as parseProviderOptions7,
4398
4598
  postJsonToApi as postJsonToApi5,
@@ -4424,7 +4624,7 @@ var xaiVideoModelOptionsSchema = lazySchema9(
4424
4624
  );
4425
4625
 
4426
4626
  // src/xai-video-model.ts
4427
- function encodePathSegment(value) {
4627
+ function encodePathSegment2(value) {
4428
4628
  const encodedValue = encodeURIComponent(value);
4429
4629
  return encodedValue === "." ? "%252E" : encodedValue === ".." ? "%252E%252E" : encodedValue;
4430
4630
  }
@@ -4747,8 +4947,8 @@ var XaiVideoModel = class {
4747
4947
  const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
4748
4948
  const { requestId } = options.operation;
4749
4949
  const baseURL = (_d = this.config.baseURL) != null ? _d : "https://api.x.ai/v1";
4750
- const { value: statusResponse, responseHeaders } = await getFromApi3({
4751
- url: `${baseURL}/videos/${encodePathSegment(requestId)}`,
4950
+ const { value: statusResponse, responseHeaders } = await getFromApi4({
4951
+ url: `${baseURL}/videos/${encodePathSegment2(requestId)}`,
4752
4952
  validateUrl: false,
4753
4953
  headers: combineHeaders6(this.config.headers(), options.headers),
4754
4954
  successfulResponseHandler: xaiVideoStatusResponseHandler,
@@ -5139,7 +5339,7 @@ var xaiSpeechTimestampsResponseSchema = z23.object({
5139
5339
 
5140
5340
  // src/xai-transcription-model.ts
5141
5341
  import {
5142
- InvalidArgumentError as InvalidArgumentError2
5342
+ InvalidArgumentError as InvalidArgumentError3
5143
5343
  } from "@ai-sdk/provider";
5144
5344
  import {
5145
5345
  combineHeaders as combineHeaders8,
@@ -5343,7 +5543,7 @@ var XaiTranscriptionModel = class _XaiTranscriptionModel {
5343
5543
  schema: xaiTranscriptionModelOptionsSchema
5344
5544
  });
5345
5545
  if ((xaiOptions == null ? void 0 : xaiOptions.multichannel) === true && xaiOptions.channels == null) {
5346
- throw new InvalidArgumentError2({
5546
+ throw new InvalidArgumentError3({
5347
5547
  argument: "providerOptions",
5348
5548
  message: "providerOptions.xai.channels is required when providerOptions.xai.multichannel is true"
5349
5549
  });