@ai-sdk/xai 4.0.8 → 4.0.10
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 +20 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +760 -694
- package/dist/index.js.map +1 -1
- package/docs/01-xai.mdx +63 -40
- package/package.json +4 -4
- package/src/convert-to-xai-chat-messages.ts +61 -43
- package/src/index.ts +1 -0
- package/src/responses/convert-to-xai-responses-input.ts +11 -0
- package/src/responses/xai-responses-api.ts +5 -1
- package/src/responses/xai-responses-language-model-options.ts +1 -0
- package/src/xai-chat-language-model-options.ts +1 -0
- 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/src/xai-video-model.ts +51 -9
package/docs/01-xai.mdx
CHANGED
|
@@ -12,21 +12,7 @@ The [xAI Grok](https://x.ai) provider contains language model support for the [x
|
|
|
12
12
|
The xAI Grok provider is available via the `@ai-sdk/xai` module. You can
|
|
13
13
|
install it with
|
|
14
14
|
|
|
15
|
-
<
|
|
16
|
-
<Tab>
|
|
17
|
-
<Snippet text="pnpm add @ai-sdk/xai" dark />
|
|
18
|
-
</Tab>
|
|
19
|
-
<Tab>
|
|
20
|
-
<Snippet text="npm install @ai-sdk/xai" dark />
|
|
21
|
-
</Tab>
|
|
22
|
-
<Tab>
|
|
23
|
-
<Snippet text="yarn add @ai-sdk/xai" dark />
|
|
24
|
-
</Tab>
|
|
25
|
-
|
|
26
|
-
<Tab>
|
|
27
|
-
<Snippet text="bun add @ai-sdk/xai" dark />
|
|
28
|
-
</Tab>
|
|
29
|
-
</Tabs>
|
|
15
|
+
<InstallPackages packages="@ai-sdk/xai" />
|
|
30
16
|
|
|
31
17
|
## Provider Instance
|
|
32
18
|
|
|
@@ -212,6 +198,42 @@ const { text } = await generateText({
|
|
|
212
198
|
});
|
|
213
199
|
```
|
|
214
200
|
|
|
201
|
+
You can control the resolution at which the model processes an image with
|
|
202
|
+
the `imageDetail` provider option on the image part:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { xai } from '@ai-sdk/xai';
|
|
206
|
+
import { generateText } from 'ai';
|
|
207
|
+
|
|
208
|
+
const { text } = await generateText({
|
|
209
|
+
model: xai('grok-4.3'),
|
|
210
|
+
messages: [
|
|
211
|
+
{
|
|
212
|
+
role: 'user',
|
|
213
|
+
content: [
|
|
214
|
+
{ type: 'text', text: 'What do you see in this image?' },
|
|
215
|
+
{
|
|
216
|
+
type: 'file',
|
|
217
|
+
mediaType: 'image/png',
|
|
218
|
+
data: fs.readFileSync('./image.png'),
|
|
219
|
+
providerOptions: {
|
|
220
|
+
xai: { imageDetail: 'low' },
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The following image detail values are supported:
|
|
230
|
+
|
|
231
|
+
- **low**: processes the image at reduced resolution and consumes fewer input tokens.
|
|
232
|
+
- **high**: processes the image at full resolution.
|
|
233
|
+
- **auto**: lets the xAI API decide.
|
|
234
|
+
|
|
235
|
+
When not set, the image is processed at full resolution.
|
|
236
|
+
|
|
215
237
|
### Web Search Tool
|
|
216
238
|
|
|
217
239
|
The web search tool enables autonomous web research with optional domain filtering and image understanding:
|
|
@@ -520,18 +542,19 @@ The following provider options are available:
|
|
|
520
542
|
|
|
521
543
|
## Model Capabilities
|
|
522
544
|
|
|
523
|
-
| Model | Image Input
|
|
524
|
-
| ----------------------------- |
|
|
525
|
-
| `grok-4.
|
|
526
|
-
| `grok-4.20-
|
|
527
|
-
| `grok-4-
|
|
528
|
-
| `grok-4-1-fast-
|
|
529
|
-
| `grok-4-1`
|
|
530
|
-
| `grok-4-
|
|
531
|
-
| `grok-4-fast-
|
|
532
|
-
| `grok-
|
|
533
|
-
| `grok-
|
|
534
|
-
| `grok-3
|
|
545
|
+
| Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Reasoning |
|
|
546
|
+
| ----------------------------- | ----------- | ----------------- | ---------- | -------------- | --------- |
|
|
547
|
+
| `grok-4.5` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
548
|
+
| `grok-4.20-reasoning` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
549
|
+
| `grok-4.20-non-reasoning` | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
550
|
+
| `grok-4-1-fast-reasoning` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
551
|
+
| `grok-4-1-fast-non-reasoning` | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
552
|
+
| `grok-4-1` | <Cross /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
553
|
+
| `grok-4-fast-reasoning` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
554
|
+
| `grok-4-fast-non-reasoning` | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
555
|
+
| `grok-code-fast-1` | <Cross /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
556
|
+
| `grok-3` | <Cross /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
557
|
+
| `grok-3-mini` | <Cross /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
535
558
|
|
|
536
559
|
<Note>
|
|
537
560
|
The table above lists popular models. Please see the [xAI
|
|
@@ -639,9 +662,9 @@ const result = await generateSpeech({
|
|
|
639
662
|
|
|
640
663
|
### Model Capabilities
|
|
641
664
|
|
|
642
|
-
| Model | Language
|
|
643
|
-
| --------- |
|
|
644
|
-
| `default` | <Check
|
|
665
|
+
| Model | Language | Speed | Output Formats |
|
|
666
|
+
| --------- | --------- | --------- | -------------------------- |
|
|
667
|
+
| `default` | <Check /> | <Check /> | mp3, wav, pcm, mulaw, alaw |
|
|
645
668
|
|
|
646
669
|
## Transcription Models
|
|
647
670
|
|
|
@@ -746,9 +769,9 @@ const result = await transcribe({
|
|
|
746
769
|
|
|
747
770
|
### Model Capabilities
|
|
748
771
|
|
|
749
|
-
| Model | Request/Response
|
|
750
|
-
| --------- |
|
|
751
|
-
| `default` | <Check
|
|
772
|
+
| Model | Request/Response | Streaming | Word Timestamps | Diarization | Multichannel |
|
|
773
|
+
| --------- | ---------------- | --------- | --------------- | ----------- | ------------ |
|
|
774
|
+
| `default` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
752
775
|
|
|
753
776
|
<Note>
|
|
754
777
|
Word timestamps, diarization results, and segments are only returned on the
|
|
@@ -883,10 +906,10 @@ const { images } = await generateImage({
|
|
|
883
906
|
|
|
884
907
|
### Image Model Capabilities
|
|
885
908
|
|
|
886
|
-
| Model | Resolution | Aspect Ratios | Image Editing
|
|
887
|
-
| ------------------------ | ---------- | ----------------------------------------------------------------------------------------------------------- |
|
|
888
|
-
| `grok-imagine-image-pro` | `1k`, `2k` | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`, `2:1`, `1:2`, `19.5:9`, `9:19.5`, `20:9`, `9:20`, `auto` | <Check
|
|
889
|
-
| `grok-imagine-image` | `1k` | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`, `2:1`, `1:2`, `19.5:9`, `9:19.5`, `20:9`, `9:20`, `auto` | <Check
|
|
909
|
+
| Model | Resolution | Aspect Ratios | Image Editing |
|
|
910
|
+
| ------------------------ | ---------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
|
|
911
|
+
| `grok-imagine-image-pro` | `1k`, `2k` | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`, `2:1`, `1:2`, `19.5:9`, `9:19.5`, `20:9`, `9:20`, `auto` | <Check /> |
|
|
912
|
+
| `grok-imagine-image` | `1k` | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`, `2:1`, `1:2`, `19.5:9`, `9:19.5`, `20:9`, `9:20`, `auto` | <Check /> |
|
|
890
913
|
|
|
891
914
|
## Video Models
|
|
892
915
|
|
|
@@ -1232,9 +1255,9 @@ and `resolution` just like text-to-video.
|
|
|
1232
1255
|
|
|
1233
1256
|
### Video Model Capabilities
|
|
1234
1257
|
|
|
1235
|
-
| Model | Duration | Aspect Ratios | Resolution | Image-to-Video
|
|
1236
|
-
| -------------------- | -------- | ------------------------------------------------- | -------------- |
|
|
1237
|
-
| `grok-imagine-video` | 1–15s | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3` | `480p`, `720p` | <Check
|
|
1258
|
+
| Model | Duration | Aspect Ratios | Resolution | Image-to-Video | Editing | Extension | R2V |
|
|
1259
|
+
| -------------------- | -------- | ------------------------------------------------- | -------------- | -------------- | --------- | --------- | --------- |
|
|
1260
|
+
| `grok-imagine-video` | 1–15s | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3` | `480p`, `720p` | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
1238
1261
|
|
|
1239
1262
|
<Note>
|
|
1240
1263
|
You can also pass any available provider model ID as a string if needed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/xai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@ai-sdk/openai-compatible": "3.0.
|
|
33
|
-
"@ai-sdk/provider": "4.0.
|
|
34
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
32
|
+
"@ai-sdk/openai-compatible": "3.0.7",
|
|
33
|
+
"@ai-sdk/provider": "4.0.3",
|
|
34
|
+
"@ai-sdk/provider-utils": "5.0.7"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "22.19.19",
|
|
@@ -6,15 +6,19 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
convertToBase64,
|
|
8
8
|
getTopLevelMediaType,
|
|
9
|
+
parseProviderOptions,
|
|
9
10
|
resolveFullMediaType,
|
|
10
11
|
resolveProviderReference,
|
|
11
12
|
} from '@ai-sdk/provider-utils';
|
|
12
|
-
import type { XaiChatPrompt } from './xai-chat-prompt';
|
|
13
|
+
import type { XaiChatPrompt, XaiUserMessageContent } from './xai-chat-prompt';
|
|
14
|
+
import { xaiFilePartProviderOptions } from './xai-file-part-options';
|
|
13
15
|
|
|
14
|
-
export function convertToXaiChatMessages(
|
|
16
|
+
export async function convertToXaiChatMessages(
|
|
17
|
+
prompt: LanguageModelV4Prompt,
|
|
18
|
+
): Promise<{
|
|
15
19
|
messages: XaiChatPrompt;
|
|
16
20
|
warnings: Array<SharedV4Warning>;
|
|
17
|
-
} {
|
|
21
|
+
}> {
|
|
18
22
|
const messages: XaiChatPrompt = [];
|
|
19
23
|
const warnings: Array<SharedV4Warning> = [];
|
|
20
24
|
|
|
@@ -31,54 +35,68 @@ export function convertToXaiChatMessages(prompt: LanguageModelV4Prompt): {
|
|
|
31
35
|
break;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
const userContent: Array<XaiUserMessageContent> = [];
|
|
39
|
+
|
|
40
|
+
for (const part of content) {
|
|
41
|
+
switch (part.type) {
|
|
42
|
+
case 'text': {
|
|
43
|
+
userContent.push({ type: 'text', text: part.text });
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case 'file': {
|
|
47
|
+
switch (part.data.type) {
|
|
48
|
+
case 'reference': {
|
|
49
|
+
userContent.push({
|
|
50
|
+
type: 'file',
|
|
51
|
+
file: {
|
|
52
|
+
file_id: resolveProviderReference({
|
|
53
|
+
reference: part.data.reference,
|
|
54
|
+
provider: 'xai',
|
|
55
|
+
}),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'text': {
|
|
61
|
+
throw new UnsupportedFunctionalityError({
|
|
62
|
+
functionality: 'text file parts',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
case 'url':
|
|
66
|
+
case 'data': {
|
|
67
|
+
if (getTopLevelMediaType(part.mediaType) === 'image') {
|
|
68
|
+
const filePartOptions = await parseProviderOptions({
|
|
69
|
+
provider: 'xai',
|
|
70
|
+
providerOptions: part.providerOptions,
|
|
71
|
+
schema: xaiFilePartProviderOptions,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
userContent.push({
|
|
75
|
+
type: 'image_url',
|
|
76
|
+
image_url: {
|
|
77
|
+
url:
|
|
78
|
+
part.data.type === 'url'
|
|
79
|
+
? part.data.url.toString()
|
|
80
|
+
: `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
|
|
81
|
+
...(filePartOptions?.imageDetail != null && {
|
|
82
|
+
detail: filePartOptions.imageDetail,
|
|
50
83
|
}),
|
|
51
84
|
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
case 'text': {
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
55
87
|
throw new UnsupportedFunctionalityError({
|
|
56
|
-
functionality:
|
|
88
|
+
functionality: `file part media type ${part.mediaType}`,
|
|
57
89
|
});
|
|
58
90
|
}
|
|
59
|
-
|
|
60
|
-
case 'data': {
|
|
61
|
-
if (getTopLevelMediaType(part.mediaType) === 'image') {
|
|
62
|
-
return {
|
|
63
|
-
type: 'image_url',
|
|
64
|
-
image_url: {
|
|
65
|
-
url:
|
|
66
|
-
part.data.type === 'url'
|
|
67
|
-
? part.data.url.toString()
|
|
68
|
-
: `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
} else {
|
|
72
|
-
throw new UnsupportedFunctionalityError({
|
|
73
|
-
functionality: `file part media type ${part.mediaType}`,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
91
|
+
break;
|
|
77
92
|
}
|
|
78
93
|
}
|
|
94
|
+
break;
|
|
79
95
|
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
messages.push({ role: 'user', content: userContent });
|
|
82
100
|
|
|
83
101
|
break;
|
|
84
102
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type {
|
|
|
4
4
|
XaiLanguageModelChatOptions as XaiProviderOptions,
|
|
5
5
|
} from './xai-chat-language-model-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. */
|
|
@@ -6,9 +6,11 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
convertToBase64,
|
|
8
8
|
getTopLevelMediaType,
|
|
9
|
+
parseProviderOptions,
|
|
9
10
|
resolveFullMediaType,
|
|
10
11
|
resolveProviderReference,
|
|
11
12
|
} from '@ai-sdk/provider-utils';
|
|
13
|
+
import { xaiFilePartProviderOptions } from '../xai-file-part-options';
|
|
12
14
|
import type {
|
|
13
15
|
XaiResponsesInput,
|
|
14
16
|
XaiResponsesUserMessageContentPart,
|
|
@@ -71,9 +73,18 @@ export async function convertToXaiResponsesInput({
|
|
|
71
73
|
? block.data.url.toString()
|
|
72
74
|
: `data:${resolveFullMediaType({ part: block })};base64,${convertToBase64(block.data.data)}`;
|
|
73
75
|
|
|
76
|
+
const filePartOptions = await parseProviderOptions({
|
|
77
|
+
provider: 'xai',
|
|
78
|
+
providerOptions: block.providerOptions,
|
|
79
|
+
schema: xaiFilePartProviderOptions,
|
|
80
|
+
});
|
|
81
|
+
|
|
74
82
|
contentParts.push({
|
|
75
83
|
type: 'input_image',
|
|
76
84
|
image_url: imageUrl,
|
|
85
|
+
...(filePartOptions?.imageDetail != null && {
|
|
86
|
+
detail: filePartOptions.imageDetail,
|
|
87
|
+
}),
|
|
77
88
|
});
|
|
78
89
|
} else if (block.data.type === 'url') {
|
|
79
90
|
// xAI's Responses API accepts non-image documents (PDF, text, CSV, etc.)
|
|
@@ -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_id: string }
|
|
31
35
|
| { type: 'input_file'; file_url: string };
|
|
32
36
|
|
|
@@ -126,7 +126,7 @@ export class XaiChatLanguageModel implements LanguageModelV4 {
|
|
|
126
126
|
|
|
127
127
|
// convert ai sdk messages to xai format
|
|
128
128
|
const { messages, warnings: messageWarnings } =
|
|
129
|
-
convertToXaiChatMessages(prompt);
|
|
129
|
+
await convertToXaiChatMessages(prompt);
|
|
130
130
|
warnings.push(...messageWarnings);
|
|
131
131
|
|
|
132
132
|
// 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
|
| { type: 'file'; file: { file_id: string } };
|
|
23
26
|
|
|
24
27
|
export interface XaiAssistantMessage {
|
|
@@ -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
|
+
>;
|
package/src/xai-video-model.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
createJsonResponseHandler,
|
|
11
11
|
delay,
|
|
12
12
|
getFromApi,
|
|
13
|
+
getTopLevelMediaType,
|
|
13
14
|
parseProviderOptions,
|
|
14
15
|
postJsonToApi,
|
|
15
16
|
type FetchFunction,
|
|
@@ -62,6 +63,9 @@ function resolveStartImage(
|
|
|
62
63
|
return getFirstFrameImage(options) ?? options.image;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
const isVideoFile = (file: Experimental_VideoModelV4File): boolean =>
|
|
67
|
+
file.mediaType != null && getTopLevelMediaType(file.mediaType) === 'video';
|
|
68
|
+
|
|
65
69
|
function fileToXaiImageUrl(file: Experimental_VideoModelV4File): string {
|
|
66
70
|
if (file.type === 'url') {
|
|
67
71
|
return file.url;
|
|
@@ -71,17 +75,35 @@ function fileToXaiImageUrl(file: Experimental_VideoModelV4File): string {
|
|
|
71
75
|
typeof file.data === 'string'
|
|
72
76
|
? file.data
|
|
73
77
|
: convertUint8ArrayToBase64(file.data);
|
|
74
|
-
return `data:${file.mediaType
|
|
78
|
+
return `data:${file.mediaType};base64,${base64Data}`;
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
// Resolves the reference images for R2V generation. First-class
|
|
78
82
|
// `inputReferences` win over the legacy `referenceImageUrls` provider option.
|
|
83
|
+
// Video references are not supported for reference-to-video and are skipped
|
|
84
|
+
// with a warning.
|
|
79
85
|
function resolveReferenceImages(
|
|
80
86
|
options: XaiVideoDoGenerateOptions,
|
|
81
87
|
xaiOptions: XaiParsedVideoModelOptions | undefined,
|
|
88
|
+
warnings: SharedV4Warning[],
|
|
82
89
|
): Array<{ url: string }> | undefined {
|
|
83
90
|
if (options.inputReferences != null && options.inputReferences.length > 0) {
|
|
84
|
-
|
|
91
|
+
const imageReferences = options.inputReferences.filter(reference => {
|
|
92
|
+
if (isVideoFile(reference)) {
|
|
93
|
+
warnings.push({
|
|
94
|
+
type: 'unsupported',
|
|
95
|
+
feature: 'inputReferences',
|
|
96
|
+
details:
|
|
97
|
+
'xAI reference-to-video accepts image references only. The video ' +
|
|
98
|
+
'reference was ignored. Use providerOptions.xai.mode ' +
|
|
99
|
+
'"extend-video" to continue from a video.',
|
|
100
|
+
});
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return imageReferences.map(reference => ({
|
|
85
107
|
url: fileToXaiImageUrl(reference),
|
|
86
108
|
}));
|
|
87
109
|
}
|
|
@@ -278,24 +300,44 @@ export class XaiVideoModel implements Experimental_VideoModelV4 {
|
|
|
278
300
|
// nested xAI request image object.
|
|
279
301
|
const startImage = resolveStartImage(options);
|
|
280
302
|
if (startImage != null) {
|
|
281
|
-
|
|
303
|
+
if (isVideoFile(startImage)) {
|
|
304
|
+
const fromFrameImages = getFirstFrameImage(options) != null;
|
|
305
|
+
warnings.push({
|
|
306
|
+
type: 'unsupported',
|
|
307
|
+
feature: fromFrameImages ? 'frameImages' : 'image',
|
|
308
|
+
details:
|
|
309
|
+
'xAI does not accept a video as a start/frame image. The video ' +
|
|
310
|
+
'was ignored. Use providerOptions.xai.mode "extend-video" to ' +
|
|
311
|
+
'continue from a video instead.',
|
|
312
|
+
});
|
|
313
|
+
} else {
|
|
314
|
+
body.image = { url: fileToXaiImageUrl(startImage) };
|
|
315
|
+
}
|
|
282
316
|
}
|
|
283
317
|
|
|
284
318
|
// xAI has no first-last-frame interpolation; warn and ignore last_frame.
|
|
285
|
-
|
|
319
|
+
const lastFrameImage = getLastFrameImage(options);
|
|
320
|
+
if (lastFrameImage != null) {
|
|
286
321
|
warnings.push({
|
|
287
322
|
type: 'unsupported',
|
|
288
323
|
feature: 'frameImages',
|
|
289
|
-
details:
|
|
290
|
-
'xAI video
|
|
291
|
-
|
|
292
|
-
|
|
324
|
+
details: isVideoFile(lastFrameImage)
|
|
325
|
+
? 'xAI does not accept a video as a start/frame image. The video ' +
|
|
326
|
+
'last frame was ignored. Use providerOptions.xai.mode ' +
|
|
327
|
+
'"extend-video" to continue from a video instead.'
|
|
328
|
+
: 'xAI video models do not support last_frame. Use ' +
|
|
329
|
+
'providerOptions.xai.mode "extend-video" to continue from a ' +
|
|
330
|
+
"video's last frame. The last frame image was ignored.",
|
|
293
331
|
});
|
|
294
332
|
}
|
|
295
333
|
|
|
296
334
|
// Reference images for R2V (reference-to-video) generation
|
|
297
335
|
if (hasReferenceImages) {
|
|
298
|
-
const referenceImages = resolveReferenceImages(
|
|
336
|
+
const referenceImages = resolveReferenceImages(
|
|
337
|
+
options,
|
|
338
|
+
xaiOptions,
|
|
339
|
+
warnings,
|
|
340
|
+
);
|
|
299
341
|
if (referenceImages != null) {
|
|
300
342
|
body.reference_images = referenceImages;
|
|
301
343
|
}
|