@ai-sdk/xai 4.0.0-beta.4 → 4.0.0-beta.40
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 +311 -8
- package/README.md +2 -0
- package/dist/index.d.ts +126 -50
- package/dist/index.js +1169 -749
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +173 -381
- package/package.json +8 -10
- package/src/convert-to-xai-chat-messages.ts +22 -6
- package/src/convert-xai-chat-usage.ts +2 -2
- package/src/files/xai-files-api.ts +16 -0
- package/src/files/xai-files-options.ts +15 -0
- package/src/files/xai-files.ts +93 -0
- package/src/index.ts +1 -0
- package/src/map-xai-finish-reason.ts +2 -2
- package/src/responses/convert-to-xai-responses-input.ts +63 -8
- package/src/responses/convert-xai-responses-usage.ts +2 -2
- package/src/responses/map-xai-responses-finish-reason.ts +3 -2
- package/src/responses/xai-responses-api.ts +31 -1
- package/src/responses/xai-responses-language-model.ts +134 -48
- package/src/responses/xai-responses-options.ts +6 -0
- package/src/responses/xai-responses-prepare-tools.ts +6 -6
- package/src/xai-chat-language-model.ts +61 -25
- package/src/xai-chat-options.ts +3 -6
- package/src/xai-chat-prompt.ts +2 -1
- package/src/xai-image-model.ts +25 -8
- package/src/xai-image-settings.ts +0 -2
- package/src/xai-prepare-tools.ts +6 -6
- package/src/xai-provider.ts +34 -21
- package/src/xai-video-model.ts +127 -20
- package/src/xai-video-options.ts +136 -14
- package/dist/index.d.mts +0 -375
- package/dist/index.mjs +0 -3061
- package/dist/index.mjs.map +0 -1
package/src/xai-image-model.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ImageModelV4, SharedV4Warning } from '@ai-sdk/provider';
|
|
2
2
|
import {
|
|
3
3
|
combineHeaders,
|
|
4
4
|
convertImageModelFileToDataUri,
|
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
getFromApi,
|
|
10
10
|
parseProviderOptions,
|
|
11
11
|
postJsonToApi,
|
|
12
|
+
serializeModelOptions,
|
|
13
|
+
WORKFLOW_SERIALIZE,
|
|
14
|
+
WORKFLOW_DESERIALIZE,
|
|
12
15
|
} from '@ai-sdk/provider-utils';
|
|
13
16
|
import { z } from 'zod/v4';
|
|
14
17
|
import { xaiFailedResponseHandler } from './xai-error';
|
|
@@ -18,21 +21,35 @@ import { XaiImageModelId } from './xai-image-settings';
|
|
|
18
21
|
interface XaiImageModelConfig {
|
|
19
22
|
provider: string;
|
|
20
23
|
baseURL: string | undefined;
|
|
21
|
-
headers
|
|
24
|
+
headers?: () => Record<string, string | undefined>;
|
|
22
25
|
fetch?: FetchFunction;
|
|
23
26
|
_internal?: {
|
|
24
27
|
currentDate?: () => Date;
|
|
25
28
|
};
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export class XaiImageModel implements
|
|
29
|
-
readonly specificationVersion = '
|
|
31
|
+
export class XaiImageModel implements ImageModelV4 {
|
|
32
|
+
readonly specificationVersion = 'v4';
|
|
30
33
|
readonly maxImagesPerCall = 3;
|
|
31
34
|
|
|
32
35
|
get provider(): string {
|
|
33
36
|
return this.config.provider;
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
static [WORKFLOW_SERIALIZE](model: XaiImageModel) {
|
|
40
|
+
return serializeModelOptions({
|
|
41
|
+
modelId: model.modelId,
|
|
42
|
+
config: model.config,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
47
|
+
modelId: XaiImageModelId;
|
|
48
|
+
config: XaiImageModelConfig;
|
|
49
|
+
}) {
|
|
50
|
+
return new XaiImageModel(options.modelId, options.config);
|
|
51
|
+
}
|
|
52
|
+
|
|
36
53
|
constructor(
|
|
37
54
|
readonly modelId: XaiImageModelId,
|
|
38
55
|
private config: XaiImageModelConfig,
|
|
@@ -49,10 +66,10 @@ export class XaiImageModel implements ImageModelV3 {
|
|
|
49
66
|
abortSignal,
|
|
50
67
|
files,
|
|
51
68
|
mask,
|
|
52
|
-
}: Parameters<
|
|
53
|
-
Awaited<ReturnType<
|
|
69
|
+
}: Parameters<ImageModelV4['doGenerate']>[0]): Promise<
|
|
70
|
+
Awaited<ReturnType<ImageModelV4['doGenerate']>>
|
|
54
71
|
> {
|
|
55
|
-
const warnings: Array<
|
|
72
|
+
const warnings: Array<SharedV4Warning> = [];
|
|
56
73
|
|
|
57
74
|
if (size != null) {
|
|
58
75
|
warnings.push({
|
|
@@ -135,7 +152,7 @@ export class XaiImageModel implements ImageModelV3 {
|
|
|
135
152
|
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
136
153
|
const { value: response, responseHeaders } = await postJsonToApi({
|
|
137
154
|
url: `${baseURL}${endpoint}`,
|
|
138
|
-
headers: combineHeaders(this.config.headers(), headers),
|
|
155
|
+
headers: combineHeaders(this.config.headers?.(), headers),
|
|
139
156
|
body,
|
|
140
157
|
failedResponseHandler: xaiFailedResponseHandler,
|
|
141
158
|
successfulResponseHandler: createJsonResponseHandler(
|
package/src/xai-prepare-tools.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
LanguageModelV4CallOptions,
|
|
3
|
+
SharedV4Warning,
|
|
4
4
|
UnsupportedFunctionalityError,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
6
|
import { XaiToolChoice } from './xai-chat-prompt';
|
|
@@ -9,8 +9,8 @@ export function prepareTools({
|
|
|
9
9
|
tools,
|
|
10
10
|
toolChoice,
|
|
11
11
|
}: {
|
|
12
|
-
tools:
|
|
13
|
-
toolChoice?:
|
|
12
|
+
tools: LanguageModelV4CallOptions['tools'];
|
|
13
|
+
toolChoice?: LanguageModelV4CallOptions['toolChoice'];
|
|
14
14
|
}): {
|
|
15
15
|
tools:
|
|
16
16
|
| Array<{
|
|
@@ -24,12 +24,12 @@ export function prepareTools({
|
|
|
24
24
|
}>
|
|
25
25
|
| undefined;
|
|
26
26
|
toolChoice: XaiToolChoice | undefined;
|
|
27
|
-
toolWarnings:
|
|
27
|
+
toolWarnings: SharedV4Warning[];
|
|
28
28
|
} {
|
|
29
29
|
// when the tools array is empty, change it to undefined to prevent errors
|
|
30
30
|
tools = tools?.length ? tools : undefined;
|
|
31
31
|
|
|
32
|
-
const toolWarnings:
|
|
32
|
+
const toolWarnings: SharedV4Warning[] = [];
|
|
33
33
|
|
|
34
34
|
if (tools == null) {
|
|
35
35
|
return { tools: undefined, toolChoice: undefined, toolWarnings };
|
package/src/xai-provider.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
type Experimental_VideoModelV4,
|
|
3
|
+
FilesV4,
|
|
4
|
+
ImageModelV4,
|
|
5
|
+
LanguageModelV4,
|
|
5
6
|
NoSuchModelError,
|
|
6
|
-
|
|
7
|
+
ProviderV4,
|
|
7
8
|
} from '@ai-sdk/provider';
|
|
8
9
|
import {
|
|
9
10
|
FetchFunction,
|
|
@@ -20,49 +21,52 @@ import { XaiResponsesLanguageModel } from './responses/xai-responses-language-mo
|
|
|
20
21
|
import { XaiResponsesModelId } from './responses/xai-responses-options';
|
|
21
22
|
import { xaiTools } from './tool';
|
|
22
23
|
import { VERSION } from './version';
|
|
24
|
+
import { XaiFiles } from './files/xai-files';
|
|
23
25
|
import { XaiVideoModel } from './xai-video-model';
|
|
24
26
|
import { XaiVideoModelId } from './xai-video-settings';
|
|
25
27
|
|
|
26
|
-
export interface XaiProvider extends
|
|
27
|
-
|
|
28
|
-
* Creates an Xai chat model for text generation.
|
|
29
|
-
*/
|
|
30
|
-
(modelId: XaiChatModelId): LanguageModelV3;
|
|
28
|
+
export interface XaiProvider extends ProviderV4 {
|
|
29
|
+
(modelId: XaiResponsesModelId): LanguageModelV4;
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
32
|
* Creates an Xai language model for text generation.
|
|
34
33
|
*/
|
|
35
|
-
languageModel(modelId:
|
|
34
|
+
languageModel(modelId: XaiResponsesModelId): LanguageModelV4;
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
37
|
* Creates an Xai chat model for text generation.
|
|
39
38
|
*/
|
|
40
|
-
chat: (modelId: XaiChatModelId) =>
|
|
39
|
+
chat: (modelId: XaiChatModelId) => LanguageModelV4;
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
|
-
* Creates an Xai responses model for
|
|
42
|
+
* Creates an Xai responses model for text generation.
|
|
44
43
|
*/
|
|
45
|
-
responses: (modelId: XaiResponsesModelId) =>
|
|
44
|
+
responses: (modelId: XaiResponsesModelId) => LanguageModelV4;
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
47
|
* Creates an Xai image model for image generation.
|
|
49
48
|
*/
|
|
50
|
-
image(modelId: XaiImageModelId):
|
|
49
|
+
image(modelId: XaiImageModelId): ImageModelV4;
|
|
51
50
|
|
|
52
51
|
/**
|
|
53
52
|
* Creates an Xai image model for image generation.
|
|
54
53
|
*/
|
|
55
|
-
imageModel(modelId: XaiImageModelId):
|
|
54
|
+
imageModel(modelId: XaiImageModelId): ImageModelV4;
|
|
56
55
|
|
|
57
56
|
/**
|
|
58
57
|
* Creates an Xai video model for video generation.
|
|
59
58
|
*/
|
|
60
|
-
video(modelId: XaiVideoModelId):
|
|
59
|
+
video(modelId: XaiVideoModelId): Experimental_VideoModelV4;
|
|
61
60
|
|
|
62
61
|
/**
|
|
63
62
|
* Creates an Xai video model for video generation.
|
|
64
63
|
*/
|
|
65
|
-
videoModel(modelId: XaiVideoModelId):
|
|
64
|
+
videoModel(modelId: XaiVideoModelId): Experimental_VideoModelV4;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Returns the xAI files interface for uploading files.
|
|
68
|
+
*/
|
|
69
|
+
files(): FilesV4;
|
|
66
70
|
|
|
67
71
|
/**
|
|
68
72
|
* Server-side agentic tools for use with the responses API.
|
|
@@ -153,11 +157,19 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
|
|
|
153
157
|
});
|
|
154
158
|
};
|
|
155
159
|
|
|
156
|
-
const
|
|
157
|
-
|
|
160
|
+
const createFiles = () =>
|
|
161
|
+
new XaiFiles({
|
|
162
|
+
provider: 'xai.files',
|
|
163
|
+
baseURL,
|
|
164
|
+
headers: getHeaders,
|
|
165
|
+
fetch: options.fetch,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const provider = (modelId: XaiResponsesModelId) =>
|
|
169
|
+
createResponsesLanguageModel(modelId);
|
|
158
170
|
|
|
159
|
-
provider.specificationVersion = '
|
|
160
|
-
provider.languageModel =
|
|
171
|
+
provider.specificationVersion = 'v4' as const;
|
|
172
|
+
provider.languageModel = createResponsesLanguageModel;
|
|
161
173
|
provider.chat = createChatLanguageModel;
|
|
162
174
|
provider.responses = createResponsesLanguageModel;
|
|
163
175
|
provider.embeddingModel = (modelId: string) => {
|
|
@@ -168,6 +180,7 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
|
|
|
168
180
|
provider.image = createImageModel;
|
|
169
181
|
provider.videoModel = createVideoModel;
|
|
170
182
|
provider.video = createVideoModel;
|
|
183
|
+
provider.files = createFiles;
|
|
171
184
|
provider.tools = xaiTools;
|
|
172
185
|
|
|
173
186
|
return provider;
|
package/src/xai-video-model.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AISDKError,
|
|
3
|
-
type
|
|
4
|
-
type
|
|
3
|
+
type Experimental_VideoModelV4,
|
|
4
|
+
type SharedV4Warning,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
6
|
import {
|
|
7
7
|
combineHeaders,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { z } from 'zod/v4';
|
|
17
17
|
import { xaiFailedResponseHandler } from './xai-error';
|
|
18
18
|
import {
|
|
19
|
-
type
|
|
19
|
+
type XaiParsedVideoModelOptions,
|
|
20
20
|
xaiVideoModelOptionsSchema,
|
|
21
21
|
} from './xai-video-options';
|
|
22
22
|
import type { XaiVideoModelId } from './xai-video-settings';
|
|
@@ -37,8 +37,29 @@ const RESOLUTION_MAP: Record<string, string> = {
|
|
|
37
37
|
'640x480': '480p',
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
function resolveVideoMode(
|
|
41
|
+
options: XaiParsedVideoModelOptions | undefined,
|
|
42
|
+
): XaiParsedVideoModelOptions['mode'] | undefined {
|
|
43
|
+
if (options?.mode != null) {
|
|
44
|
+
return options.mode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (options?.videoUrl != null) {
|
|
48
|
+
return 'edit-video';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
options?.referenceImageUrls != null &&
|
|
53
|
+
options.referenceImageUrls.length > 0
|
|
54
|
+
) {
|
|
55
|
+
return 'reference-to-video';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
62
|
+
readonly specificationVersion = 'v4';
|
|
42
63
|
readonly maxVideosPerCall = 1;
|
|
43
64
|
|
|
44
65
|
get provider(): string {
|
|
@@ -51,18 +72,22 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
51
72
|
) {}
|
|
52
73
|
|
|
53
74
|
async doGenerate(
|
|
54
|
-
options: Parameters<
|
|
55
|
-
): Promise<Awaited<ReturnType<
|
|
75
|
+
options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
|
|
76
|
+
): Promise<Awaited<ReturnType<Experimental_VideoModelV4['doGenerate']>>> {
|
|
56
77
|
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
57
|
-
const warnings:
|
|
78
|
+
const warnings: SharedV4Warning[] = [];
|
|
58
79
|
|
|
59
80
|
const xaiOptions = (await parseProviderOptions({
|
|
60
81
|
provider: 'xai',
|
|
61
82
|
providerOptions: options.providerOptions,
|
|
62
83
|
schema: xaiVideoModelOptionsSchema,
|
|
63
|
-
})) as
|
|
84
|
+
})) as XaiParsedVideoModelOptions | undefined;
|
|
85
|
+
|
|
86
|
+
const effectiveMode = resolveVideoMode(xaiOptions);
|
|
64
87
|
|
|
65
|
-
const isEdit =
|
|
88
|
+
const isEdit = effectiveMode === 'edit-video';
|
|
89
|
+
const isExtension = effectiveMode === 'extend-video';
|
|
90
|
+
const hasReferenceImages = effectiveMode === 'reference-to-video';
|
|
66
91
|
|
|
67
92
|
if (options.fps != null) {
|
|
68
93
|
warnings.push({
|
|
@@ -90,6 +115,7 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
90
115
|
});
|
|
91
116
|
}
|
|
92
117
|
|
|
118
|
+
// Edit mode: duration, aspectRatio, resolution not supported
|
|
93
119
|
if (isEdit && options.duration != null) {
|
|
94
120
|
warnings.push({
|
|
95
121
|
type: 'unsupported',
|
|
@@ -117,22 +143,46 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
117
143
|
});
|
|
118
144
|
}
|
|
119
145
|
|
|
146
|
+
// Extension mode: aspectRatio and resolution not supported
|
|
147
|
+
if (isExtension && options.aspectRatio != null) {
|
|
148
|
+
warnings.push({
|
|
149
|
+
type: 'unsupported',
|
|
150
|
+
feature: 'aspectRatio',
|
|
151
|
+
details: 'xAI video extension does not support custom aspect ratio.',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (
|
|
156
|
+
isExtension &&
|
|
157
|
+
(xaiOptions?.resolution != null || options.resolution != null)
|
|
158
|
+
) {
|
|
159
|
+
warnings.push({
|
|
160
|
+
type: 'unsupported',
|
|
161
|
+
feature: 'resolution',
|
|
162
|
+
details: 'xAI video extension does not support custom resolution.',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
120
166
|
const body: Record<string, unknown> = {
|
|
121
167
|
model: this.modelId,
|
|
122
168
|
prompt: options.prompt,
|
|
123
169
|
};
|
|
124
170
|
|
|
125
|
-
|
|
171
|
+
const allowDuration = !isEdit;
|
|
172
|
+
const allowAspectRatio = !isEdit && !isExtension;
|
|
173
|
+
const allowResolution = !isEdit && !isExtension;
|
|
174
|
+
|
|
175
|
+
if (allowDuration && options.duration != null) {
|
|
126
176
|
body.duration = options.duration;
|
|
127
177
|
}
|
|
128
178
|
|
|
129
|
-
if (
|
|
179
|
+
if (allowAspectRatio && options.aspectRatio != null) {
|
|
130
180
|
body.aspect_ratio = options.aspectRatio;
|
|
131
181
|
}
|
|
132
182
|
|
|
133
|
-
if (
|
|
183
|
+
if (allowResolution && xaiOptions?.resolution != null) {
|
|
134
184
|
body.resolution = xaiOptions.resolution;
|
|
135
|
-
} else if (
|
|
185
|
+
} else if (allowResolution && options.resolution != null) {
|
|
136
186
|
const mapped = RESOLUTION_MAP[options.resolution];
|
|
137
187
|
if (mapped != null) {
|
|
138
188
|
body.resolution = mapped;
|
|
@@ -147,12 +197,17 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
147
197
|
}
|
|
148
198
|
}
|
|
149
199
|
|
|
150
|
-
// Video editing: pass source video URL (nested object
|
|
151
|
-
if (
|
|
152
|
-
body.video = { url: xaiOptions
|
|
200
|
+
// Video editing: pass source video URL (nested object)
|
|
201
|
+
if (isEdit) {
|
|
202
|
+
body.video = { url: xaiOptions!.videoUrl };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Video extension: pass source video URL (nested object)
|
|
206
|
+
if (isExtension) {
|
|
207
|
+
body.video = { url: xaiOptions!.videoUrl };
|
|
153
208
|
}
|
|
154
209
|
|
|
155
|
-
//
|
|
210
|
+
// Convert SDK image input to the nested xAI request image object
|
|
156
211
|
if (options.image != null) {
|
|
157
212
|
if (options.image.type === 'url') {
|
|
158
213
|
body.image = { url: options.image.url };
|
|
@@ -167,14 +222,23 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
167
222
|
}
|
|
168
223
|
}
|
|
169
224
|
|
|
225
|
+
// Reference images for R2V (reference-to-video) generation
|
|
226
|
+
if (hasReferenceImages) {
|
|
227
|
+
body.reference_images = xaiOptions!.referenceImageUrls!.map(url => ({
|
|
228
|
+
url,
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
|
|
170
232
|
if (xaiOptions != null) {
|
|
171
233
|
for (const [key, value] of Object.entries(xaiOptions)) {
|
|
172
234
|
if (
|
|
173
235
|
![
|
|
236
|
+
'mode',
|
|
174
237
|
'pollIntervalMs',
|
|
175
238
|
'pollTimeoutMs',
|
|
176
239
|
'resolution',
|
|
177
240
|
'videoUrl',
|
|
241
|
+
'referenceImageUrls',
|
|
178
242
|
].includes(key)
|
|
179
243
|
) {
|
|
180
244
|
body[key] = value;
|
|
@@ -184,9 +248,19 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
184
248
|
|
|
185
249
|
const baseURL = this.config.baseURL ?? 'https://api.x.ai/v1';
|
|
186
250
|
|
|
187
|
-
//
|
|
251
|
+
// Determine endpoint based on mode
|
|
252
|
+
let endpoint: string;
|
|
253
|
+
if (isEdit) {
|
|
254
|
+
endpoint = `${baseURL}/videos/edits`;
|
|
255
|
+
} else if (isExtension) {
|
|
256
|
+
endpoint = `${baseURL}/videos/extensions`;
|
|
257
|
+
} else {
|
|
258
|
+
endpoint = `${baseURL}/videos/generations`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Step 1: Create video generation/edit/extension request
|
|
188
262
|
const { value: createResponse } = await postJsonToApi({
|
|
189
|
-
url:
|
|
263
|
+
url: endpoint,
|
|
190
264
|
headers: combineHeaders(this.config.headers(), options.headers),
|
|
191
265
|
body,
|
|
192
266
|
failedResponseHandler: xaiFailedResponseHandler,
|
|
@@ -239,6 +313,14 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
239
313
|
statusResponse.status === 'done' ||
|
|
240
314
|
(statusResponse.status == null && statusResponse.video?.url)
|
|
241
315
|
) {
|
|
316
|
+
if (statusResponse.video?.respect_moderation === false) {
|
|
317
|
+
throw new AISDKError({
|
|
318
|
+
name: 'XAI_VIDEO_MODERATION_ERROR',
|
|
319
|
+
message:
|
|
320
|
+
'Video generation was blocked due to a content policy violation.',
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
242
324
|
if (!statusResponse.video?.url) {
|
|
243
325
|
throw new AISDKError({
|
|
244
326
|
name: 'XAI_VIDEO_GENERATION_ERROR',
|
|
@@ -268,6 +350,12 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
268
350
|
...(statusResponse.video.duration != null
|
|
269
351
|
? { duration: statusResponse.video.duration }
|
|
270
352
|
: {}),
|
|
353
|
+
...(statusResponse.usage?.cost_in_usd_ticks != null
|
|
354
|
+
? { costInUsdTicks: statusResponse.usage.cost_in_usd_ticks }
|
|
355
|
+
: {}),
|
|
356
|
+
...(statusResponse.progress != null
|
|
357
|
+
? { progress: statusResponse.progress }
|
|
358
|
+
: {}),
|
|
271
359
|
},
|
|
272
360
|
},
|
|
273
361
|
};
|
|
@@ -280,6 +368,13 @@ export class XaiVideoModel implements Experimental_VideoModelV3 {
|
|
|
280
368
|
});
|
|
281
369
|
}
|
|
282
370
|
|
|
371
|
+
if (statusResponse.status === 'failed') {
|
|
372
|
+
throw new AISDKError({
|
|
373
|
+
name: 'XAI_VIDEO_GENERATION_FAILED',
|
|
374
|
+
message: 'Video generation failed.',
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
283
378
|
// 'pending' → continue polling
|
|
284
379
|
}
|
|
285
380
|
}
|
|
@@ -299,4 +394,16 @@ const xaiVideoStatusResponseSchema = z.object({
|
|
|
299
394
|
})
|
|
300
395
|
.nullish(),
|
|
301
396
|
model: z.string().nullish(),
|
|
397
|
+
usage: z
|
|
398
|
+
.object({
|
|
399
|
+
cost_in_usd_ticks: z.number().nullish(),
|
|
400
|
+
})
|
|
401
|
+
.nullish(),
|
|
402
|
+
progress: z.number().nullish(),
|
|
403
|
+
error: z
|
|
404
|
+
.object({
|
|
405
|
+
code: z.string().nullish(),
|
|
406
|
+
message: z.string().nullish(),
|
|
407
|
+
})
|
|
408
|
+
.nullish(),
|
|
302
409
|
});
|
package/src/xai-video-options.ts
CHANGED
|
@@ -1,23 +1,145 @@
|
|
|
1
1
|
import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const nonEmptyStringSchema = z.string().min(1);
|
|
5
|
+
const resolutionSchema = z.enum(['480p', '720p']);
|
|
6
|
+
const modeSchema = z.enum(['edit-video', 'extend-video', 'reference-to-video']);
|
|
7
|
+
|
|
8
|
+
export type XaiVideoMode = z.infer<typeof modeSchema>;
|
|
9
|
+
type XaiVideoResolution = z.infer<typeof resolutionSchema>;
|
|
10
|
+
|
|
11
|
+
interface XaiVideoSharedOptions {
|
|
5
12
|
pollIntervalMs?: number | null;
|
|
6
13
|
pollTimeoutMs?: number | null;
|
|
7
|
-
resolution?:
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
resolution?: XaiVideoResolution | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface XaiVideoEditModeOptions extends XaiVideoSharedOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Select edit-video mode explicitly for best autocomplete and narrowing.
|
|
20
|
+
*/
|
|
21
|
+
mode: 'edit-video';
|
|
22
|
+
/** Source video URL to edit. */
|
|
23
|
+
videoUrl: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface XaiVideoExtendModeOptions extends XaiVideoSharedOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Select extend-video mode explicitly for best autocomplete and narrowing.
|
|
29
|
+
*/
|
|
30
|
+
mode: 'extend-video';
|
|
31
|
+
/** Source video URL to extend from its last frame. */
|
|
32
|
+
videoUrl: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface XaiVideoReferenceToVideoOptions extends XaiVideoSharedOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Select reference-to-video mode explicitly for best autocomplete and narrowing.
|
|
38
|
+
*/
|
|
39
|
+
mode: 'reference-to-video';
|
|
40
|
+
/** Reference image URLs (1-7) for R2V generation. */
|
|
41
|
+
referenceImageUrls: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface XaiVideoGenerationOptions extends XaiVideoSharedOptions {
|
|
45
|
+
mode?: undefined;
|
|
46
|
+
videoUrl?: undefined;
|
|
47
|
+
referenceImageUrls?: undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface XaiLegacyEditVideoOptions extends XaiVideoSharedOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Legacy backward-compatible shape: omitting `mode` while providing
|
|
53
|
+
* `videoUrl` behaves like edit-video.
|
|
54
|
+
*/
|
|
55
|
+
mode?: undefined;
|
|
56
|
+
videoUrl: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface XaiLegacyReferenceToVideoOptions extends XaiVideoSharedOptions {
|
|
60
|
+
/**
|
|
61
|
+
* Legacy backward-compatible shape: omitting `mode` while providing
|
|
62
|
+
* `referenceImageUrls` behaves like reference-to-video.
|
|
63
|
+
*/
|
|
64
|
+
mode?: undefined;
|
|
65
|
+
referenceImageUrls: string[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Provider options for xAI video generation.
|
|
70
|
+
*
|
|
71
|
+
* Use the `mode` option to select the operation:
|
|
72
|
+
*
|
|
73
|
+
* - `'edit-video'` + `videoUrl` -- video editing (`POST /v1/videos/edits`)
|
|
74
|
+
* - `'extend-video'` + `videoUrl` -- video extension (`POST /v1/videos/extensions`)
|
|
75
|
+
* - `'reference-to-video'` + `referenceImageUrls` -- R2V generation (`POST /v1/videos/generations`)
|
|
76
|
+
* - no `mode` -- standard generation from text prompts or image input
|
|
77
|
+
*
|
|
78
|
+
* Runtime remains backward compatible with legacy auto-detected provider
|
|
79
|
+
* options, but the public TypeScript type is intentionally explicit so editors
|
|
80
|
+
* can suggest valid modes and flag invalid field combinations.
|
|
81
|
+
*/
|
|
82
|
+
export type XaiVideoModelOptions =
|
|
83
|
+
| XaiVideoGenerationOptions
|
|
84
|
+
| XaiVideoEditModeOptions
|
|
85
|
+
| XaiVideoExtendModeOptions
|
|
86
|
+
| XaiVideoReferenceToVideoOptions
|
|
87
|
+
| XaiLegacyEditVideoOptions
|
|
88
|
+
| XaiLegacyReferenceToVideoOptions;
|
|
89
|
+
|
|
90
|
+
// ── Runtime schemas ───────────────────────────────────────────────────
|
|
91
|
+
const baseFields = {
|
|
92
|
+
pollIntervalMs: z.number().positive().nullish(),
|
|
93
|
+
pollTimeoutMs: z.number().positive().nullish(),
|
|
94
|
+
resolution: resolutionSchema.nullish(),
|
|
10
95
|
};
|
|
11
96
|
|
|
97
|
+
const editVideoSchema = z.object({
|
|
98
|
+
...baseFields,
|
|
99
|
+
mode: z.literal('edit-video'),
|
|
100
|
+
videoUrl: nonEmptyStringSchema,
|
|
101
|
+
referenceImageUrls: z.undefined().optional(),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const extendVideoSchema = z.object({
|
|
105
|
+
...baseFields,
|
|
106
|
+
mode: z.literal('extend-video'),
|
|
107
|
+
videoUrl: nonEmptyStringSchema,
|
|
108
|
+
referenceImageUrls: z.undefined().optional(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const referenceToVideoSchema = z.object({
|
|
112
|
+
...baseFields,
|
|
113
|
+
mode: z.literal('reference-to-video'),
|
|
114
|
+
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7),
|
|
115
|
+
videoUrl: z.undefined().optional(),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const autoDetectSchema = z.object({
|
|
119
|
+
...baseFields,
|
|
120
|
+
mode: z.undefined().optional(),
|
|
121
|
+
videoUrl: nonEmptyStringSchema.optional(),
|
|
122
|
+
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export const xaiVideoModelOptions = z.union([
|
|
126
|
+
editVideoSchema,
|
|
127
|
+
extendVideoSchema,
|
|
128
|
+
referenceToVideoSchema,
|
|
129
|
+
autoDetectSchema,
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const runtimeSchema = z
|
|
133
|
+
.object({
|
|
134
|
+
mode: modeSchema.optional(),
|
|
135
|
+
videoUrl: nonEmptyStringSchema.optional(),
|
|
136
|
+
referenceImageUrls: z.array(nonEmptyStringSchema).min(1).max(7).optional(),
|
|
137
|
+
...baseFields,
|
|
138
|
+
})
|
|
139
|
+
.passthrough();
|
|
140
|
+
|
|
141
|
+
export type XaiParsedVideoModelOptions = z.infer<typeof runtimeSchema>;
|
|
142
|
+
|
|
12
143
|
export const xaiVideoModelOptionsSchema = lazySchema(() =>
|
|
13
|
-
zodSchema(
|
|
14
|
-
z
|
|
15
|
-
.object({
|
|
16
|
-
pollIntervalMs: z.number().positive().nullish(),
|
|
17
|
-
pollTimeoutMs: z.number().positive().nullish(),
|
|
18
|
-
resolution: z.enum(['480p', '720p']).nullish(),
|
|
19
|
-
videoUrl: z.string().nullish(),
|
|
20
|
-
})
|
|
21
|
-
.passthrough(),
|
|
22
|
-
),
|
|
144
|
+
zodSchema(runtimeSchema),
|
|
23
145
|
);
|