@ai-sdk/xai 4.0.43 → 4.0.47
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 +34 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1112 -633
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +60 -0
- package/package.json +6 -6
- package/src/responses/xai-responses-api.ts +22 -18
- package/src/responses/xai-responses-batch.ts +558 -0
- package/src/responses/xai-responses-language-model.ts +109 -3
- 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,
|
|
@@ -41,7 +42,7 @@ import {
|
|
|
41
42
|
} from './xai-responses-language-model-options';
|
|
42
43
|
import { prepareResponsesTools } from './xai-responses-prepare-tools';
|
|
43
44
|
|
|
44
|
-
type XaiResponsesConfig = {
|
|
45
|
+
export type XaiResponsesConfig = {
|
|
45
46
|
provider: string;
|
|
46
47
|
baseURL: string | undefined;
|
|
47
48
|
headers?: () => Record<string, string | undefined>;
|
|
@@ -49,6 +50,91 @@ type XaiResponsesConfig = {
|
|
|
49
50
|
fetch?: FetchFunction;
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
function createXaiResponsesStreamError({
|
|
54
|
+
message,
|
|
55
|
+
code,
|
|
56
|
+
eventType,
|
|
57
|
+
data,
|
|
58
|
+
}: {
|
|
59
|
+
message: string;
|
|
60
|
+
code?: string | null;
|
|
61
|
+
eventType: 'error' | 'response.failed';
|
|
62
|
+
data: unknown;
|
|
63
|
+
}) {
|
|
64
|
+
const statusCode = getHttpStatusCode(code);
|
|
65
|
+
|
|
66
|
+
return createProviderStreamError({
|
|
67
|
+
message,
|
|
68
|
+
type: eventType,
|
|
69
|
+
code: code ?? undefined,
|
|
70
|
+
...(statusCode != null
|
|
71
|
+
? {
|
|
72
|
+
statusCode,
|
|
73
|
+
isRetryable: isRetryableStatusCode(statusCode),
|
|
74
|
+
}
|
|
75
|
+
: getXaiResponsesStreamErrorMetadata(code)),
|
|
76
|
+
data,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getXaiResponsesStreamErrorMetadata(code?: string | null): {
|
|
81
|
+
statusCode?: number;
|
|
82
|
+
isRetryable?: boolean;
|
|
83
|
+
} {
|
|
84
|
+
switch (code) {
|
|
85
|
+
case 'rate_limit_exceeded':
|
|
86
|
+
case 'rate_limit_error':
|
|
87
|
+
return { statusCode: 429, isRetryable: true };
|
|
88
|
+
case 'insufficient_quota':
|
|
89
|
+
return { statusCode: 429, isRetryable: false };
|
|
90
|
+
case 'api_error':
|
|
91
|
+
case 'internal_server_error':
|
|
92
|
+
case 'server_error':
|
|
93
|
+
return { statusCode: 500, isRetryable: true };
|
|
94
|
+
case 'overloaded_error':
|
|
95
|
+
case 'service_unavailable':
|
|
96
|
+
return { statusCode: 503, isRetryable: true };
|
|
97
|
+
case 'timeout':
|
|
98
|
+
case 'timeout_error':
|
|
99
|
+
return { statusCode: 504, isRetryable: true };
|
|
100
|
+
case 'authentication_error':
|
|
101
|
+
case 'invalid_api_key':
|
|
102
|
+
return { statusCode: 401, isRetryable: false };
|
|
103
|
+
case 'permission_error':
|
|
104
|
+
return { statusCode: 403, isRetryable: false };
|
|
105
|
+
case 'not_found_error':
|
|
106
|
+
case 'model_not_found':
|
|
107
|
+
return { statusCode: 404, isRetryable: false };
|
|
108
|
+
case 'bad_request':
|
|
109
|
+
case 'context_length_exceeded':
|
|
110
|
+
case 'invalid_request_error':
|
|
111
|
+
return { statusCode: 400, isRetryable: false };
|
|
112
|
+
default:
|
|
113
|
+
return {};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getHttpStatusCode(value: unknown): number | undefined {
|
|
118
|
+
const statusCode =
|
|
119
|
+
typeof value === 'string' && /^\d{3}$/.test(value) ? Number(value) : value;
|
|
120
|
+
|
|
121
|
+
return typeof statusCode === 'number' &&
|
|
122
|
+
Number.isInteger(statusCode) &&
|
|
123
|
+
statusCode >= 400 &&
|
|
124
|
+
statusCode <= 599
|
|
125
|
+
? statusCode
|
|
126
|
+
: undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isRetryableStatusCode(statusCode: number): boolean {
|
|
130
|
+
return (
|
|
131
|
+
statusCode === 408 ||
|
|
132
|
+
statusCode === 409 ||
|
|
133
|
+
statusCode === 429 ||
|
|
134
|
+
statusCode >= 500
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
52
138
|
export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
53
139
|
readonly specificationVersion = 'v4';
|
|
54
140
|
|
|
@@ -88,7 +174,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
|
88
174
|
'text/*': [/^https?:\/\/.*$/],
|
|
89
175
|
};
|
|
90
176
|
|
|
91
|
-
|
|
177
|
+
protected async getArgs({
|
|
92
178
|
prompt,
|
|
93
179
|
maxOutputTokens,
|
|
94
180
|
temperature,
|
|
@@ -820,11 +906,31 @@ export class XaiResponsesLanguageModel implements LanguageModelV4 {
|
|
|
820
906
|
usage = convertXaiResponsesUsage(event.response.usage);
|
|
821
907
|
}
|
|
822
908
|
|
|
909
|
+
if (event.response.error != null) {
|
|
910
|
+
controller.enqueue({
|
|
911
|
+
type: 'error',
|
|
912
|
+
error: createXaiResponsesStreamError({
|
|
913
|
+
message: event.response.error.message,
|
|
914
|
+
code: event.response.error.code,
|
|
915
|
+
eventType: event.type,
|
|
916
|
+
data: event,
|
|
917
|
+
}),
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
|
|
823
921
|
return;
|
|
824
922
|
}
|
|
825
923
|
|
|
826
924
|
if (event.type === 'error') {
|
|
827
|
-
controller.enqueue({
|
|
925
|
+
controller.enqueue({
|
|
926
|
+
type: 'error',
|
|
927
|
+
error: createXaiResponsesStreamError({
|
|
928
|
+
message: event.message,
|
|
929
|
+
code: event.code,
|
|
930
|
+
eventType: event.type,
|
|
931
|
+
data: event,
|
|
932
|
+
}),
|
|
933
|
+
});
|
|
828
934
|
return;
|
|
829
935
|
}
|
|
830
936
|
|
|
@@ -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,
|