@ai-sdk/xai 3.0.112 → 3.0.114

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
@@ -99,6 +99,26 @@ xAI language models can also be used in the `streamText` function
99
99
  and support structured data generation with [`Output`](/docs/reference/ai-sdk-core/output)
100
100
  (see [AI SDK Core](/docs/ai-sdk-core)).
101
101
 
102
+ ### Reasoning Effort
103
+
104
+ For models with configurable reasoning, you can control how much effort the
105
+ model spends thinking before responding with
106
+ `providerOptions.xai.reasoningEffort`. The AI SDK option accepts `'none'`,
107
+ `'low'`, `'medium'`, and `'high'`, but each xAI model supports a subset.
108
+
109
+ <Note>
110
+ Support and defaults are model-specific. `grok-4.3` supports `'none'`,
111
+ `'low'`, `'medium'`, and `'high'`. `grok-4.5` supports `'low'`, `'medium'`,
112
+ and `'high'`, defaults to `'high'`, and cannot disable reasoning. The
113
+ `grok-4.20-reasoning` and `grok-4.20-non-reasoning` variants do not accept
114
+ this option, and neither does `grok-build-0.1`. For
115
+ `grok-4.20-multi-agent`, `'low'`, `'medium'`, and `'high'` control the number
116
+ of agents instead of reasoning depth. See xAI's [reasoning
117
+ docs](https://docs.x.ai/developers/model-capabilities/text/reasoning) and
118
+ [Grok 4.3 model page](https://docs.x.ai/developers/models/grok-4.3) for
119
+ current details.
120
+ </Note>
121
+
102
122
  ### Provider Options
103
123
 
104
124
  xAI chat models support additional provider options that are not part of
@@ -107,7 +127,7 @@ the [standard call settings](/docs/ai-sdk-core/settings). You can pass them in t
107
127
  ```ts
108
128
  import { xai, type XaiLanguageModelChatOptions } from '@ai-sdk/xai';
109
129
 
110
- const model = xai('grok-3-mini');
130
+ const model = xai('grok-4.5');
111
131
 
112
132
  await generateText({
113
133
  model,
@@ -123,14 +143,7 @@ The following optional provider options are available for xAI chat models:
123
143
 
124
144
  - **reasoningEffort** _'none' | 'low' | 'medium' | 'high'_
125
145
 
126
- Reasoning effort for reasoning models. `'none'` disables reasoning entirely.
127
-
128
- <Note>
129
- Not every Grok model accepts every reasoning effort, for example
130
- `grok-build-0.1` does not support reasoning effort. See xAI's [reasoning
131
- docs](https://docs.x.ai/docs/guides/reasoning) for the values each model
132
- accepts.
133
- </Note>
146
+ Control the reasoning effort for supported models. See [Reasoning Effort](#reasoning-effort) for model-specific values and defaults.
134
147
 
135
148
  - **logprobs** _boolean_
136
149
 
@@ -485,7 +498,7 @@ import { xai, type XaiLanguageModelResponsesOptions } from '@ai-sdk/xai';
485
498
  import { generateText } from 'ai';
486
499
 
487
500
  const result = await generateText({
488
- model: xai.responses('grok-4.20-non-reasoning'),
501
+ model: xai.responses('grok-4.5'),
489
502
  providerOptions: {
490
503
  xai: {
491
504
  reasoningEffort: 'high',
@@ -499,14 +512,7 @@ The following provider options are available:
499
512
 
500
513
  - **reasoningEffort** _'none' | 'low' | 'medium' | 'high'_
501
514
 
502
- Control the reasoning effort for the model. Higher effort may produce more thorough results at the cost of increased latency and token usage. `'none'` disables reasoning entirely.
503
-
504
- <Note>
505
- Not every Grok model accepts every reasoning effort, for example
506
- `grok-build-0.1` does not support reasoning effort. See xAI's [reasoning
507
- docs](https://docs.x.ai/docs/guides/reasoning) for the values each model
508
- accepts.
509
- </Note>
515
+ Control the reasoning effort for supported models. See [Reasoning Effort](#reasoning-effort) for model-specific values and defaults.
510
516
 
511
517
  - **logprobs** _boolean_
512
518
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "3.0.112",
3
+ "version": "3.0.114",
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.62",
32
+ "@ai-sdk/openai-compatible": "2.0.63",
33
33
  "@ai-sdk/provider": "3.0.14",
34
- "@ai-sdk/provider-utils": "4.0.40"
34
+ "@ai-sdk/provider-utils": "4.0.41"
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