@ai-sdk/alibaba 1.0.49 → 1.0.50
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 +10 -0
- package/dist/index.d.mts +16 -8
- package/dist/index.d.ts +16 -8
- package/dist/index.js +74 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +74 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/alibaba-video-model.ts +130 -39
- package/src/alibaba-video-settings.ts +2 -0
|
@@ -34,7 +34,10 @@ export type AlibabaVideoModelOptions = {
|
|
|
34
34
|
shotType?: 'single' | 'multi' | null;
|
|
35
35
|
/** Whether to add watermark to generated video. Defaults to false. */
|
|
36
36
|
watermark?: boolean | null;
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Enable audio generation (I2V/R2V and wan3 models;
|
|
39
|
+
* wan2.7 models always generate audio). Defaults to true on wan3.
|
|
40
|
+
*/
|
|
38
41
|
audio?: boolean | null;
|
|
39
42
|
/**
|
|
40
43
|
* Reference URLs for reference-to-video mode.
|
|
@@ -43,22 +46,34 @@ export type AlibabaVideoModelOptions = {
|
|
|
43
46
|
*/
|
|
44
47
|
referenceUrls?: string[] | null;
|
|
45
48
|
/**
|
|
46
|
-
* Explicit media array
|
|
47
|
-
*
|
|
49
|
+
* Explicit media array (wan2.7 and wan3 models). Overrides the automatic
|
|
50
|
+
* mapping from `inputReferences` and `frameImages`.
|
|
48
51
|
* Use `Image 1`, `Video 1`, etc. in prompts to reference media items
|
|
49
|
-
* (images and
|
|
52
|
+
* (images, videos, and audio are counted separately, in array order).
|
|
53
|
+
*
|
|
54
|
+
* `reference_audio`, `file`, and `link` are wan3-only and have no top-level
|
|
55
|
+
* call option, so they can only be passed here. wan3 also rejects mixing
|
|
56
|
+
* `reference_*`/`file`/`link` with `first_frame`/`last_frame`.
|
|
50
57
|
*/
|
|
51
58
|
media?: Array<{
|
|
52
|
-
type:
|
|
59
|
+
type:
|
|
60
|
+
| 'reference_image'
|
|
61
|
+
| 'reference_video'
|
|
62
|
+
| 'reference_audio'
|
|
63
|
+
| 'first_frame'
|
|
64
|
+
| 'last_frame'
|
|
65
|
+
| 'file'
|
|
66
|
+
| 'link';
|
|
53
67
|
/** Public URL, or a `data:{mime};base64,{data}` URI for images. */
|
|
54
68
|
url: string;
|
|
55
69
|
/** URL to an audio file used as voice reference for this media item. */
|
|
56
70
|
referenceVoice?: string | null;
|
|
57
71
|
}> | null;
|
|
58
72
|
/**
|
|
59
|
-
* Aspect ratio (wan2.7 text-to-video and reference-to-video models
|
|
73
|
+
* Aspect ratio (wan2.7 text-to-video and reference-to-video models, and
|
|
74
|
+
* every wan3 generation). `adaptive` is wan3-only, and is its default.
|
|
60
75
|
*/
|
|
61
|
-
ratio?: '16:9' | '9:16' | '1:1' | '4:3' | '3:4' | null;
|
|
76
|
+
ratio?: 'adaptive' | '16:9' | '9:16' | '1:1' | '4:3' | '3:4' | null;
|
|
62
77
|
/** Polling interval in milliseconds. Defaults to 5000 (5 seconds). */
|
|
63
78
|
pollIntervalMs?: number | null;
|
|
64
79
|
/** Maximum wait time in milliseconds for video generation. Defaults to 600000 (10 minutes). */
|
|
@@ -83,14 +98,20 @@ const alibabaVideoModelOptionsSchema = lazySchema(() =>
|
|
|
83
98
|
type: z.enum([
|
|
84
99
|
'reference_image',
|
|
85
100
|
'reference_video',
|
|
101
|
+
'reference_audio',
|
|
86
102
|
'first_frame',
|
|
103
|
+
'last_frame',
|
|
104
|
+
'file',
|
|
105
|
+
'link',
|
|
87
106
|
]),
|
|
88
107
|
url: z.string(),
|
|
89
108
|
referenceVoice: z.string().nullish(),
|
|
90
109
|
}),
|
|
91
110
|
)
|
|
92
111
|
.nullish(),
|
|
93
|
-
ratio: z
|
|
112
|
+
ratio: z
|
|
113
|
+
.enum(['adaptive', '16:9', '9:16', '1:1', '4:3', '3:4'])
|
|
114
|
+
.nullish(),
|
|
94
115
|
pollIntervalMs: z.number().positive().nullish(),
|
|
95
116
|
pollTimeoutMs: z.number().positive().nullish(),
|
|
96
117
|
})
|
|
@@ -149,8 +170,12 @@ const alibabaVideoTaskStatusSchema = z.object({
|
|
|
149
170
|
.object({
|
|
150
171
|
duration: z.number().nullish(),
|
|
151
172
|
output_video_duration: z.number().nullish(),
|
|
173
|
+
// wan3 splits the total: input video counts toward its 30s ceiling.
|
|
174
|
+
input_video_duration: z.number().nullish(),
|
|
175
|
+
fps: z.number().nullish(),
|
|
152
176
|
SR: z.number().nullish(),
|
|
153
177
|
size: z.string().nullish(),
|
|
178
|
+
ratio: z.string().nullish(),
|
|
154
179
|
})
|
|
155
180
|
.nullish(),
|
|
156
181
|
request_id: z.string().nullish(),
|
|
@@ -160,17 +185,29 @@ type AlibabaVideoTaskStatusResponse = z.infer<
|
|
|
160
185
|
typeof alibabaVideoTaskStatusSchema
|
|
161
186
|
>;
|
|
162
187
|
|
|
188
|
+
// Only meaningful for ids that name their mode (wan2.6/wan2.7). wan3 ships a
|
|
189
|
+
// single all-in-one id, so its mode comes from the media the request carries.
|
|
163
190
|
function detectMode(modelId: string): 't2v' | 'i2v' | 'r2v' {
|
|
164
191
|
if (modelId.includes('-i2v')) return 'i2v';
|
|
165
192
|
if (modelId.includes('-r2v')) return 'r2v';
|
|
166
193
|
return 't2v';
|
|
167
194
|
}
|
|
168
195
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Request protocol, selected by model id:
|
|
198
|
+
* - `legacy` (wan2.6 and earlier): `parameters.size`, `input.img_url`, and
|
|
199
|
+
* `input.reference_urls`.
|
|
200
|
+
* - `wan27`: resolution tiers and `ratio` instead of `size`, `input.media`
|
|
201
|
+
* instead of `input.reference_urls`, no `shot_type`, audio always on.
|
|
202
|
+
* - `wan3`: like `wan27`, plus a real `last_frame` slot, an `audio` toggle,
|
|
203
|
+
* a 480P tier, and one id covering every mode.
|
|
204
|
+
*/
|
|
205
|
+
type AlibabaVideoProtocol = 'legacy' | 'wan27' | 'wan3';
|
|
206
|
+
|
|
207
|
+
function detectProtocol(modelId: string): AlibabaVideoProtocol {
|
|
208
|
+
if (modelId.startsWith('wan3')) return 'wan3';
|
|
209
|
+
if (modelId.startsWith('wan2.7')) return 'wan27';
|
|
210
|
+
return 'legacy';
|
|
174
211
|
}
|
|
175
212
|
|
|
176
213
|
// Maps SDK "WIDTHxHEIGHT" resolutions to Alibaba resolution tiers.
|
|
@@ -229,6 +266,13 @@ function getFirstFrameImage(
|
|
|
229
266
|
?.image;
|
|
230
267
|
}
|
|
231
268
|
|
|
269
|
+
function getLastFrameImage(
|
|
270
|
+
options: Parameters<Experimental_VideoModelV3['doGenerate']>[0],
|
|
271
|
+
): Experimental_VideoModelV3File | undefined {
|
|
272
|
+
return options.frameImages?.find(frame => frame.frameType === 'last_frame')
|
|
273
|
+
?.image;
|
|
274
|
+
}
|
|
275
|
+
|
|
232
276
|
function resolveStartImage(
|
|
233
277
|
options: Parameters<Experimental_VideoModelV3['doGenerate']>[0],
|
|
234
278
|
): Experimental_VideoModelV3File | undefined {
|
|
@@ -239,11 +283,16 @@ function isVideoUrl(url: string): boolean {
|
|
|
239
283
|
return /\.(mp4|mov)([?#]|$)/i.test(url);
|
|
240
284
|
}
|
|
241
285
|
|
|
242
|
-
// Builds the
|
|
286
|
+
// Builds the input.media array (wan2.7 and wan3) from inputReferences plus the
|
|
287
|
+
// frame images the caller resolved for this protocol.
|
|
243
288
|
function resolveMedia(
|
|
244
289
|
options: Parameters<Experimental_VideoModelV3['doGenerate']>[0],
|
|
245
290
|
alibabaOptions: AlibabaVideoModelOptions | undefined,
|
|
246
291
|
warnings: SharedV3Warning[],
|
|
292
|
+
frames: {
|
|
293
|
+
first?: Experimental_VideoModelV3File;
|
|
294
|
+
last?: Experimental_VideoModelV3File;
|
|
295
|
+
},
|
|
247
296
|
): Array<Record<string, unknown>> | undefined {
|
|
248
297
|
if (alibabaOptions?.media != null && alibabaOptions.media.length > 0) {
|
|
249
298
|
return alibabaOptions.media.map(item => ({
|
|
@@ -279,11 +328,17 @@ function resolveMedia(
|
|
|
279
328
|
}
|
|
280
329
|
}
|
|
281
330
|
|
|
282
|
-
|
|
283
|
-
if (firstFrame != null) {
|
|
331
|
+
if (frames.first != null) {
|
|
284
332
|
media.push({
|
|
285
333
|
type: 'first_frame',
|
|
286
|
-
url: convertImageModelFileToDataUri(
|
|
334
|
+
url: convertImageModelFileToDataUri(frames.first),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (frames.last != null) {
|
|
339
|
+
media.push({
|
|
340
|
+
type: 'last_frame',
|
|
341
|
+
url: convertImageModelFileToDataUri(frames.last),
|
|
287
342
|
});
|
|
288
343
|
}
|
|
289
344
|
|
|
@@ -364,20 +419,37 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
364
419
|
}
|
|
365
420
|
|
|
366
421
|
const startImage = resolveStartImage(options);
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
422
|
+
const protocol = detectProtocol(this.modelId);
|
|
423
|
+
const wan27 = protocol === 'wan27';
|
|
424
|
+
const wan3 = protocol === 'wan3';
|
|
425
|
+
// Resolution tiers and input.media replaced size/img_url from wan2.7 on.
|
|
426
|
+
const tieredProtocol = wan27 || wan3;
|
|
427
|
+
// wan2.7 T2V and R2V take an explicit aspect ratio (I2V follows the input
|
|
428
|
+
// image); wan3 serves every mode from one id, so it always takes one.
|
|
429
|
+
const supportsRatio = wan3 || (wan27 && mode !== 'i2v');
|
|
430
|
+
|
|
431
|
+
// Handle image input for I2V mode (wan3 carries frames in input.media)
|
|
432
|
+
if (!wan3 && mode === 'i2v' && startImage != null) {
|
|
373
433
|
input.img_url = fileToImageString(startImage);
|
|
374
434
|
}
|
|
375
435
|
|
|
376
|
-
|
|
377
|
-
|
|
436
|
+
if (wan3) {
|
|
437
|
+
// The media the request carries decides whether this is text-, image-,
|
|
438
|
+
// or reference-to-video, so there is no mode to read off the id. A bare
|
|
439
|
+
// prompt stays text-to-video.
|
|
440
|
+
const media = resolveMedia(options, alibabaOptions, warnings, {
|
|
441
|
+
first: startImage,
|
|
442
|
+
last: getLastFrameImage(options),
|
|
443
|
+
});
|
|
444
|
+
if (media != null) {
|
|
445
|
+
input.media = media;
|
|
446
|
+
}
|
|
447
|
+
} else if (mode === 'r2v') {
|
|
378
448
|
if (wan27) {
|
|
379
449
|
// wan2.7: input.media
|
|
380
|
-
const media = resolveMedia(options, alibabaOptions, warnings
|
|
450
|
+
const media = resolveMedia(options, alibabaOptions, warnings, {
|
|
451
|
+
first: getFirstFrameImage(options),
|
|
452
|
+
});
|
|
381
453
|
if (media != null) {
|
|
382
454
|
input.media = media;
|
|
383
455
|
}
|
|
@@ -394,11 +466,10 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
394
466
|
}
|
|
395
467
|
}
|
|
396
468
|
|
|
397
|
-
const lastFrame = options
|
|
398
|
-
frame => frame.frameType === 'last_frame',
|
|
399
|
-
)?.image;
|
|
469
|
+
const lastFrame = getLastFrameImage(options);
|
|
400
470
|
|
|
401
|
-
|
|
471
|
+
// wan3 has a real closing-frame slot, filled in input.media above.
|
|
472
|
+
if (lastFrame != null && !wan3) {
|
|
402
473
|
warnings.push({
|
|
403
474
|
type: 'unsupported',
|
|
404
475
|
feature: 'frameImages',
|
|
@@ -411,7 +482,8 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
411
482
|
if (
|
|
412
483
|
options.inputReferences != null &&
|
|
413
484
|
options.inputReferences.length > 0 &&
|
|
414
|
-
mode !== 'r2v'
|
|
485
|
+
mode !== 'r2v' &&
|
|
486
|
+
!wan3
|
|
415
487
|
) {
|
|
416
488
|
warnings.push({
|
|
417
489
|
type: 'unsupported',
|
|
@@ -435,17 +507,22 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
435
507
|
|
|
436
508
|
// Resolution / Size mapping
|
|
437
509
|
if (options.resolution != null) {
|
|
438
|
-
if (mode === 'i2v' ||
|
|
439
|
-
// I2V
|
|
510
|
+
if (mode === 'i2v' || tieredProtocol) {
|
|
511
|
+
// I2V, wan2.7, and wan3 use the "720P" / "1080P" tier format
|
|
440
512
|
const resolutionTier =
|
|
441
513
|
resolutionTierMap[options.resolution] || options.resolution;
|
|
442
|
-
|
|
514
|
+
// wan3 adds a 480P tier to wan2.7's two.
|
|
515
|
+
const supportedTiers = wan3
|
|
516
|
+
? ['480P', '720P', '1080P']
|
|
517
|
+
: ['720P', '1080P'];
|
|
518
|
+
if (tieredProtocol && !supportedTiers.includes(resolutionTier)) {
|
|
443
519
|
warnings.push({
|
|
444
520
|
type: 'unsupported',
|
|
445
521
|
feature: 'resolution',
|
|
446
522
|
details:
|
|
447
|
-
'wan2.7 models only support
|
|
448
|
-
|
|
523
|
+
`${wan3 ? 'wan3' : 'wan2.7'} models only support the ` +
|
|
524
|
+
`${supportedTiers.join(', ')} resolution tiers. ` +
|
|
525
|
+
`The resolution "${options.resolution}" was ignored.`,
|
|
449
526
|
});
|
|
450
527
|
} else {
|
|
451
528
|
parameters.resolution = resolutionTier;
|
|
@@ -475,14 +552,14 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
475
552
|
parameters.prompt_extend = alibabaOptions.promptExtend;
|
|
476
553
|
}
|
|
477
554
|
if (alibabaOptions?.shotType != null) {
|
|
478
|
-
if (
|
|
555
|
+
if (tieredProtocol) {
|
|
479
556
|
// wan2.7 removed shot_type; shot structure is described in the prompt
|
|
480
557
|
warnings.push({
|
|
481
558
|
type: 'unsupported',
|
|
482
559
|
feature: 'shotType',
|
|
483
560
|
details:
|
|
484
|
-
'wan2.7 models do not support the shotType
|
|
485
|
-
'Describe the shot structure in the prompt instead.',
|
|
561
|
+
`${wan3 ? 'wan3' : 'wan2.7'} models do not support the shotType ` +
|
|
562
|
+
'option. Describe the shot structure in the prompt instead.',
|
|
486
563
|
});
|
|
487
564
|
} else {
|
|
488
565
|
parameters.shot_type = alibabaOptions.shotType;
|
|
@@ -650,6 +727,20 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
650
727
|
finalResponse.usage.output_video_duration,
|
|
651
728
|
resolution: finalResponse.usage.SR,
|
|
652
729
|
size: finalResponse.usage.size,
|
|
730
|
+
// wan3-only. Spread rather than set to undefined so the
|
|
731
|
+
// metadata shape for older wan models is unchanged.
|
|
732
|
+
...(finalResponse.usage.input_video_duration != null
|
|
733
|
+
? {
|
|
734
|
+
inputVideoDuration:
|
|
735
|
+
finalResponse.usage.input_video_duration,
|
|
736
|
+
}
|
|
737
|
+
: {}),
|
|
738
|
+
...(finalResponse.usage.fps != null
|
|
739
|
+
? { fps: finalResponse.usage.fps }
|
|
740
|
+
: {}),
|
|
741
|
+
...(finalResponse.usage.ratio != null
|
|
742
|
+
? { ratio: finalResponse.usage.ratio }
|
|
743
|
+
: {}),
|
|
653
744
|
},
|
|
654
745
|
}
|
|
655
746
|
: {}),
|