@ai-sdk/xai 4.0.44 → 4.0.48
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 +32 -0
- package/dist/index.d.ts +60 -20
- package/dist/index.js +1183 -635
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +60 -0
- package/package.json +2 -2
- package/src/responses/xai-responses-api.ts +47 -18
- package/src/responses/xai-responses-batch.ts +558 -0
- package/src/responses/xai-responses-language-model.ts +159 -4
- package/src/tool/web-search.ts +38 -15
- package/src/xai-chat-language-model.ts +29 -22
- package/src/xai-provider.ts +6 -5
- package/src/xai-video-model.ts +12 -1
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
combineHeaders,
|
|
14
14
|
createEventSourceResponseHandler,
|
|
15
15
|
createJsonResponseHandler,
|
|
16
|
+
createProviderStreamError,
|
|
16
17
|
isCustomReasoning,
|
|
17
18
|
mapReasoningToProviderEffort,
|
|
18
19
|
parseProviderOptions,
|
|
@@ -21,16 +22,20 @@ import {
|
|
|
21
22
|
WORKFLOW_SERIALIZE,
|
|
22
23
|
WORKFLOW_DESERIALIZE,
|
|
23
24
|
type FetchFunction,
|
|
25
|
+
type InferSchema,
|
|
24
26
|
type ParseResult,
|
|
25
27
|
} from '@ai-sdk/provider-utils';
|
|
26
28
|
import type { z } from 'zod/v4';
|
|
27
29
|
import { getResponseMetadata } from '../get-response-metadata';
|
|
28
30
|
import { supportsReasoningEffort } from '../supports-reasoning-effort';
|
|
31
|
+
import type { webSearchOutputSchema } from '../tool/web-search';
|
|
29
32
|
import { xaiFailedResponseHandler } from '../xai-error';
|
|
30
33
|
import { convertToXaiResponsesInput } from './convert-to-xai-responses-input';
|
|
31
34
|
import { convertXaiResponsesUsage } from './convert-xai-responses-usage';
|
|
32
35
|
import { mapXaiResponsesFinishReason } from './map-xai-responses-finish-reason';
|
|
33
36
|
import {
|
|
37
|
+
webSearchWireActionSchema,
|
|
38
|
+
webSearchWireSourceSchema,
|
|
34
39
|
xaiResponsesChunkSchema,
|
|
35
40
|
xaiResponsesResponseSchema,
|
|
36
41
|
type XaiResponsesIncludeOptions,
|
|
@@ -41,7 +46,7 @@ import {
|
|
|
41
46
|
} from './xai-responses-language-model-options';
|
|
42
47
|
import { prepareResponsesTools } from './xai-responses-prepare-tools';
|
|
43
48
|
|
|
44
|
-
type XaiResponsesConfig = {
|
|
49
|
+
export type XaiResponsesConfig = {
|
|
45
50
|
provider: string;
|
|
46
51
|
baseURL: string | undefined;
|
|
47
52
|
headers?: () => Record<string, string | undefined>;
|
|
@@ -49,6 +54,91 @@ type XaiResponsesConfig = {
|
|
|
49
54
|
fetch?: FetchFunction;
|
|
50
55
|
};
|
|
51
56
|
|
|
57
|
+
function createXaiResponsesStreamError({
|
|
58
|
+
message,
|
|
59
|
+
code,
|
|
60
|
+
eventType,
|
|
61
|
+
data,
|
|
62
|
+
}: {
|
|
63
|
+
message: string;
|
|
64
|
+
code?: string | null;
|
|
65
|
+
eventType: 'error' | 'response.failed';
|
|
66
|
+
data: unknown;
|
|
67
|
+
}) {
|
|
68
|
+
const statusCode = getHttpStatusCode(code);
|
|
69
|
+
|
|
70
|
+
return createProviderStreamError({
|
|
71
|
+
message,
|
|
72
|
+
type: eventType,
|
|
73
|
+
code: code ?? undefined,
|
|
74
|
+
...(statusCode != null
|
|
75
|
+
? {
|
|
76
|
+
statusCode,
|
|
77
|
+
isRetryable: isRetryableStatusCode(statusCode),
|
|
78
|
+
}
|
|
79
|
+
: getXaiResponsesStreamErrorMetadata(code)),
|
|
80
|
+
data,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getXaiResponsesStreamErrorMetadata(code?: string | null): {
|
|
85
|
+
statusCode?: number;
|
|
86
|
+
isRetryable?: boolean;
|
|
87
|
+
} {
|
|
88
|
+
switch (code) {
|
|
89
|
+
case 'rate_limit_exceeded':
|
|
90
|
+
case 'rate_limit_error':
|
|
91
|
+
return { statusCode: 429, isRetryable: true };
|
|
92
|
+
case 'insufficient_quota':
|
|
93
|
+
return { statusCode: 429, isRetryable: false };
|
|
94
|
+
case 'api_error':
|
|
95
|
+
case 'internal_server_error':
|
|
96
|
+
case 'server_error':
|
|
97
|
+
return { statusCode: 500, isRetryable: true };
|
|
98
|
+
case 'overloaded_error':
|
|
99
|
+
case 'service_unavailable':
|
|
100
|
+
return { statusCode: 503, isRetryable: true };
|
|
101
|
+
case 'timeout':
|
|
102
|
+
case 'timeout_error':
|
|
103
|
+
return { statusCode: 504, isRetryable: true };
|
|
104
|
+
case 'authentication_error':
|
|
105
|
+
case 'invalid_api_key':
|
|
106
|
+
return { statusCode: 401, isRetryable: false };
|
|
107
|
+
case 'permission_error':
|
|
108
|
+
return { statusCode: 403, isRetryable: false };
|
|
109
|
+
case 'not_found_error':
|
|
110
|
+
case 'model_not_found':
|
|
111
|
+
return { statusCode: 404, isRetryable: false };
|
|
112
|
+
case 'bad_request':
|
|
113
|
+
case 'context_length_exceeded':
|
|
114
|
+
case 'invalid_request_error':
|
|
115
|
+
return { statusCode: 400, isRetryable: false };
|
|
116
|
+
default:
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getHttpStatusCode(value: unknown): number | undefined {
|
|
122
|
+
const statusCode =
|
|
123
|
+
typeof value === 'string' && /^\d{3}$/.test(value) ? Number(value) : value;
|
|
124
|
+
|
|
125
|
+
return typeof statusCode === 'number' &&
|
|
126
|
+
Number.isInteger(statusCode) &&
|
|
127
|
+
statusCode >= 400 &&
|
|
128
|
+
statusCode <= 599
|
|
129
|
+
? statusCode
|
|
130
|
+
: undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function isRetryableStatusCode(statusCode: number): boolean {
|
|
134
|
+
return (
|
|
135
|
+
statusCode === 408 ||
|
|
136
|
+
statusCode === 409 ||
|
|
137
|
+
statusCode === 429 ||
|
|
138
|
+
statusCode >= 500
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
52
142
|
export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
53
143
|
readonly specificationVersion = 'v4';
|
|
54
144
|
|
|
@@ -88,7 +178,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
|
88
178
|
'text/*': [/^https?:\/\/.*$/],
|
|
89
179
|
};
|
|
90
180
|
|
|
91
|
-
|
|
181
|
+
protected async getArgs({
|
|
92
182
|
prompt,
|
|
93
183
|
maxOutputTokens,
|
|
94
184
|
temperature,
|
|
@@ -436,6 +526,15 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
|
436
526
|
providerExecuted: true,
|
|
437
527
|
});
|
|
438
528
|
|
|
529
|
+
if (part.type === 'web_search_call') {
|
|
530
|
+
content.push({
|
|
531
|
+
type: 'tool-result',
|
|
532
|
+
toolCallId: part.id,
|
|
533
|
+
toolName,
|
|
534
|
+
result: mapWebSearchAction(part.action),
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
439
538
|
continue;
|
|
440
539
|
}
|
|
441
540
|
|
|
@@ -820,11 +919,31 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
|
820
919
|
usage = convertXaiResponsesUsage(event.response.usage);
|
|
821
920
|
}
|
|
822
921
|
|
|
922
|
+
if (event.response.error != null) {
|
|
923
|
+
controller.enqueue({
|
|
924
|
+
type: 'error',
|
|
925
|
+
error: createXaiResponsesStreamError({
|
|
926
|
+
message: event.response.error.message,
|
|
927
|
+
code: event.response.error.code,
|
|
928
|
+
eventType: event.type,
|
|
929
|
+
data: event,
|
|
930
|
+
}),
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
|
|
823
934
|
return;
|
|
824
935
|
}
|
|
825
936
|
|
|
826
937
|
if (event.type === 'error') {
|
|
827
|
-
controller.enqueue({
|
|
938
|
+
controller.enqueue({
|
|
939
|
+
type: 'error',
|
|
940
|
+
error: createXaiResponsesStreamError({
|
|
941
|
+
message: event.message,
|
|
942
|
+
code: event.code,
|
|
943
|
+
eventType: event.type,
|
|
944
|
+
data: event,
|
|
945
|
+
}),
|
|
946
|
+
});
|
|
828
947
|
return;
|
|
829
948
|
}
|
|
830
949
|
|
|
@@ -1133,7 +1252,10 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1133
1252
|
type: 'tool-result',
|
|
1134
1253
|
toolCallId: part.id,
|
|
1135
1254
|
toolName,
|
|
1136
|
-
result:
|
|
1255
|
+
result:
|
|
1256
|
+
part.type === 'web_search_call'
|
|
1257
|
+
? mapWebSearchAction(part.action)
|
|
1258
|
+
: {},
|
|
1137
1259
|
});
|
|
1138
1260
|
}
|
|
1139
1261
|
|
|
@@ -1251,3 +1373,36 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1251
1373
|
};
|
|
1252
1374
|
}
|
|
1253
1375
|
}
|
|
1376
|
+
|
|
1377
|
+
function mapWebSearchAction(
|
|
1378
|
+
action: unknown,
|
|
1379
|
+
): InferSchema<typeof webSearchOutputSchema> {
|
|
1380
|
+
const parsed = webSearchWireActionSchema.safeParse(action);
|
|
1381
|
+
if (!parsed.success) return {};
|
|
1382
|
+
|
|
1383
|
+
const a = parsed.data;
|
|
1384
|
+
const sources = a.sources?.flatMap(s => {
|
|
1385
|
+
const source = webSearchWireSourceSchema.safeParse(s);
|
|
1386
|
+
return source.success ? [source.data] : [];
|
|
1387
|
+
});
|
|
1388
|
+
const sourcesExtra = sources != null && sources.length > 0 ? { sources } : {};
|
|
1389
|
+
|
|
1390
|
+
switch (a.type) {
|
|
1391
|
+
case 'search':
|
|
1392
|
+
return {
|
|
1393
|
+
action: {
|
|
1394
|
+
type: 'search',
|
|
1395
|
+
...(a.query != null && { query: a.query }),
|
|
1396
|
+
...(a.queries != null && { queries: a.queries }),
|
|
1397
|
+
},
|
|
1398
|
+
...sourcesExtra,
|
|
1399
|
+
};
|
|
1400
|
+
case 'open_page':
|
|
1401
|
+
return { action: { type: 'openPage', url: a.url }, ...sourcesExtra };
|
|
1402
|
+
case 'find_in_page':
|
|
1403
|
+
return {
|
|
1404
|
+
action: { type: 'findInPage', url: a.url, pattern: a.pattern },
|
|
1405
|
+
...sourcesExtra,
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
}
|
package/src/tool/web-search.ts
CHANGED
|
@@ -16,17 +16,30 @@ export const webSearchArgsSchema = lazySchema(() =>
|
|
|
16
16
|
),
|
|
17
17
|
);
|
|
18
18
|
|
|
19
|
-
const webSearchOutputSchema = lazySchema(() =>
|
|
19
|
+
export const webSearchOutputSchema = lazySchema(() =>
|
|
20
20
|
zodSchema(
|
|
21
21
|
z.object({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
action: z
|
|
23
|
+
.discriminatedUnion('type', [
|
|
24
|
+
z.object({
|
|
25
|
+
type: z.literal('search'),
|
|
26
|
+
query: z.string().optional(),
|
|
27
|
+
queries: z.array(z.string()).optional(),
|
|
28
|
+
}),
|
|
29
|
+
z.object({
|
|
30
|
+
type: z.literal('openPage'),
|
|
31
|
+
url: z.string().nullish(),
|
|
32
|
+
}),
|
|
33
|
+
z.object({
|
|
34
|
+
type: z.literal('findInPage'),
|
|
35
|
+
url: z.string().nullish(),
|
|
36
|
+
pattern: z.string().nullish(),
|
|
37
|
+
}),
|
|
38
|
+
])
|
|
39
|
+
.optional(),
|
|
40
|
+
sources: z
|
|
41
|
+
.array(z.object({ type: z.literal('url'), url: z.string() }))
|
|
42
|
+
.optional(),
|
|
30
43
|
}),
|
|
31
44
|
),
|
|
32
45
|
);
|
|
@@ -34,12 +47,22 @@ const webSearchOutputSchema = lazySchema(() =>
|
|
|
34
47
|
const webSearchToolFactory = createProviderExecutedToolFactory<
|
|
35
48
|
{},
|
|
36
49
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
action?:
|
|
51
|
+
| {
|
|
52
|
+
type: 'search';
|
|
53
|
+
query?: string;
|
|
54
|
+
queries?: string[];
|
|
55
|
+
}
|
|
56
|
+
| {
|
|
57
|
+
type: 'openPage';
|
|
58
|
+
url?: string | null;
|
|
59
|
+
}
|
|
60
|
+
| {
|
|
61
|
+
type: 'findInPage';
|
|
62
|
+
url?: string | null;
|
|
63
|
+
pattern?: string | null;
|
|
64
|
+
};
|
|
65
|
+
sources?: Array<{ type: 'url'; url: string }>;
|
|
43
66
|
},
|
|
44
67
|
{
|
|
45
68
|
allowedDomains?: string[];
|
|
@@ -672,31 +672,36 @@ export class XaiChatLanguageModel implements LanguageModelV4 {
|
|
|
672
672
|
}
|
|
673
673
|
|
|
674
674
|
// XAI API Response Schemas
|
|
675
|
-
const xaiUsageSchema = z
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
675
|
+
const xaiUsageSchema = z
|
|
676
|
+
.object({
|
|
677
|
+
prompt_tokens: z.number(),
|
|
678
|
+
completion_tokens: z.number(),
|
|
679
|
+
total_tokens: z.number(),
|
|
680
|
+
cost_in_usd_ticks: z.number().nullish(),
|
|
681
|
+
prompt_tokens_details: z
|
|
682
|
+
.object({
|
|
683
|
+
text_tokens: z.number().nullish(),
|
|
684
|
+
audio_tokens: z.number().nullish(),
|
|
685
|
+
image_tokens: z.number().nullish(),
|
|
686
|
+
cached_tokens: z.number().nullish(),
|
|
687
|
+
})
|
|
688
|
+
.catchall(z.json())
|
|
689
|
+
.nullish(),
|
|
690
|
+
completion_tokens_details: z
|
|
691
|
+
.object({
|
|
692
|
+
reasoning_tokens: z.number().nullish(),
|
|
693
|
+
audio_tokens: z.number().nullish(),
|
|
694
|
+
accepted_prediction_tokens: z.number().nullish(),
|
|
695
|
+
rejected_prediction_tokens: z.number().nullish(),
|
|
696
|
+
})
|
|
697
|
+
.catchall(z.json())
|
|
698
|
+
.nullish(),
|
|
699
|
+
})
|
|
700
|
+
.catchall(z.json());
|
|
696
701
|
|
|
697
702
|
export type XaiChatUsage = z.infer<typeof xaiUsageSchema>;
|
|
698
703
|
|
|
699
|
-
const xaiChatResponseSchema = z.object({
|
|
704
|
+
export const xaiChatResponseSchema = z.object({
|
|
700
705
|
id: z.string().nullish(),
|
|
701
706
|
created: z.number().nullish(),
|
|
702
707
|
model: z.string().nullish(),
|
|
@@ -733,6 +738,8 @@ const xaiChatResponseSchema = z.object({
|
|
|
733
738
|
error: z.string().nullish(),
|
|
734
739
|
});
|
|
735
740
|
|
|
741
|
+
export type XaiChatResponse = z.infer<typeof xaiChatResponseSchema>;
|
|
742
|
+
|
|
736
743
|
const xaiChatChunkSchema = z.object({
|
|
737
744
|
id: z.string().nullish(),
|
|
738
745
|
created: z.number().nullish(),
|
package/src/xai-provider.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
type Experimental_RealtimeFactoryV4 as RealtimeFactoryV4,
|
|
3
3
|
type Experimental_RealtimeFactoryV4GetTokenOptions as RealtimeFactoryV4GetTokenOptions,
|
|
4
4
|
type Experimental_VideoModelV4,
|
|
5
|
+
type Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
|
|
5
6
|
type FilesV4,
|
|
6
7
|
type ImageModelV4,
|
|
7
8
|
type LanguageModelV4,
|
|
@@ -22,7 +23,7 @@ import { XaiChatLanguageModel } from './xai-chat-language-model';
|
|
|
22
23
|
import type { XaiChatModelId } from './xai-chat-language-model-options';
|
|
23
24
|
import { XaiImageModel } from './xai-image-model';
|
|
24
25
|
import type { XaiImageModelId } from './xai-image-settings';
|
|
25
|
-
import {
|
|
26
|
+
import { XaiResponsesBatchLanguageModel } from './responses/xai-responses-batch';
|
|
26
27
|
import type { XaiResponsesModelId } from './responses/xai-responses-language-model-options';
|
|
27
28
|
import { XaiRealtimeModel } from './realtime/xai-realtime-model';
|
|
28
29
|
import { xaiTools } from './tool';
|
|
@@ -34,12 +35,12 @@ import { XaiSpeechModel } from './xai-speech-model';
|
|
|
34
35
|
import { XaiTranscriptionModel } from './xai-transcription-model';
|
|
35
36
|
|
|
36
37
|
export interface XaiProvider extends ProviderV4 {
|
|
37
|
-
(modelId: XaiResponsesModelId):
|
|
38
|
+
(modelId: XaiResponsesModelId): BatchLanguageModelV4;
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Creates an Xai language model for text generation.
|
|
41
42
|
*/
|
|
42
|
-
languageModel(modelId: XaiResponsesModelId):
|
|
43
|
+
languageModel(modelId: XaiResponsesModelId): BatchLanguageModelV4;
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Creates an Xai chat model for text generation.
|
|
@@ -49,7 +50,7 @@ export interface XaiProvider extends ProviderV4 {
|
|
|
49
50
|
/**
|
|
50
51
|
* Creates an Xai responses model for text generation.
|
|
51
52
|
*/
|
|
52
|
-
responses: (modelId: XaiResponsesModelId) =>
|
|
53
|
+
responses: (modelId: XaiResponsesModelId) => BatchLanguageModelV4;
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Creates an Xai image model for image generation.
|
|
@@ -166,7 +167,7 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
|
|
|
166
167
|
};
|
|
167
168
|
|
|
168
169
|
const createResponsesLanguageModel = (modelId: XaiResponsesModelId) => {
|
|
169
|
-
return new
|
|
170
|
+
return new XaiResponsesBatchLanguageModel(modelId, {
|
|
170
171
|
provider: 'xai.responses',
|
|
171
172
|
baseURL,
|
|
172
173
|
headers: getHeaders,
|
package/src/xai-video-model.ts
CHANGED
|
@@ -39,6 +39,17 @@ interface XaiVideoModelConfig {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function encodePathSegment(value: string): string {
|
|
43
|
+
const encodedValue = encodeURIComponent(value);
|
|
44
|
+
|
|
45
|
+
// URL parsing normalizes both literal and percent-encoded dot segments.
|
|
46
|
+
return encodedValue === '.'
|
|
47
|
+
? '%252E'
|
|
48
|
+
: encodedValue === '..'
|
|
49
|
+
? '%252E%252E'
|
|
50
|
+
: encodedValue;
|
|
51
|
+
}
|
|
52
|
+
|
|
42
53
|
const RESOLUTION_MAP: Record<string, string> = {
|
|
43
54
|
'1920x1080': '1080p',
|
|
44
55
|
'1280x720': '720p',
|
|
@@ -538,7 +549,7 @@ export class XaiVideoModel implements VideoModelV4 {
|
|
|
538
549
|
const baseURL = this.config.baseURL ?? 'https://api.x.ai/v1';
|
|
539
550
|
|
|
540
551
|
const { value: statusResponse, responseHeaders } = await getFromApi({
|
|
541
|
-
url: `${baseURL}/videos/${requestId}`,
|
|
552
|
+
url: `${baseURL}/videos/${encodePathSegment(requestId)}`,
|
|
542
553
|
validateUrl: false,
|
|
543
554
|
headers: combineHeaders(this.config.headers(), options.headers),
|
|
544
555
|
successfulResponseHandler: xaiVideoStatusResponseHandler,
|