@ai-sdk/xai 4.0.38 → 4.0.40
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 +21 -0
- package/README.md +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +126 -59
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +55 -16
- package/package.json +3 -3
- package/src/xai-error.ts +12 -2
- package/src/xai-speech-model-options.ts +14 -0
- package/src/xai-speech-model.ts +78 -10
- package/src/xai-video-model.ts +19 -8
package/docs/01-xai.mdx
CHANGED
|
@@ -59,10 +59,10 @@ You can use the following optional settings to customize the xAI provider instan
|
|
|
59
59
|
## Language Models
|
|
60
60
|
|
|
61
61
|
You can create [xAI models](https://console.x.ai) using a provider instance. The
|
|
62
|
-
first argument is the model id, e.g. `grok-4.
|
|
62
|
+
first argument is the model id, e.g. `grok-4.6`.
|
|
63
63
|
|
|
64
64
|
```ts
|
|
65
|
-
const model = xai('grok-4.
|
|
65
|
+
const model = xai('grok-4.6');
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
<Note>
|
|
@@ -81,7 +81,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
81
81
|
import { generateText } from 'ai';
|
|
82
82
|
|
|
83
83
|
const { text } = await generateText({
|
|
84
|
-
model: xai('grok-4.
|
|
84
|
+
model: xai('grok-4.6'),
|
|
85
85
|
prompt: 'Write a vegetarian lasagna recipe for 4 people.',
|
|
86
86
|
});
|
|
87
87
|
```
|
|
@@ -199,7 +199,7 @@ calling pattern.
|
|
|
199
199
|
The xAI Responses API is the default when using `xai(modelId)` (since AI SDK 7). You can also use `xai.responses(modelId)` explicitly. This enables the model to autonomously orchestrate tool calls and research on xAI's servers.
|
|
200
200
|
|
|
201
201
|
```ts
|
|
202
|
-
const model = xai.responses('grok-4.
|
|
202
|
+
const model = xai.responses('grok-4.6');
|
|
203
203
|
```
|
|
204
204
|
|
|
205
205
|
The Responses API provides server-side tools that the model can autonomously execute during its reasoning process:
|
|
@@ -221,7 +221,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
221
221
|
import { generateText } from 'ai';
|
|
222
222
|
|
|
223
223
|
const { text } = await generateText({
|
|
224
|
-
model: xai.responses('grok-
|
|
224
|
+
model: xai.responses('grok-4.6'),
|
|
225
225
|
messages: [
|
|
226
226
|
{
|
|
227
227
|
role: 'user',
|
|
@@ -246,7 +246,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
246
246
|
import { generateText } from 'ai';
|
|
247
247
|
|
|
248
248
|
const { text } = await generateText({
|
|
249
|
-
model: xai('grok-4.
|
|
249
|
+
model: xai('grok-4.6'),
|
|
250
250
|
messages: [
|
|
251
251
|
{
|
|
252
252
|
role: 'user',
|
|
@@ -283,7 +283,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
283
283
|
import { generateText } from 'ai';
|
|
284
284
|
|
|
285
285
|
const { text, sources } = await generateText({
|
|
286
|
-
model: xai.responses('grok-4.
|
|
286
|
+
model: xai.responses('grok-4.6'),
|
|
287
287
|
prompt: 'What are the latest developments in AI?',
|
|
288
288
|
tools: {
|
|
289
289
|
web_search: xai.tools.webSearch({
|
|
@@ -321,7 +321,7 @@ The X search tool enables searching X (Twitter) for posts, with filtering by han
|
|
|
321
321
|
|
|
322
322
|
```ts
|
|
323
323
|
const { text, sources } = await generateText({
|
|
324
|
-
model: xai.responses('grok-4.
|
|
324
|
+
model: xai.responses('grok-4.6'),
|
|
325
325
|
prompt: 'What are people saying about AI on X this week?',
|
|
326
326
|
tools: {
|
|
327
327
|
x_search: xai.tools.xSearch({
|
|
@@ -367,7 +367,7 @@ The code execution tool enables the model to write and execute Python code for c
|
|
|
367
367
|
|
|
368
368
|
```ts
|
|
369
369
|
const { text } = await generateText({
|
|
370
|
-
model: xai.responses('grok-4.
|
|
370
|
+
model: xai.responses('grok-4.6'),
|
|
371
371
|
prompt:
|
|
372
372
|
'Calculate the compound interest for $10,000 at 5% annually for 10 years',
|
|
373
373
|
tools: {
|
|
@@ -382,7 +382,7 @@ The view image tool enables the model to view and analyze images:
|
|
|
382
382
|
|
|
383
383
|
```ts
|
|
384
384
|
const { text } = await generateText({
|
|
385
|
-
model: xai.responses('grok-4.
|
|
385
|
+
model: xai.responses('grok-4.6'),
|
|
386
386
|
prompt: 'Describe what you see in the image',
|
|
387
387
|
tools: {
|
|
388
388
|
view_image: xai.tools.viewImage(),
|
|
@@ -396,7 +396,7 @@ The view X video tool enables the model to view and analyze videos from X (Twitt
|
|
|
396
396
|
|
|
397
397
|
```ts
|
|
398
398
|
const { text } = await generateText({
|
|
399
|
-
model: xai.responses('grok-4.
|
|
399
|
+
model: xai.responses('grok-4.6'),
|
|
400
400
|
prompt: 'Summarize the content of this X video',
|
|
401
401
|
tools: {
|
|
402
402
|
view_x_video: xai.tools.viewXVideo(),
|
|
@@ -413,7 +413,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
413
413
|
import { generateText } from 'ai';
|
|
414
414
|
|
|
415
415
|
const result = await generateText({
|
|
416
|
-
model: xai.responses('grok-4.
|
|
416
|
+
model: xai.responses('grok-4.6'),
|
|
417
417
|
prompt:
|
|
418
418
|
'Generate an image of a corgi surfing a big wave, in the style of a Japanese woodblock print',
|
|
419
419
|
tools: {
|
|
@@ -451,7 +451,7 @@ The MCP server tool enables the model to connect to remote [Model Context Protoc
|
|
|
451
451
|
|
|
452
452
|
```ts
|
|
453
453
|
const { text } = await generateText({
|
|
454
|
-
model: xai.responses('grok-4.
|
|
454
|
+
model: xai.responses('grok-4.6'),
|
|
455
455
|
prompt: 'Use the weather tool to check conditions in San Francisco',
|
|
456
456
|
tools: {
|
|
457
457
|
weather_server: xai.tools.mcpServer({
|
|
@@ -499,7 +499,7 @@ import { xai, type XaiLanguageModelResponsesOptions } from '@ai-sdk/xai';
|
|
|
499
499
|
import { streamText } from 'ai';
|
|
500
500
|
|
|
501
501
|
const result = streamText({
|
|
502
|
-
model: xai.responses('grok-4.
|
|
502
|
+
model: xai.responses('grok-4.6'),
|
|
503
503
|
prompt: 'What documents do you have access to?',
|
|
504
504
|
tools: {
|
|
505
505
|
file_search: xai.tools.fileSearch({
|
|
@@ -546,7 +546,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
546
546
|
import { streamText } from 'ai';
|
|
547
547
|
|
|
548
548
|
const { stream } = streamText({
|
|
549
|
-
model: xai.responses('grok-4.
|
|
549
|
+
model: xai.responses('grok-4.6'),
|
|
550
550
|
prompt: 'Research AI safety developments and calculate risk metrics',
|
|
551
551
|
tools: {
|
|
552
552
|
web_search: xai.tools.webSearch(),
|
|
@@ -580,7 +580,7 @@ import { xai, type XaiLanguageModelResponsesOptions } from '@ai-sdk/xai';
|
|
|
580
580
|
import { generateText } from 'ai';
|
|
581
581
|
|
|
582
582
|
const result = await generateText({
|
|
583
|
-
model: xai.responses('grok-4.
|
|
583
|
+
model: xai.responses('grok-4.6'),
|
|
584
584
|
providerOptions: {
|
|
585
585
|
xai: {
|
|
586
586
|
reasoningEffort: 'high',
|
|
@@ -746,6 +746,45 @@ const result = await generateSpeech({
|
|
|
746
746
|
|
|
747
747
|
Whether to normalize written-form input text before synthesizing speech.
|
|
748
748
|
|
|
749
|
+
- **withTimestamps** _boolean_
|
|
750
|
+
|
|
751
|
+
Return character-level timing metadata alongside the audio. The timing data
|
|
752
|
+
and total duration are exposed via `providerMetadata.xai` (see below).
|
|
753
|
+
|
|
754
|
+
- **replace** _Record<string, string>_
|
|
755
|
+
|
|
756
|
+
Map of phrases to spoken substitutions applied before synthesis. Values may
|
|
757
|
+
be respellings (`{ 'Acme Mobile': 'Acme Mobull' }`) or IPA phonetics
|
|
758
|
+
(`{ nginx: '/ˈɛndʒɪn ˈɛks/' }`).
|
|
759
|
+
|
|
760
|
+
### Provider Metadata
|
|
761
|
+
|
|
762
|
+
xAI speech results include provider-specific metadata under
|
|
763
|
+
`providerMetadata.xai`:
|
|
764
|
+
|
|
765
|
+
- **traceId** _string_ — the xAI trace ID for the request, useful for
|
|
766
|
+
debugging with xAI support.
|
|
767
|
+
- **duration** _number_ — total audio duration in seconds (only with
|
|
768
|
+
`withTimestamps`).
|
|
769
|
+
- **contentType** _string_ — MIME type of the decoded audio, e.g.
|
|
770
|
+
`'audio/mpeg'` (only with `withTimestamps`).
|
|
771
|
+
- **audioTimestamps** `{ graphChars: string[]; graphTimes: [number, number][] }` —
|
|
772
|
+
per-character alignment data (only with `withTimestamps`). `graphChars[i]`
|
|
773
|
+
is the character spoken during the `[start, end]` interval
|
|
774
|
+
`graphTimes[i]`, in seconds.
|
|
775
|
+
|
|
776
|
+
```ts
|
|
777
|
+
const result = await generateSpeech({
|
|
778
|
+
model: xai.speech(),
|
|
779
|
+
text: 'Hello world.',
|
|
780
|
+
providerOptions: {
|
|
781
|
+
xai: { withTimestamps: true } satisfies XaiSpeechModelOptions,
|
|
782
|
+
},
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
const { traceId, duration, audioTimestamps } = result.providerMetadata.xai;
|
|
786
|
+
```
|
|
787
|
+
|
|
749
788
|
### Model Capabilities
|
|
750
789
|
|
|
751
790
|
| Model | Language | Speed | Output Formats |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/xai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.40",
|
|
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
|
|
33
|
-
"@ai-sdk/provider": "
|
|
32
|
+
"@ai-sdk/provider": "4.0.7",
|
|
33
|
+
"@ai-sdk/provider-utils": "5.0.27"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "22.19.19",
|
package/src/xai-error.ts
CHANGED
|
@@ -15,15 +15,25 @@ const responsesErrorSchema = z.object({
|
|
|
15
15
|
error: z.string(),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
// Text to Speech error shape, e.g. {"error":"speed must be between 0.7 and 1.5"}
|
|
19
|
+
const speechErrorSchema = z.object({
|
|
20
|
+
error: z.string(),
|
|
21
|
+
});
|
|
22
|
+
|
|
18
23
|
export const xaiErrorDataSchema = z.union([
|
|
19
24
|
chatCompletionsErrorSchema,
|
|
20
25
|
responsesErrorSchema,
|
|
26
|
+
speechErrorSchema,
|
|
21
27
|
]);
|
|
22
28
|
|
|
23
29
|
export type XaiErrorData = z.infer<typeof xaiErrorDataSchema>;
|
|
24
30
|
|
|
25
31
|
export const xaiFailedResponseHandler = createJsonErrorResponseHandler({
|
|
26
32
|
errorSchema: xaiErrorDataSchema,
|
|
27
|
-
errorToMessage: data =>
|
|
28
|
-
|
|
33
|
+
errorToMessage: data => {
|
|
34
|
+
if (typeof data.error === 'string') {
|
|
35
|
+
return 'code' in data ? `${data.code}: ${data.error}` : data.error;
|
|
36
|
+
}
|
|
37
|
+
return data.error.message;
|
|
38
|
+
},
|
|
29
39
|
});
|
|
@@ -46,6 +46,20 @@ export const xaiSpeechModelOptionsSchema = lazySchema(() =>
|
|
|
46
46
|
* Normalize written-form text into spoken-form text before synthesis.
|
|
47
47
|
*/
|
|
48
48
|
textNormalization: z.boolean().nullish(),
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Return character-level timing metadata alongside the audio. When
|
|
52
|
+
* enabled, the response carries per-character start/end times and the
|
|
53
|
+
* total duration, exposed via `providerMetadata.xai`.
|
|
54
|
+
*/
|
|
55
|
+
withTimestamps: z.boolean().nullish(),
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Map of phrases to spoken substitutions applied before synthesis.
|
|
59
|
+
* Values may be respellings (`{ 'Acme Mobile': 'Acme Mobull' }`) or IPA
|
|
60
|
+
* phonetics (`{ nginx: '/ˈɛndʒɪn ˈɛks/' }`).
|
|
61
|
+
*/
|
|
62
|
+
replace: z.record(z.string(), z.string()).nullish(),
|
|
49
63
|
}),
|
|
50
64
|
),
|
|
51
65
|
);
|
package/src/xai-speech-model.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { SharedV4Warning, SpeechModelV4 } from '@ai-sdk/provider';
|
|
2
2
|
import {
|
|
3
3
|
combineHeaders,
|
|
4
|
+
convertBase64ToUint8Array,
|
|
4
5
|
createBinaryResponseHandler,
|
|
6
|
+
createJsonResponseHandler,
|
|
5
7
|
parseProviderOptions,
|
|
6
8
|
postJsonToApi,
|
|
7
9
|
resolve,
|
|
@@ -11,6 +13,7 @@ import {
|
|
|
11
13
|
type FetchFunction,
|
|
12
14
|
type Resolvable,
|
|
13
15
|
} from '@ai-sdk/provider-utils';
|
|
16
|
+
import { z } from 'zod/v4';
|
|
14
17
|
import { xaiFailedResponseHandler } from './xai-error';
|
|
15
18
|
import { xaiSpeechModelOptionsSchema } from './xai-speech-model-options';
|
|
16
19
|
|
|
@@ -122,22 +125,30 @@ export class XaiSpeechModel implements SpeechModelV4 {
|
|
|
122
125
|
speed,
|
|
123
126
|
optimize_streaming_latency: xaiOptions?.optimizeStreamingLatency,
|
|
124
127
|
text_normalization: xaiOptions?.textNormalization,
|
|
128
|
+
with_timestamps: xaiOptions?.withTimestamps,
|
|
129
|
+
replace: xaiOptions?.replace,
|
|
125
130
|
};
|
|
126
131
|
|
|
127
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
requestBody,
|
|
134
|
+
warnings,
|
|
135
|
+
withTimestamps: xaiOptions?.withTimestamps === true,
|
|
136
|
+
};
|
|
128
137
|
}
|
|
129
138
|
|
|
130
139
|
async doGenerate(
|
|
131
140
|
options: Parameters<SpeechModelV4['doGenerate']>[0],
|
|
132
141
|
): Promise<Awaited<ReturnType<SpeechModelV4['doGenerate']>>> {
|
|
133
142
|
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
134
|
-
const { requestBody, warnings } =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
} = await postJsonToApi
|
|
143
|
+
const { requestBody, warnings, withTimestamps } =
|
|
144
|
+
await this.getArgs(options);
|
|
145
|
+
|
|
146
|
+
// With `with_timestamps` the API returns a JSON envelope carrying
|
|
147
|
+
// base64-encoded audio plus character-level timings instead of raw
|
|
148
|
+
// audio bytes.
|
|
149
|
+
const { value, responseHeaders, rawValue } = await postJsonToApi<
|
|
150
|
+
Uint8Array | XaiSpeechTimestampsResponse
|
|
151
|
+
>({
|
|
141
152
|
url: `${this.config.baseURL}/tts`,
|
|
142
153
|
headers: combineHeaders(
|
|
143
154
|
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
@@ -145,11 +156,30 @@ export class XaiSpeechModel implements SpeechModelV4 {
|
|
|
145
156
|
),
|
|
146
157
|
body: requestBody,
|
|
147
158
|
failedResponseHandler: xaiFailedResponseHandler,
|
|
148
|
-
successfulResponseHandler:
|
|
159
|
+
successfulResponseHandler: withTimestamps
|
|
160
|
+
? createJsonResponseHandler(xaiSpeechTimestampsResponseSchema)
|
|
161
|
+
: createBinaryResponseHandler(),
|
|
149
162
|
abortSignal: options.abortSignal,
|
|
150
163
|
fetch: this.config.fetch,
|
|
151
164
|
});
|
|
152
165
|
|
|
166
|
+
let audio: Uint8Array;
|
|
167
|
+
let envelope: XaiSpeechTimestampsResponse | undefined;
|
|
168
|
+
if (value instanceof Uint8Array) {
|
|
169
|
+
audio = value;
|
|
170
|
+
} else {
|
|
171
|
+
envelope = value;
|
|
172
|
+
// Empty audio is returned as-is so the core layer throws
|
|
173
|
+
// NoSpeechGeneratedError.
|
|
174
|
+
audio =
|
|
175
|
+
envelope.audio != null
|
|
176
|
+
? convertBase64ToUint8Array(envelope.audio)
|
|
177
|
+
: new Uint8Array(0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// xAI returns a trace id on every response (success and error).
|
|
181
|
+
const traceId = responseHeaders?.['x-trace-id'];
|
|
182
|
+
|
|
153
183
|
return {
|
|
154
184
|
audio,
|
|
155
185
|
warnings,
|
|
@@ -160,8 +190,46 @@ export class XaiSpeechModel implements SpeechModelV4 {
|
|
|
160
190
|
timestamp: currentDate,
|
|
161
191
|
modelId: this.modelId,
|
|
162
192
|
headers: responseHeaders,
|
|
163
|
-
body:
|
|
193
|
+
body: rawValue,
|
|
194
|
+
},
|
|
195
|
+
providerMetadata: {
|
|
196
|
+
xai: {
|
|
197
|
+
...(traceId != null ? { traceId } : {}),
|
|
198
|
+
...(envelope?.duration != null
|
|
199
|
+
? { duration: envelope.duration }
|
|
200
|
+
: {}),
|
|
201
|
+
...(envelope?.content_type != null
|
|
202
|
+
? { contentType: envelope.content_type }
|
|
203
|
+
: {}),
|
|
204
|
+
...(envelope?.audio_timestamps != null
|
|
205
|
+
? {
|
|
206
|
+
audioTimestamps: {
|
|
207
|
+
graphChars: envelope.audio_timestamps.graph_chars,
|
|
208
|
+
graphTimes: envelope.audio_timestamps.graph_times,
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
: {}),
|
|
212
|
+
},
|
|
164
213
|
},
|
|
165
214
|
};
|
|
166
215
|
}
|
|
167
216
|
}
|
|
217
|
+
|
|
218
|
+
// Minimal schema for the `with_timestamps` JSON envelope: only the fields
|
|
219
|
+
// the implementation reads, with `.nullish()` so provider API changes don't
|
|
220
|
+
// break parsing.
|
|
221
|
+
const xaiSpeechTimestampsResponseSchema = z.object({
|
|
222
|
+
audio: z.string().nullish(),
|
|
223
|
+
content_type: z.string().nullish(),
|
|
224
|
+
duration: z.number().nullish(),
|
|
225
|
+
audio_timestamps: z
|
|
226
|
+
.object({
|
|
227
|
+
graph_chars: z.array(z.string()),
|
|
228
|
+
graph_times: z.array(z.tuple([z.number(), z.number()])),
|
|
229
|
+
})
|
|
230
|
+
.nullish(),
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
type XaiSpeechTimestampsResponse = z.infer<
|
|
234
|
+
typeof xaiSpeechTimestampsResponseSchema
|
|
235
|
+
>;
|
package/src/xai-video-model.ts
CHANGED
|
@@ -581,19 +581,30 @@ export class XaiVideoModel implements VideoModelV4 {
|
|
|
581
581
|
statusResponse.status === 'done' ||
|
|
582
582
|
(statusResponse.status == null && statusResponse.video?.url)
|
|
583
583
|
) {
|
|
584
|
+
// Terminal outcomes, so they are reported the same way as an upstream `failed`
|
|
584
585
|
if (statusResponse.video?.respect_moderation === false) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
586
|
+
return {
|
|
587
|
+
status: 'error' as const,
|
|
588
|
+
error:
|
|
588
589
|
'Video generation was blocked due to a content policy violation.',
|
|
589
|
-
|
|
590
|
+
response: {
|
|
591
|
+
timestamp: currentDate,
|
|
592
|
+
modelId: this.modelId,
|
|
593
|
+
headers: responseHeaders,
|
|
594
|
+
},
|
|
595
|
+
};
|
|
590
596
|
}
|
|
591
597
|
|
|
592
598
|
if (!statusResponse.video?.url) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
599
|
+
return {
|
|
600
|
+
status: 'error' as const,
|
|
601
|
+
error: 'Video generation completed but no video URL was returned.',
|
|
602
|
+
response: {
|
|
603
|
+
timestamp: currentDate,
|
|
604
|
+
modelId: this.modelId,
|
|
605
|
+
headers: responseHeaders,
|
|
606
|
+
},
|
|
607
|
+
};
|
|
597
608
|
}
|
|
598
609
|
|
|
599
610
|
return {
|