@ai-sdk/xai 3.0.103 → 3.0.105
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 +19 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +635 -595
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +645 -605
- package/dist/index.mjs.map +1 -1
- package/docs/01-xai.mdx +36 -0
- package/package.json +4 -4
- package/src/convert-to-xai-chat-messages.ts +48 -35
- package/src/index.ts +1 -0
- package/src/responses/convert-to-xai-responses-input.ts +15 -2
- package/src/responses/xai-responses-api.ts +8 -1
- package/src/responses/xai-responses-language-model.ts +8 -5
- package/src/xai-chat-language-model.ts +1 -1
- package/src/xai-chat-prompt.ts +4 -1
- package/src/xai-file-part-options.ts +21 -0
package/docs/01-xai.mdx
CHANGED
|
@@ -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.3'),
|
|
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:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/xai",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.105",
|
|
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.58",
|
|
33
|
+
"@ai-sdk/provider-utils": "4.0.37",
|
|
34
|
+
"@ai-sdk/provider": "3.0.13"
|
|
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
|
}
|
|
@@ -113,7 +126,7 @@ export function convertToXaiChatMessages(prompt: LanguageModelV3Prompt): {
|
|
|
113
126
|
contentValue = output.value;
|
|
114
127
|
break;
|
|
115
128
|
case 'execution-denied':
|
|
116
|
-
contentValue = output.reason ?? 'Tool execution denied.';
|
|
129
|
+
contentValue = output.reason ?? 'Tool call execution denied.';
|
|
117
130
|
break;
|
|
118
131
|
case 'content':
|
|
119
132
|
case 'json':
|
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 = {
|
|
@@ -225,6 +229,9 @@ const outputItemSchema = z.discriminatedUnion('type', [
|
|
|
225
229
|
type: z.literal('reasoning'),
|
|
226
230
|
id: z.string(),
|
|
227
231
|
summary: z.array(reasoningSummaryPartSchema),
|
|
232
|
+
content: z
|
|
233
|
+
.array(z.object({ type: z.string(), text: z.string() }))
|
|
234
|
+
.nullish(),
|
|
228
235
|
status: z.string(),
|
|
229
236
|
encrypted_content: z.string().nullish(),
|
|
230
237
|
}),
|
|
@@ -367,11 +367,14 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
case 'reasoning': {
|
|
370
|
-
const
|
|
371
|
-
.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
370
|
+
const texts =
|
|
371
|
+
part.summary.length > 0
|
|
372
|
+
? part.summary.map(s => s.text)
|
|
373
|
+
: (part.content ?? []).map(c => c.text);
|
|
374
|
+
|
|
375
|
+
const reasoningText = texts
|
|
376
|
+
.filter(text => text && text.length > 0)
|
|
377
|
+
.join('');
|
|
375
378
|
|
|
376
379
|
// condition changed here since encrypted content can now come with empty reasoning text
|
|
377
380
|
if (reasoningText || part.encrypted_content) {
|
|
@@ -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-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
|
+
>;
|