@ai-sdk/xai 4.0.0-beta.6 → 4.0.0-beta.75
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 +667 -9
- package/README.md +2 -0
- package/dist/index.d.ts +218 -68
- package/dist/index.js +2081 -779
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +445 -54
- package/package.json +15 -15
- package/src/convert-to-xai-chat-messages.ts +48 -27
- package/src/convert-xai-chat-usage.ts +3 -3
- package/src/files/xai-files-api.ts +16 -0
- package/src/files/xai-files-options.ts +19 -0
- package/src/files/xai-files.ts +94 -0
- package/src/index.ts +9 -4
- package/src/map-xai-finish-reason.ts +2 -2
- package/src/realtime/index.ts +2 -0
- package/src/realtime/xai-realtime-event-mapper.ts +399 -0
- package/src/realtime/xai-realtime-model-options.ts +3 -0
- package/src/realtime/xai-realtime-model.ts +101 -0
- package/src/remove-additional-properties.ts +24 -0
- package/src/responses/convert-to-xai-responses-input.ts +100 -23
- package/src/responses/convert-xai-responses-usage.ts +3 -3
- package/src/responses/map-xai-responses-finish-reason.ts +3 -2
- package/src/responses/xai-responses-api.ts +34 -1
- package/src/responses/{xai-responses-options.ts → xai-responses-language-model-options.ts} +13 -7
- package/src/responses/xai-responses-language-model.ts +173 -64
- package/src/responses/xai-responses-prepare-tools.ts +10 -8
- package/src/tool/code-execution.ts +2 -2
- package/src/tool/file-search.ts +2 -2
- package/src/tool/mcp-server.ts +2 -2
- package/src/tool/view-image.ts +2 -2
- package/src/tool/view-x-video.ts +2 -2
- package/src/tool/web-search.ts +4 -2
- package/src/tool/x-search.ts +2 -2
- package/src/{xai-chat-options.ts → xai-chat-language-model-options.ts} +28 -13
- package/src/xai-chat-language-model.ts +65 -29
- package/src/xai-chat-prompt.ts +2 -1
- package/src/xai-error.ts +13 -3
- package/src/xai-image-model.ts +28 -11
- package/src/xai-prepare-tools.ts +9 -8
- package/src/xai-provider.ts +115 -19
- package/src/xai-speech-model-options.ts +55 -0
- package/src/xai-speech-model.ts +167 -0
- package/src/xai-transcription-model-options.ts +70 -0
- package/src/xai-transcription-model.ts +166 -0
- package/src/xai-video-model-options.ts +145 -0
- package/src/xai-video-model.ts +129 -22
- package/dist/index.d.mts +0 -372
- package/dist/index.mjs +0 -3061
- package/dist/index.mjs.map +0 -1
- package/src/xai-video-options.ts +0 -23
- /package/src/{xai-image-options.ts → xai-image-model-options.ts} +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { LanguageModelV4FinishReason } from '@ai-sdk/provider';
|
|
2
2
|
|
|
3
3
|
export function mapXaiResponsesFinishReason(
|
|
4
4
|
finishReason: string | null | undefined,
|
|
5
|
-
):
|
|
5
|
+
): LanguageModelV4FinishReason['unified'] {
|
|
6
6
|
switch (finishReason) {
|
|
7
7
|
case 'stop':
|
|
8
8
|
case 'completed':
|
|
9
9
|
return 'stop';
|
|
10
10
|
case 'length':
|
|
11
|
+
case 'max_output_tokens':
|
|
11
12
|
return 'length';
|
|
12
13
|
case 'tool_calls':
|
|
13
14
|
case 'function_call':
|
|
@@ -26,7 +26,9 @@ export type XaiResponsesSystemMessage = {
|
|
|
26
26
|
|
|
27
27
|
export type XaiResponsesUserMessageContentPart =
|
|
28
28
|
| { type: 'input_text'; text: string }
|
|
29
|
-
| { type: 'input_image'; image_url: string }
|
|
29
|
+
| { type: 'input_image'; image_url: string }
|
|
30
|
+
| { type: 'input_file'; file_id: string }
|
|
31
|
+
| { type: 'input_file'; file_url: string };
|
|
30
32
|
|
|
31
33
|
export type XaiResponsesUserMessage = {
|
|
32
34
|
role: 'user';
|
|
@@ -77,6 +79,7 @@ export type XaiResponsesTool =
|
|
|
77
79
|
type: 'web_search';
|
|
78
80
|
allowed_domains?: string[];
|
|
79
81
|
excluded_domains?: string[];
|
|
82
|
+
enable_image_search?: boolean;
|
|
80
83
|
enable_image_understanding?: boolean;
|
|
81
84
|
}
|
|
82
85
|
| {
|
|
@@ -223,6 +226,9 @@ const outputItemSchema = z.discriminatedUnion('type', [
|
|
|
223
226
|
type: z.literal('reasoning'),
|
|
224
227
|
id: z.string(),
|
|
225
228
|
summary: z.array(reasoningSummaryPartSchema),
|
|
229
|
+
content: z
|
|
230
|
+
.array(z.object({ type: z.string(), text: z.string() }))
|
|
231
|
+
.nullish(),
|
|
226
232
|
status: z.string(),
|
|
227
233
|
encrypted_content: z.string().nullish(),
|
|
228
234
|
}),
|
|
@@ -244,6 +250,7 @@ export const xaiResponsesUsageSchema = z.object({
|
|
|
244
250
|
.optional(),
|
|
245
251
|
num_sources_used: z.number().optional(),
|
|
246
252
|
num_server_side_tools_used: z.number().optional(),
|
|
253
|
+
cost_in_usd_ticks: z.number().nullish(),
|
|
247
254
|
});
|
|
248
255
|
|
|
249
256
|
export const xaiResponsesResponseSchema = z.object({
|
|
@@ -518,6 +525,32 @@ export const xaiResponsesChunkSchema = z.union([
|
|
|
518
525
|
output_index: z.number(),
|
|
519
526
|
output: z.string().optional(),
|
|
520
527
|
}),
|
|
528
|
+
z.object({
|
|
529
|
+
type: z.literal('response.incomplete'),
|
|
530
|
+
response: z.object({
|
|
531
|
+
incomplete_details: z.object({ reason: z.string() }).nullish(),
|
|
532
|
+
usage: xaiResponsesUsageSchema.nullish(),
|
|
533
|
+
}),
|
|
534
|
+
}),
|
|
535
|
+
z.object({
|
|
536
|
+
type: z.literal('response.failed'),
|
|
537
|
+
response: z.object({
|
|
538
|
+
error: z
|
|
539
|
+
.object({
|
|
540
|
+
code: z.string().nullish(),
|
|
541
|
+
message: z.string(),
|
|
542
|
+
})
|
|
543
|
+
.nullish(),
|
|
544
|
+
incomplete_details: z.object({ reason: z.string() }).nullish(),
|
|
545
|
+
usage: xaiResponsesUsageSchema.nullish(),
|
|
546
|
+
}),
|
|
547
|
+
}),
|
|
548
|
+
z.object({
|
|
549
|
+
type: z.literal('error'),
|
|
550
|
+
code: z.string().nullish(),
|
|
551
|
+
message: z.string(),
|
|
552
|
+
param: z.string().nullish(),
|
|
553
|
+
}),
|
|
521
554
|
z.object({
|
|
522
555
|
type: z.literal('response.done'),
|
|
523
556
|
response: xaiResponsesResponseSchema,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
|
|
3
3
|
export type XaiResponsesModelId =
|
|
4
|
-
| 'grok-4-
|
|
5
|
-
| 'grok-4-
|
|
6
|
-
| 'grok-4'
|
|
7
|
-
| 'grok-
|
|
8
|
-
| 'grok-4-fast-reasoning'
|
|
4
|
+
| 'grok-4.20-non-reasoning'
|
|
5
|
+
| 'grok-4.20-reasoning'
|
|
6
|
+
| 'grok-4.3'
|
|
7
|
+
| 'grok-latest'
|
|
9
8
|
| (string & {});
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -14,13 +13,20 @@ export type XaiResponsesModelId =
|
|
|
14
13
|
export const xaiLanguageModelResponsesOptions = z.object({
|
|
15
14
|
/**
|
|
16
15
|
* Constrains how hard a reasoning model thinks before responding.
|
|
17
|
-
* Possible values are `
|
|
16
|
+
* Possible values are `none` (disables reasoning entirely; supported by
|
|
17
|
+
* `grok-4.3` and newer reasoning models), `low` (uses fewer reasoning
|
|
18
|
+
* tokens), `medium`, and `high` (uses more reasoning tokens).
|
|
19
|
+
*
|
|
20
|
+
* @see https://docs.x.ai/docs/guides/reasoning
|
|
18
21
|
*/
|
|
19
|
-
reasoningEffort: z.enum(['low', 'medium', 'high']).optional(),
|
|
22
|
+
reasoningEffort: z.enum(['none', 'low', 'medium', 'high']).optional(),
|
|
23
|
+
reasoningSummary: z.enum(['auto', 'concise', 'detailed']).optional(),
|
|
20
24
|
logprobs: z.boolean().optional(),
|
|
21
25
|
topLogprobs: z.number().int().min(0).max(8).optional(),
|
|
22
26
|
/**
|
|
23
27
|
* Whether to store the input message(s) and model response for later retrieval.
|
|
28
|
+
* Must be set to `false` for teams with Zero Data Retention (ZDR) enabled,
|
|
29
|
+
* otherwise the API will return an error.
|
|
24
30
|
* @default true
|
|
25
31
|
*/
|
|
26
32
|
store: z.boolean().optional(),
|
|
@@ -1,55 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4,
|
|
3
|
+
LanguageModelV4CallOptions,
|
|
4
|
+
LanguageModelV4Content,
|
|
5
|
+
LanguageModelV4FinishReason,
|
|
6
|
+
LanguageModelV4GenerateResult,
|
|
7
|
+
LanguageModelV4StreamPart,
|
|
8
|
+
LanguageModelV4StreamResult,
|
|
9
|
+
LanguageModelV4Usage,
|
|
10
|
+
SharedV4Warning,
|
|
11
11
|
} from '@ai-sdk/provider';
|
|
12
12
|
import {
|
|
13
13
|
combineHeaders,
|
|
14
14
|
createEventSourceResponseHandler,
|
|
15
15
|
createJsonResponseHandler,
|
|
16
|
-
|
|
16
|
+
isCustomReasoning,
|
|
17
|
+
mapReasoningToProviderEffort,
|
|
17
18
|
parseProviderOptions,
|
|
18
|
-
ParseResult,
|
|
19
19
|
postJsonToApi,
|
|
20
|
+
serializeModelOptions,
|
|
21
|
+
WORKFLOW_SERIALIZE,
|
|
22
|
+
WORKFLOW_DESERIALIZE,
|
|
23
|
+
type FetchFunction,
|
|
24
|
+
type ParseResult,
|
|
20
25
|
} from '@ai-sdk/provider-utils';
|
|
21
|
-
import { z } from 'zod/v4';
|
|
26
|
+
import type { z } from 'zod/v4';
|
|
22
27
|
import { getResponseMetadata } from '../get-response-metadata';
|
|
23
28
|
import { xaiFailedResponseHandler } from '../xai-error';
|
|
24
29
|
import { convertToXaiResponsesInput } from './convert-to-xai-responses-input';
|
|
25
30
|
import { convertXaiResponsesUsage } from './convert-xai-responses-usage';
|
|
26
31
|
import { mapXaiResponsesFinishReason } from './map-xai-responses-finish-reason';
|
|
27
32
|
import {
|
|
28
|
-
XaiResponsesIncludeOptions,
|
|
29
33
|
xaiResponsesChunkSchema,
|
|
30
34
|
xaiResponsesResponseSchema,
|
|
35
|
+
type XaiResponsesIncludeOptions,
|
|
31
36
|
} from './xai-responses-api';
|
|
32
37
|
import {
|
|
33
|
-
XaiResponsesModelId,
|
|
34
38
|
xaiLanguageModelResponsesOptions,
|
|
35
|
-
|
|
39
|
+
type XaiResponsesModelId,
|
|
40
|
+
} from './xai-responses-language-model-options';
|
|
36
41
|
import { prepareResponsesTools } from './xai-responses-prepare-tools';
|
|
37
42
|
|
|
38
43
|
type XaiResponsesConfig = {
|
|
39
44
|
provider: string;
|
|
40
45
|
baseURL: string | undefined;
|
|
41
|
-
headers
|
|
46
|
+
headers?: () => Record<string, string | undefined>;
|
|
42
47
|
generateId: () => string;
|
|
43
48
|
fetch?: FetchFunction;
|
|
44
49
|
};
|
|
45
50
|
|
|
46
|
-
export class XaiResponsesLanguageModel implements
|
|
47
|
-
readonly specificationVersion = '
|
|
51
|
+
export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
52
|
+
readonly specificationVersion = 'v4';
|
|
48
53
|
|
|
49
54
|
readonly modelId: XaiResponsesModelId;
|
|
50
55
|
|
|
51
56
|
private readonly config: XaiResponsesConfig;
|
|
52
57
|
|
|
58
|
+
static [WORKFLOW_SERIALIZE](model: XaiResponsesLanguageModel) {
|
|
59
|
+
return serializeModelOptions({
|
|
60
|
+
modelId: model.modelId,
|
|
61
|
+
config: model.config,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
66
|
+
modelId: XaiResponsesModelId;
|
|
67
|
+
config: XaiResponsesConfig;
|
|
68
|
+
}) {
|
|
69
|
+
return new XaiResponsesLanguageModel(options.modelId, options.config);
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
constructor(modelId: XaiResponsesModelId, config: XaiResponsesConfig) {
|
|
54
73
|
this.modelId = modelId;
|
|
55
74
|
this.config = config;
|
|
@@ -61,6 +80,11 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
61
80
|
|
|
62
81
|
readonly supportedUrls: Record<string, RegExp[]> = {
|
|
63
82
|
'image/*': [/^https?:\/\/.*$/],
|
|
83
|
+
// xAI's Responses API accepts non-image documents (PDF, plain text, CSV, etc.) as
|
|
84
|
+
// `{ type: 'input_file', file_url }`. Keeping these URLs intact here lets them pass
|
|
85
|
+
// through to the converter instead of being downloaded to bytes by the SDK.
|
|
86
|
+
'application/pdf': [/^https?:\/\/.*$/],
|
|
87
|
+
'text/*': [/^https?:\/\/.*$/],
|
|
64
88
|
};
|
|
65
89
|
|
|
66
90
|
private async getArgs({
|
|
@@ -74,8 +98,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
74
98
|
providerOptions,
|
|
75
99
|
tools,
|
|
76
100
|
toolChoice,
|
|
77
|
-
|
|
78
|
-
|
|
101
|
+
reasoning,
|
|
102
|
+
}: LanguageModelV4CallOptions) {
|
|
103
|
+
const warnings: SharedV4Warning[] = [];
|
|
79
104
|
|
|
80
105
|
const options =
|
|
81
106
|
(await parseProviderOptions({
|
|
@@ -110,7 +135,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
110
135
|
|
|
111
136
|
const { input, inputWarnings } = await convertToXaiResponsesInput({
|
|
112
137
|
prompt,
|
|
113
|
-
store: true,
|
|
138
|
+
store: options.store ?? true,
|
|
114
139
|
});
|
|
115
140
|
warnings.push(...inputWarnings);
|
|
116
141
|
|
|
@@ -139,6 +164,24 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
139
164
|
}
|
|
140
165
|
}
|
|
141
166
|
|
|
167
|
+
const resolvedReasoningEffort =
|
|
168
|
+
options.reasoningEffort ??
|
|
169
|
+
(isCustomReasoning(reasoning)
|
|
170
|
+
? reasoning === 'none'
|
|
171
|
+
? undefined
|
|
172
|
+
: mapReasoningToProviderEffort({
|
|
173
|
+
reasoning,
|
|
174
|
+
effortMap: {
|
|
175
|
+
minimal: 'low',
|
|
176
|
+
low: 'low',
|
|
177
|
+
medium: 'medium',
|
|
178
|
+
high: 'high',
|
|
179
|
+
xhigh: 'high',
|
|
180
|
+
},
|
|
181
|
+
warnings,
|
|
182
|
+
})
|
|
183
|
+
: undefined);
|
|
184
|
+
|
|
142
185
|
const baseArgs: Record<string, unknown> = {
|
|
143
186
|
model: this.modelId,
|
|
144
187
|
input,
|
|
@@ -165,8 +208,16 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
165
208
|
: { type: 'json_object' },
|
|
166
209
|
},
|
|
167
210
|
}),
|
|
168
|
-
...(
|
|
169
|
-
|
|
211
|
+
...((resolvedReasoningEffort != null ||
|
|
212
|
+
options.reasoningSummary != null) && {
|
|
213
|
+
reasoning: {
|
|
214
|
+
...(resolvedReasoningEffort != null && {
|
|
215
|
+
effort: resolvedReasoningEffort,
|
|
216
|
+
}),
|
|
217
|
+
...(options.reasoningSummary != null && {
|
|
218
|
+
summary: options.reasoningSummary,
|
|
219
|
+
}),
|
|
220
|
+
},
|
|
170
221
|
}),
|
|
171
222
|
...(options.store === false && {
|
|
172
223
|
store: options.store,
|
|
@@ -199,8 +250,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
199
250
|
}
|
|
200
251
|
|
|
201
252
|
async doGenerate(
|
|
202
|
-
options:
|
|
203
|
-
): Promise<
|
|
253
|
+
options: LanguageModelV4CallOptions,
|
|
254
|
+
): Promise<LanguageModelV4GenerateResult> {
|
|
204
255
|
const {
|
|
205
256
|
args: body,
|
|
206
257
|
warnings,
|
|
@@ -217,7 +268,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
217
268
|
rawValue: rawResponse,
|
|
218
269
|
} = await postJsonToApi({
|
|
219
270
|
url: `${this.config.baseURL ?? 'https://api.x.ai/v1'}/responses`,
|
|
220
|
-
headers: combineHeaders(this.config.headers(), options.headers),
|
|
271
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
221
272
|
body,
|
|
222
273
|
failedResponseHandler: xaiFailedResponseHandler,
|
|
223
274
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -227,7 +278,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
227
278
|
fetch: this.config.fetch,
|
|
228
279
|
});
|
|
229
280
|
|
|
230
|
-
const content: Array<
|
|
281
|
+
const content: Array<LanguageModelV4Content> = [];
|
|
282
|
+
let hasFunctionCall = false;
|
|
231
283
|
|
|
232
284
|
const webSearchSubTools = [
|
|
233
285
|
'web_search',
|
|
@@ -350,6 +402,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
350
402
|
}
|
|
351
403
|
|
|
352
404
|
case 'function_call': {
|
|
405
|
+
hasFunctionCall = true;
|
|
353
406
|
content.push({
|
|
354
407
|
type: 'tool-call',
|
|
355
408
|
toolCallId: part.call_id,
|
|
@@ -360,16 +413,22 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
360
413
|
}
|
|
361
414
|
|
|
362
415
|
case 'reasoning': {
|
|
363
|
-
const
|
|
364
|
-
.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
416
|
+
const texts =
|
|
417
|
+
part.summary.length > 0
|
|
418
|
+
? part.summary.map(s => s.text)
|
|
419
|
+
: (part.content ?? []).map(c => c.text);
|
|
420
|
+
|
|
421
|
+
const reasoningText = texts
|
|
422
|
+
.filter(text => text && text.length > 0)
|
|
423
|
+
.join('');
|
|
424
|
+
|
|
425
|
+
// condition changed here since encrypted content can now come with empty reasoning text
|
|
426
|
+
if (reasoningText || part.encrypted_content) {
|
|
427
|
+
const hasMetadata = part.encrypted_content || part.id;
|
|
428
|
+
content.push({
|
|
429
|
+
type: 'reasoning',
|
|
430
|
+
text: reasoningText,
|
|
431
|
+
...(hasMetadata && {
|
|
373
432
|
providerMetadata: {
|
|
374
433
|
xai: {
|
|
375
434
|
...(part.encrypted_content && {
|
|
@@ -378,13 +437,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
378
437
|
...(part.id && { itemId: part.id }),
|
|
379
438
|
},
|
|
380
439
|
},
|
|
381
|
-
})
|
|
382
|
-
}
|
|
383
|
-
content.push({
|
|
384
|
-
type: 'reasoning',
|
|
385
|
-
text: reasoningText,
|
|
386
|
-
});
|
|
387
|
-
}
|
|
440
|
+
}),
|
|
441
|
+
});
|
|
388
442
|
}
|
|
389
443
|
break;
|
|
390
444
|
}
|
|
@@ -398,7 +452,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
398
452
|
return {
|
|
399
453
|
content,
|
|
400
454
|
finishReason: {
|
|
401
|
-
unified:
|
|
455
|
+
unified: hasFunctionCall
|
|
456
|
+
? 'tool-calls'
|
|
457
|
+
: mapXaiResponsesFinishReason(response.status),
|
|
402
458
|
raw: response.status ?? undefined,
|
|
403
459
|
},
|
|
404
460
|
usage: response.usage
|
|
@@ -407,6 +463,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
407
463
|
inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
|
|
408
464
|
outputTokens: { total: 0, text: 0, reasoning: 0 },
|
|
409
465
|
},
|
|
466
|
+
...(response.usage?.cost_in_usd_ticks != null && {
|
|
467
|
+
providerMetadata: {
|
|
468
|
+
xai: {
|
|
469
|
+
costInUsdTicks: response.usage.cost_in_usd_ticks,
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
}),
|
|
410
473
|
request: { body },
|
|
411
474
|
response: {
|
|
412
475
|
...getResponseMetadata(response),
|
|
@@ -418,8 +481,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
418
481
|
}
|
|
419
482
|
|
|
420
483
|
async doStream(
|
|
421
|
-
options:
|
|
422
|
-
): Promise<
|
|
484
|
+
options: LanguageModelV4CallOptions,
|
|
485
|
+
): Promise<LanguageModelV4StreamResult> {
|
|
423
486
|
const {
|
|
424
487
|
args,
|
|
425
488
|
warnings,
|
|
@@ -436,7 +499,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
436
499
|
|
|
437
500
|
const { responseHeaders, value: response } = await postJsonToApi({
|
|
438
501
|
url: `${this.config.baseURL ?? 'https://api.x.ai/v1'}/responses`,
|
|
439
|
-
headers: combineHeaders(this.config.headers(), options.headers),
|
|
502
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
440
503
|
body,
|
|
441
504
|
failedResponseHandler: xaiFailedResponseHandler,
|
|
442
505
|
successfulResponseHandler: createEventSourceResponseHandler(
|
|
@@ -446,11 +509,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
446
509
|
fetch: this.config.fetch,
|
|
447
510
|
});
|
|
448
511
|
|
|
449
|
-
let finishReason:
|
|
512
|
+
let finishReason: LanguageModelV4FinishReason = {
|
|
450
513
|
unified: 'other',
|
|
451
514
|
raw: undefined,
|
|
452
515
|
};
|
|
453
|
-
let
|
|
516
|
+
let hasFunctionCall = false;
|
|
517
|
+
let usage: LanguageModelV4Usage | undefined = undefined;
|
|
518
|
+
let costInUsdTicks: number | undefined = undefined;
|
|
454
519
|
let isFirstChunk = true;
|
|
455
520
|
const contentBlocks: Record<string, { type: 'text' }> = {};
|
|
456
521
|
const seenToolCalls = new Set<string>();
|
|
@@ -473,7 +538,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
473
538
|
stream: response.pipeThrough(
|
|
474
539
|
new TransformStream<
|
|
475
540
|
ParseResult<z.infer<typeof xaiResponsesChunkSchema>>,
|
|
476
|
-
|
|
541
|
+
LanguageModelV4StreamPart
|
|
477
542
|
>({
|
|
478
543
|
start(controller) {
|
|
479
544
|
controller.enqueue({ type: 'stream-start', warnings });
|
|
@@ -508,16 +573,18 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
508
573
|
if (event.type === 'response.reasoning_summary_part.added') {
|
|
509
574
|
const blockId = `reasoning-${event.item_id}`;
|
|
510
575
|
|
|
511
|
-
activeReasoning[event.item_id]
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
576
|
+
if (activeReasoning[event.item_id] == null) {
|
|
577
|
+
activeReasoning[event.item_id] = {};
|
|
578
|
+
controller.enqueue({
|
|
579
|
+
type: 'reasoning-start',
|
|
580
|
+
id: blockId,
|
|
581
|
+
providerMetadata: {
|
|
582
|
+
xai: {
|
|
583
|
+
itemId: event.item_id,
|
|
584
|
+
},
|
|
518
585
|
},
|
|
519
|
-
}
|
|
520
|
-
}
|
|
586
|
+
});
|
|
587
|
+
}
|
|
521
588
|
}
|
|
522
589
|
|
|
523
590
|
if (event.type === 'response.reasoning_summary_text.delta') {
|
|
@@ -633,17 +700,32 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
633
700
|
|
|
634
701
|
if (
|
|
635
702
|
event.type === 'response.done' ||
|
|
636
|
-
event.type === 'response.completed'
|
|
703
|
+
event.type === 'response.completed' ||
|
|
704
|
+
event.type === 'response.incomplete'
|
|
637
705
|
) {
|
|
638
706
|
const response = event.response;
|
|
639
707
|
|
|
640
708
|
if (response.usage) {
|
|
641
709
|
usage = convertXaiResponsesUsage(response.usage);
|
|
710
|
+
costInUsdTicks = response.usage.cost_in_usd_ticks ?? undefined;
|
|
642
711
|
}
|
|
643
712
|
|
|
644
|
-
if (response.
|
|
713
|
+
if (event.type === 'response.incomplete') {
|
|
714
|
+
const reason =
|
|
715
|
+
'incomplete_details' in response
|
|
716
|
+
? response.incomplete_details?.reason
|
|
717
|
+
: undefined;
|
|
718
|
+
finishReason = {
|
|
719
|
+
unified: reason
|
|
720
|
+
? mapXaiResponsesFinishReason(reason)
|
|
721
|
+
: 'other',
|
|
722
|
+
raw: reason ?? 'incomplete',
|
|
723
|
+
};
|
|
724
|
+
} else if ('status' in response && response.status) {
|
|
645
725
|
finishReason = {
|
|
646
|
-
unified:
|
|
726
|
+
unified: hasFunctionCall
|
|
727
|
+
? 'tool-calls'
|
|
728
|
+
: mapXaiResponsesFinishReason(response.status),
|
|
647
729
|
raw: response.status,
|
|
648
730
|
};
|
|
649
731
|
}
|
|
@@ -651,6 +733,25 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
651
733
|
return;
|
|
652
734
|
}
|
|
653
735
|
|
|
736
|
+
if (event.type === 'response.failed') {
|
|
737
|
+
const reason = event.response.incomplete_details?.reason;
|
|
738
|
+
finishReason = {
|
|
739
|
+
unified: reason ? mapXaiResponsesFinishReason(reason) : 'error',
|
|
740
|
+
raw: reason ?? 'error',
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
if (event.response.usage) {
|
|
744
|
+
usage = convertXaiResponsesUsage(event.response.usage);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (event.type === 'error') {
|
|
751
|
+
controller.enqueue({ type: 'error', error: event });
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
654
755
|
// Custom tool call input streaming - already handled by output_item events
|
|
655
756
|
if (
|
|
656
757
|
event.type === 'response.custom_tool_call_input.delta' ||
|
|
@@ -911,6 +1012,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
911
1012
|
toolName: part.name,
|
|
912
1013
|
});
|
|
913
1014
|
} else if (event.type === 'response.output_item.done') {
|
|
1015
|
+
hasFunctionCall = true;
|
|
914
1016
|
ongoingToolCalls[event.output_index] = undefined;
|
|
915
1017
|
|
|
916
1018
|
controller.enqueue({
|
|
@@ -951,6 +1053,13 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
|
|
|
951
1053
|
},
|
|
952
1054
|
outputTokens: { total: 0, text: 0, reasoning: 0 },
|
|
953
1055
|
},
|
|
1056
|
+
...(costInUsdTicks != null && {
|
|
1057
|
+
providerMetadata: {
|
|
1058
|
+
xai: {
|
|
1059
|
+
costInUsdTicks,
|
|
1060
|
+
},
|
|
1061
|
+
},
|
|
1062
|
+
}),
|
|
954
1063
|
});
|
|
955
1064
|
},
|
|
956
1065
|
}),
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
LanguageModelV3CallOptions,
|
|
3
|
-
SharedV3Warning,
|
|
4
2
|
UnsupportedFunctionalityError,
|
|
3
|
+
type LanguageModelV4CallOptions,
|
|
4
|
+
type SharedV4Warning,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
6
|
import { validateTypes } from '@ai-sdk/provider-utils';
|
|
7
|
+
import { removeAdditionalPropertiesFalse } from '../remove-additional-properties';
|
|
7
8
|
import { fileSearchArgsSchema } from '../tool/file-search';
|
|
8
9
|
import { mcpServerArgsSchema } from '../tool/mcp-server';
|
|
9
10
|
import { webSearchArgsSchema } from '../tool/web-search';
|
|
10
11
|
import { xSearchArgsSchema } from '../tool/x-search';
|
|
11
|
-
import { XaiResponsesTool } from './xai-responses-api';
|
|
12
|
+
import type { XaiResponsesTool } from './xai-responses-api';
|
|
12
13
|
|
|
13
14
|
type XaiResponsesToolChoice =
|
|
14
15
|
| 'auto'
|
|
@@ -20,16 +21,16 @@ export async function prepareResponsesTools({
|
|
|
20
21
|
tools,
|
|
21
22
|
toolChoice,
|
|
22
23
|
}: {
|
|
23
|
-
tools:
|
|
24
|
-
toolChoice?:
|
|
24
|
+
tools: LanguageModelV4CallOptions['tools'];
|
|
25
|
+
toolChoice?: LanguageModelV4CallOptions['toolChoice'];
|
|
25
26
|
}): Promise<{
|
|
26
27
|
tools: Array<XaiResponsesTool> | undefined;
|
|
27
28
|
toolChoice: XaiResponsesToolChoice | undefined;
|
|
28
|
-
toolWarnings:
|
|
29
|
+
toolWarnings: SharedV4Warning[];
|
|
29
30
|
}> {
|
|
30
31
|
const normalizedTools = tools?.length ? tools : undefined;
|
|
31
32
|
|
|
32
|
-
const toolWarnings:
|
|
33
|
+
const toolWarnings: SharedV4Warning[] = [];
|
|
33
34
|
|
|
34
35
|
if (normalizedTools == null) {
|
|
35
36
|
return { tools: undefined, toolChoice: undefined, toolWarnings };
|
|
@@ -53,6 +54,7 @@ export async function prepareResponsesTools({
|
|
|
53
54
|
type: 'web_search',
|
|
54
55
|
allowed_domains: args.allowedDomains,
|
|
55
56
|
excluded_domains: args.excludedDomains,
|
|
57
|
+
enable_image_search: args.enableImageSearch,
|
|
56
58
|
enable_image_understanding: args.enableImageUnderstanding,
|
|
57
59
|
});
|
|
58
60
|
break;
|
|
@@ -142,7 +144,7 @@ export async function prepareResponsesTools({
|
|
|
142
144
|
type: 'function',
|
|
143
145
|
name: tool.name,
|
|
144
146
|
description: tool.description,
|
|
145
|
-
parameters: tool.inputSchema,
|
|
147
|
+
parameters: removeAdditionalPropertiesFalse(tool.inputSchema),
|
|
146
148
|
...(tool.strict != null ? { strict: tool.strict } : {}),
|
|
147
149
|
});
|
|
148
150
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createProviderExecutedToolFactory } from '@ai-sdk/provider-utils';
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
3
|
|
|
4
4
|
const codeExecutionOutputSchema = z.object({
|
|
@@ -6,7 +6,7 @@ const codeExecutionOutputSchema = z.object({
|
|
|
6
6
|
error: z.string().optional().describe('any error that occurred'),
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
const codeExecutionToolFactory =
|
|
9
|
+
const codeExecutionToolFactory = createProviderExecutedToolFactory({
|
|
10
10
|
id: 'xai.code_execution',
|
|
11
11
|
inputSchema: z.object({}).describe('no input parameters'),
|
|
12
12
|
outputSchema: codeExecutionOutputSchema,
|
package/src/tool/file-search.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
createProviderExecutedToolFactory,
|
|
3
3
|
lazySchema,
|
|
4
4
|
zodSchema,
|
|
5
5
|
} from '@ai-sdk/provider-utils';
|
|
@@ -36,7 +36,7 @@ const fileSearchOutputSchema = lazySchema(() =>
|
|
|
36
36
|
),
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
const fileSearchToolFactory =
|
|
39
|
+
const fileSearchToolFactory = createProviderExecutedToolFactory<
|
|
40
40
|
{},
|
|
41
41
|
{
|
|
42
42
|
/**
|
package/src/tool/mcp-server.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
createProviderExecutedToolFactory,
|
|
3
3
|
lazySchema,
|
|
4
4
|
zodSchema,
|
|
5
5
|
} from '@ai-sdk/provider-utils';
|
|
@@ -41,7 +41,7 @@ const mcpServerOutputSchema = lazySchema(() =>
|
|
|
41
41
|
),
|
|
42
42
|
);
|
|
43
43
|
|
|
44
|
-
const mcpServerToolFactory =
|
|
44
|
+
const mcpServerToolFactory = createProviderExecutedToolFactory<
|
|
45
45
|
{},
|
|
46
46
|
{
|
|
47
47
|
name: string;
|
package/src/tool/view-image.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createProviderExecutedToolFactory } from '@ai-sdk/provider-utils';
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
3
|
|
|
4
4
|
const viewImageOutputSchema = z.object({
|
|
@@ -9,7 +9,7 @@ const viewImageOutputSchema = z.object({
|
|
|
9
9
|
.describe('objects detected in the image'),
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
const viewImageToolFactory =
|
|
12
|
+
const viewImageToolFactory = createProviderExecutedToolFactory({
|
|
13
13
|
id: 'xai.view_image',
|
|
14
14
|
inputSchema: z.object({}).describe('no input parameters'),
|
|
15
15
|
outputSchema: viewImageOutputSchema,
|