@ai-sdk/xai 3.0.113 → 3.0.115

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "3.0.113",
3
+ "version": "3.0.115",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -29,9 +29,9 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@ai-sdk/openai-compatible": "2.0.63",
32
+ "@ai-sdk/openai-compatible": "2.0.64",
33
33
  "@ai-sdk/provider": "3.0.14",
34
- "@ai-sdk/provider-utils": "4.0.41"
34
+ "@ai-sdk/provider-utils": "4.0.42"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "20.17.24",
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  AISDKError,
3
+ APICallError,
3
4
  type Experimental_VideoModelV3,
4
5
  type Experimental_VideoModelV3File,
5
6
  type SharedV3Warning,
6
7
  } from '@ai-sdk/provider';
7
8
  import {
8
- cancelResponseBody,
9
9
  combineHeaders,
10
10
  convertUint8ArrayToBase64,
11
11
  createJsonResponseHandler,
@@ -15,6 +15,7 @@ import {
15
15
  getFromApi,
16
16
  parseProviderOptions,
17
17
  postJsonToApi,
18
+ safeParseJSON,
18
19
  type ResponseHandler,
19
20
  } from '@ai-sdk/provider-utils';
20
21
  import { z } from 'zod/v4';
@@ -552,16 +553,80 @@ const xaiVideoStatusJsonResponseHandler = createJsonResponseHandler(
552
553
  xaiVideoStatusResponseSchema,
553
554
  );
554
555
 
556
+ // Generous bound for a `{status, progress}` payload of ~50 bytes.
557
+ const MAX_PENDING_BODY_BYTES = 1024 * 1024;
558
+
559
+ const textDecoder = new TextDecoder();
560
+
561
+ // Bounded replacement for `response.text()`. Throws on overflow without
562
+ // cancelling the body: cancelling a tee branch neither settles nor releases
563
+ // the underlying source while the sibling branch is live.
564
+ async function readPendingBody({
565
+ response,
566
+ url,
567
+ requestBodyValues,
568
+ }: Parameters<ResponseHandler<unknown>>[0]): Promise<string> {
569
+ if (response.body == null) {
570
+ return '';
571
+ }
572
+
573
+ const reader = response.body.getReader();
574
+ const chunks: Uint8Array[] = [];
575
+ let totalBytes = 0;
576
+
577
+ try {
578
+ while (true) {
579
+ const { done, value } = await reader.read();
580
+ if (done) break;
581
+
582
+ totalBytes += value.length;
583
+ if (totalBytes > MAX_PENDING_BODY_BYTES) {
584
+ throw new APICallError({
585
+ message: `xAI video status response exceeded ${MAX_PENDING_BODY_BYTES} bytes`,
586
+ url,
587
+ requestBodyValues,
588
+ statusCode: response.status,
589
+ responseHeaders: extractResponseHeaders(response),
590
+ });
591
+ }
592
+
593
+ chunks.push(value);
594
+ }
595
+ } finally {
596
+ reader.releaseLock();
597
+ }
598
+
599
+ const merged = new Uint8Array(totalBytes);
600
+ let offset = 0;
601
+ for (const chunk of chunks) {
602
+ merged.set(chunk, offset);
603
+ offset += chunk.length;
604
+ }
605
+ return textDecoder.decode(merged);
606
+ }
607
+
555
608
  const xaiVideoStatusResponseHandler: ResponseHandler<
556
609
  z.infer<typeof xaiVideoStatusResponseSchema>
557
610
  > = async options => {
611
+ // xAI answers 202 while a generation is still running, sometimes with an
612
+ // empty body. Read it rather than cancelling: `body.cancel()` never settles
613
+ // on a tee branch, which `Response.clone()` in fetch instrumentation creates.
558
614
  if (options.response.status === 202) {
559
615
  const responseHeaders = extractResponseHeaders(options.response);
560
- await cancelResponseBody(options.response);
616
+ const text = await readPendingBody(options);
617
+
618
+ if (text.trim().length === 0) {
619
+ return { responseHeaders, value: { status: 'pending' } };
620
+ }
621
+
622
+ const parsed = await safeParseJSON({
623
+ text,
624
+ schema: xaiVideoStatusResponseSchema,
625
+ });
561
626
 
562
627
  return {
563
628
  responseHeaders,
564
- value: { status: 'pending' },
629
+ value: parsed.success ? parsed.value : { status: 'pending' },
565
630
  };
566
631
  }
567
632