@ai-sdk/xai 3.0.104 → 3.0.106
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 +17 -0
- package/dist/index.d.mts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +667 -599
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +677 -609
- package/dist/index.mjs.map +1 -1
- package/docs/01-xai.mdx +42 -5
- package/package.json +4 -4
- package/src/convert-to-xai-chat-messages.ts +47 -34
- package/src/index.ts +1 -0
- package/src/responses/convert-to-xai-responses-input.ts +15 -2
- package/src/responses/xai-responses-api.ts +5 -1
- package/src/responses/xai-responses-options.ts +1 -0
- package/src/xai-chat-language-model.ts +1 -1
- package/src/xai-chat-options.ts +1 -0
- package/src/xai-chat-prompt.ts +4 -1
- package/src/xai-file-part-options.ts +21 -0
- package/src/xai-video-model.ts +55 -9
package/docs/01-xai.mdx
CHANGED
|
@@ -73,10 +73,10 @@ You can use the following optional settings to customize the xAI provider instan
|
|
|
73
73
|
## Language Models
|
|
74
74
|
|
|
75
75
|
You can create [xAI models](https://console.x.ai) using a provider instance. The
|
|
76
|
-
first argument is the model id, e.g. `grok-4.
|
|
76
|
+
first argument is the model id, e.g. `grok-4.5`.
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
|
-
const model = xai('grok-4.
|
|
79
|
+
const model = xai('grok-4.5');
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
By default, `xai(modelId)` uses the Chat API. To use the Responses API with server-side agentic tools, explicitly use `xai.responses(modelId)`.
|
|
@@ -90,7 +90,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
90
90
|
import { generateText } from 'ai';
|
|
91
91
|
|
|
92
92
|
const { text } = await generateText({
|
|
93
|
-
model: xai('grok-4.
|
|
93
|
+
model: xai('grok-4.5'),
|
|
94
94
|
prompt: 'Write a vegetarian lasagna recipe for 4 people.',
|
|
95
95
|
});
|
|
96
96
|
```
|
|
@@ -149,7 +149,7 @@ The following optional provider options are available for xAI chat models:
|
|
|
149
149
|
You can use the xAI Responses API with the `xai.responses(modelId)` factory method for server-side agentic tool calling. This enables the model to autonomously orchestrate tool calls and research on xAI's servers.
|
|
150
150
|
|
|
151
151
|
```ts
|
|
152
|
-
const model = xai.responses('grok-4.
|
|
152
|
+
const model = xai.responses('grok-4.5');
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
The Responses API provides server-side tools that the model can autonomously execute during its reasoning process:
|
|
@@ -171,7 +171,7 @@ import { xai } from '@ai-sdk/xai';
|
|
|
171
171
|
import { generateText } from 'ai';
|
|
172
172
|
|
|
173
173
|
const { text } = await generateText({
|
|
174
|
-
model: xai.responses('grok-
|
|
174
|
+
model: xai.responses('grok-4.5'),
|
|
175
175
|
messages: [
|
|
176
176
|
{
|
|
177
177
|
role: 'user',
|
|
@@ -184,6 +184,42 @@ const { text } = await generateText({
|
|
|
184
184
|
});
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
+
You can control the resolution at which the model processes an image with
|
|
188
|
+
the `imageDetail` provider option on the image part:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { xai } from '@ai-sdk/xai';
|
|
192
|
+
import { generateText } from 'ai';
|
|
193
|
+
|
|
194
|
+
const { text } = await generateText({
|
|
195
|
+
model: xai('grok-4.5'),
|
|
196
|
+
messages: [
|
|
197
|
+
{
|
|
198
|
+
role: 'user',
|
|
199
|
+
content: [
|
|
200
|
+
{ type: 'text', text: 'What do you see in this image?' },
|
|
201
|
+
{
|
|
202
|
+
type: 'file',
|
|
203
|
+
mediaType: 'image/png',
|
|
204
|
+
data: fs.readFileSync('./image.png'),
|
|
205
|
+
providerOptions: {
|
|
206
|
+
xai: { imageDetail: 'low' },
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The following image detail values are supported:
|
|
216
|
+
|
|
217
|
+
- **low**: processes the image at reduced resolution and consumes fewer input tokens.
|
|
218
|
+
- **high**: processes the image at full resolution.
|
|
219
|
+
- **auto**: lets the xAI API decide.
|
|
220
|
+
|
|
221
|
+
When not set, the image is processed at full resolution.
|
|
222
|
+
|
|
187
223
|
### Web Search Tool
|
|
188
224
|
|
|
189
225
|
The web search tool enables autonomous web research with optional domain filtering and image understanding:
|
|
@@ -786,6 +822,7 @@ console.log('Sources:', await result.sources);
|
|
|
786
822
|
|
|
787
823
|
| Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Reasoning |
|
|
788
824
|
| ----------------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
|
|
825
|
+
| `grok-4.5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
|
789
826
|
| `grok-4.20-reasoning` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
|
790
827
|
| `grok-4.20-non-reasoning` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Cross size={18} /> |
|
|
791
828
|
| `grok-4-1-fast-reasoning` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/xai",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.106",
|
|
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.
|
|
33
|
-
"@ai-sdk/provider": "
|
|
34
|
-
"@ai-sdk/provider
|
|
32
|
+
"@ai-sdk/openai-compatible": "2.0.59",
|
|
33
|
+
"@ai-sdk/provider-utils": "4.0.38",
|
|
34
|
+
"@ai-sdk/provider": "3.0.14"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "20.17.24",
|
|
@@ -3,13 +3,16 @@ import {
|
|
|
3
3
|
type SharedV3Warning,
|
|
4
4
|
type LanguageModelV3Prompt,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
|
-
import { convertToBase64 } from '@ai-sdk/provider-utils';
|
|
7
|
-
import type { XaiChatPrompt } from './xai-chat-prompt';
|
|
6
|
+
import { convertToBase64, parseProviderOptions } from '@ai-sdk/provider-utils';
|
|
7
|
+
import type { XaiChatPrompt, XaiUserMessageContent } from './xai-chat-prompt';
|
|
8
|
+
import { xaiFilePartProviderOptions } from './xai-file-part-options';
|
|
8
9
|
|
|
9
|
-
export function convertToXaiChatMessages(
|
|
10
|
+
export async function convertToXaiChatMessages(
|
|
11
|
+
prompt: LanguageModelV3Prompt,
|
|
12
|
+
): Promise<{
|
|
10
13
|
messages: XaiChatPrompt;
|
|
11
14
|
warnings: Array<SharedV3Warning>;
|
|
12
|
-
} {
|
|
15
|
+
}> {
|
|
13
16
|
const messages: XaiChatPrompt = [];
|
|
14
17
|
const warnings: Array<SharedV3Warning> = [];
|
|
15
18
|
|
|
@@ -26,38 +29,48 @@ export function convertToXaiChatMessages(prompt: LanguageModelV3Prompt): {
|
|
|
26
29
|
break;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
32
|
+
const userContent: Array<XaiUserMessageContent> = [];
|
|
33
|
+
|
|
34
|
+
for (const part of content) {
|
|
35
|
+
switch (part.type) {
|
|
36
|
+
case 'text': {
|
|
37
|
+
userContent.push({ type: 'text', text: part.text });
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
case 'file': {
|
|
41
|
+
if (part.mediaType.startsWith('image/')) {
|
|
42
|
+
const mediaType =
|
|
43
|
+
part.mediaType === 'image/*' ? 'image/jpeg' : part.mediaType;
|
|
44
|
+
|
|
45
|
+
const filePartOptions = await parseProviderOptions({
|
|
46
|
+
provider: 'xai',
|
|
47
|
+
providerOptions: part.providerOptions,
|
|
48
|
+
schema: xaiFilePartProviderOptions,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
userContent.push({
|
|
52
|
+
type: 'image_url',
|
|
53
|
+
image_url: {
|
|
54
|
+
url:
|
|
55
|
+
part.data instanceof URL
|
|
56
|
+
? part.data.toString()
|
|
57
|
+
: `data:${mediaType};base64,${convertToBase64(part.data)}`,
|
|
58
|
+
...(filePartOptions?.imageDetail != null && {
|
|
59
|
+
detail: filePartOptions.imageDetail,
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
throw new UnsupportedFunctionalityError({
|
|
65
|
+
functionality: `file part media type ${part.mediaType}`,
|
|
66
|
+
});
|
|
57
67
|
}
|
|
68
|
+
break;
|
|
58
69
|
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
messages.push({ role: 'user', content: userContent });
|
|
61
74
|
|
|
62
75
|
break;
|
|
63
76
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type {
|
|
|
4
4
|
XaiLanguageModelChatOptions as XaiProviderOptions,
|
|
5
5
|
} from './xai-chat-options';
|
|
6
6
|
export type { XaiErrorData } from './xai-error';
|
|
7
|
+
export type { XaiFilePartProviderOptions } from './xai-file-part-options';
|
|
7
8
|
export type {
|
|
8
9
|
XaiLanguageModelResponsesOptions,
|
|
9
10
|
/** @deprecated Use `XaiLanguageModelResponsesOptions` instead. */
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
type SharedV3Warning,
|
|
4
4
|
type LanguageModelV3Message,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
|
-
import { convertToBase64 } from '@ai-sdk/provider-utils';
|
|
6
|
+
import { convertToBase64, parseProviderOptions } from '@ai-sdk/provider-utils';
|
|
7
|
+
import { xaiFilePartProviderOptions } from '../xai-file-part-options';
|
|
7
8
|
import type {
|
|
8
9
|
XaiResponsesInput,
|
|
9
10
|
XaiResponsesUserMessageContentPart,
|
|
@@ -53,7 +54,19 @@ export async function convertToXaiResponsesInput({
|
|
|
53
54
|
? block.data.toString()
|
|
54
55
|
: `data:${mediaType};base64,${convertToBase64(block.data)}`;
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
const filePartOptions = await parseProviderOptions({
|
|
58
|
+
provider: 'xai',
|
|
59
|
+
providerOptions: block.providerOptions,
|
|
60
|
+
schema: xaiFilePartProviderOptions,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
contentParts.push({
|
|
64
|
+
type: 'input_image',
|
|
65
|
+
image_url: imageUrl,
|
|
66
|
+
...(filePartOptions?.imageDetail != null && {
|
|
67
|
+
detail: filePartOptions.imageDetail,
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
57
70
|
} else if (block.data instanceof URL) {
|
|
58
71
|
// xAI's Responses API accepts non-image documents (PDF, text, CSV, etc.)
|
|
59
72
|
// via `{ type: 'input_file', file_url }`. See
|
|
@@ -26,7 +26,11 @@ export type XaiResponsesSystemMessage = {
|
|
|
26
26
|
|
|
27
27
|
export type XaiResponsesUserMessageContentPart =
|
|
28
28
|
| { type: 'input_text'; text: string }
|
|
29
|
-
| {
|
|
29
|
+
| {
|
|
30
|
+
type: 'input_image';
|
|
31
|
+
image_url: string;
|
|
32
|
+
detail?: 'low' | 'high' | 'auto';
|
|
33
|
+
}
|
|
30
34
|
| { type: 'input_file'; file_url: string };
|
|
31
35
|
|
|
32
36
|
export type XaiResponsesUserMessage = {
|
|
@@ -105,7 +105,7 @@ export class XaiChatLanguageModel implements LanguageModelV3 {
|
|
|
105
105
|
|
|
106
106
|
// convert ai sdk messages to xai format
|
|
107
107
|
const { messages, warnings: messageWarnings } =
|
|
108
|
-
convertToXaiChatMessages(prompt);
|
|
108
|
+
await convertToXaiChatMessages(prompt);
|
|
109
109
|
warnings.push(...messageWarnings);
|
|
110
110
|
|
|
111
111
|
// prepare tools for xai
|
package/src/xai-chat-options.ts
CHANGED
package/src/xai-chat-prompt.ts
CHANGED
|
@@ -18,7 +18,10 @@ export interface XaiUserMessage {
|
|
|
18
18
|
|
|
19
19
|
export type XaiUserMessageContent =
|
|
20
20
|
| { type: 'text'; text: string }
|
|
21
|
-
| {
|
|
21
|
+
| {
|
|
22
|
+
type: 'image_url';
|
|
23
|
+
image_url: { url: string; detail?: 'low' | 'high' | 'auto' };
|
|
24
|
+
};
|
|
22
25
|
|
|
23
26
|
export interface XaiAssistantMessage {
|
|
24
27
|
role: 'assistant';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
|
|
3
|
+
// provider options for file parts (images) in user messages
|
|
4
|
+
export const xaiFilePartProviderOptions = z.object({
|
|
5
|
+
/**
|
|
6
|
+
* Controls the resolution at which the model processes the image.
|
|
7
|
+
* `low` processes the image at reduced resolution and consumes fewer
|
|
8
|
+
* input tokens, `high` processes the image at full resolution, and
|
|
9
|
+
* `auto` lets the API decide. Defaults to full resolution when not set.
|
|
10
|
+
*
|
|
11
|
+
* Note: the xAI API silently ignores invalid values, so the value is
|
|
12
|
+
* validated client-side.
|
|
13
|
+
*
|
|
14
|
+
* @see https://docs.x.ai/developers/model-capabilities/images/understanding
|
|
15
|
+
*/
|
|
16
|
+
imageDetail: z.enum(['low', 'high', 'auto']).optional(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type XaiFilePartProviderOptions = z.infer<
|
|
20
|
+
typeof xaiFilePartProviderOptions
|
|
21
|
+
>;
|
package/src/xai-video-model.ts
CHANGED
|
@@ -62,6 +62,14 @@ function resolveStartImage(
|
|
|
62
62
|
return getFirstFrameImage(options) ?? options.image;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function getTopLevelMediaType(mediaType: string): string {
|
|
66
|
+
const slashIndex = mediaType.indexOf('/');
|
|
67
|
+
return slashIndex === -1 ? mediaType : mediaType.substring(0, slashIndex);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const isVideoFile = (file: Experimental_VideoModelV3File): boolean =>
|
|
71
|
+
file.mediaType != null && getTopLevelMediaType(file.mediaType) === 'video';
|
|
72
|
+
|
|
65
73
|
function fileToXaiImageUrl(file: Experimental_VideoModelV3File): string {
|
|
66
74
|
if (file.type === 'url') {
|
|
67
75
|
return file.url;
|
|
@@ -71,17 +79,35 @@ function fileToXaiImageUrl(file: Experimental_VideoModelV3File): string {
|
|
|
71
79
|
typeof file.data === 'string'
|
|
72
80
|
? file.data
|
|
73
81
|
: convertUint8ArrayToBase64(file.data);
|
|
74
|
-
return `data:${file.mediaType
|
|
82
|
+
return `data:${file.mediaType};base64,${base64Data}`;
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
// Resolves the reference images for R2V generation. First-class
|
|
78
86
|
// `inputReferences` win over the legacy `referenceImageUrls` provider option.
|
|
87
|
+
// Video references are not supported for reference-to-video and are skipped
|
|
88
|
+
// with a warning.
|
|
79
89
|
function resolveReferenceImages(
|
|
80
90
|
options: XaiVideoDoGenerateOptions,
|
|
81
91
|
xaiOptions: XaiParsedVideoModelOptions | undefined,
|
|
92
|
+
warnings: SharedV3Warning[],
|
|
82
93
|
): Array<{ url: string }> | undefined {
|
|
83
94
|
if (options.inputReferences != null && options.inputReferences.length > 0) {
|
|
84
|
-
|
|
95
|
+
const imageReferences = options.inputReferences.filter(reference => {
|
|
96
|
+
if (isVideoFile(reference)) {
|
|
97
|
+
warnings.push({
|
|
98
|
+
type: 'unsupported',
|
|
99
|
+
feature: 'inputReferences',
|
|
100
|
+
details:
|
|
101
|
+
'xAI reference-to-video accepts image references only. The video ' +
|
|
102
|
+
'reference was ignored. Use providerOptions.xai.mode ' +
|
|
103
|
+
'"extend-video" to continue from a video.',
|
|
104
|
+
});
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return imageReferences.map(reference => ({
|
|
85
111
|
url: fileToXaiImageUrl(reference),
|
|
86
112
|
}));
|
|
87
113
|
}
|
|
@@ -278,24 +304,44 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
278
304
|
// nested xAI request image object.
|
|
279
305
|
const startImage = resolveStartImage(options);
|
|
280
306
|
if (startImage != null) {
|
|
281
|
-
|
|
307
|
+
if (isVideoFile(startImage)) {
|
|
308
|
+
const fromFrameImages = getFirstFrameImage(options) != null;
|
|
309
|
+
warnings.push({
|
|
310
|
+
type: 'unsupported',
|
|
311
|
+
feature: fromFrameImages ? 'frameImages' : 'image',
|
|
312
|
+
details:
|
|
313
|
+
'xAI does not accept a video as a start/frame image. The video ' +
|
|
314
|
+
'was ignored. Use providerOptions.xai.mode "extend-video" to ' +
|
|
315
|
+
'continue from a video instead.',
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
body.image = { url: fileToXaiImageUrl(startImage) };
|
|
319
|
+
}
|
|
282
320
|
}
|
|
283
321
|
|
|
284
322
|
// xAI has no first-last-frame interpolation; warn and ignore last_frame.
|
|
285
|
-
|
|
323
|
+
const lastFrameImage = getLastFrameImage(options);
|
|
324
|
+
if (lastFrameImage != null) {
|
|
286
325
|
warnings.push({
|
|
287
326
|
type: 'unsupported',
|
|
288
327
|
feature: 'frameImages',
|
|
289
|
-
details:
|
|
290
|
-
'xAI video
|
|
291
|
-
|
|
292
|
-
|
|
328
|
+
details: isVideoFile(lastFrameImage)
|
|
329
|
+
? 'xAI does not accept a video as a start/frame image. The video ' +
|
|
330
|
+
'last frame was ignored. Use providerOptions.xai.mode ' +
|
|
331
|
+
'"extend-video" to continue from a video instead.'
|
|
332
|
+
: 'xAI video models do not support last_frame. Use ' +
|
|
333
|
+
'providerOptions.xai.mode "extend-video" to continue from a ' +
|
|
334
|
+
"video's last frame. The last frame image was ignored.",
|
|
293
335
|
});
|
|
294
336
|
}
|
|
295
337
|
|
|
296
338
|
// Reference images for R2V (reference-to-video) generation
|
|
297
339
|
if (hasReferenceImages) {
|
|
298
|
-
const referenceImages = resolveReferenceImages(
|
|
340
|
+
const referenceImages = resolveReferenceImages(
|
|
341
|
+
options,
|
|
342
|
+
xaiOptions,
|
|
343
|
+
warnings,
|
|
344
|
+
);
|
|
299
345
|
if (referenceImages != null) {
|
|
300
346
|
body.reference_images = referenceImages;
|
|
301
347
|
}
|