@ai-sdk/alibaba 1.0.34 → 1.0.36
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 +25 -0
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +165 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +166 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/alibaba-chat-language-model.ts +19 -35
- package/src/alibaba-video-model.ts +210 -31
- package/src/alibaba-video-settings.ts +4 -0
- package/src/convert-to-alibaba-chat-messages.ts +1 -1
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '@ai-sdk/provider';
|
|
7
7
|
import {
|
|
8
8
|
combineHeaders,
|
|
9
|
+
convertImageModelFileToDataUri,
|
|
9
10
|
convertUint8ArrayToBase64,
|
|
10
11
|
createJsonErrorResponseHandler,
|
|
11
12
|
createJsonResponseHandler,
|
|
@@ -41,6 +42,23 @@ export type AlibabaVideoModelOptions = {
|
|
|
41
42
|
* Use character identifiers (character1, character2) in prompts to reference them.
|
|
42
43
|
*/
|
|
43
44
|
referenceUrls?: string[] | null;
|
|
45
|
+
/**
|
|
46
|
+
* Explicit media array for reference-to-video mode (wan2.7 models).
|
|
47
|
+
* Overrides the automatic mapping from `inputReferences` and `frameImages`.
|
|
48
|
+
* Use `Image 1`, `Video 1`, etc. in prompts to reference media items
|
|
49
|
+
* (images and videos are counted separately, in array order).
|
|
50
|
+
*/
|
|
51
|
+
media?: Array<{
|
|
52
|
+
type: 'reference_image' | 'reference_video' | 'first_frame';
|
|
53
|
+
/** Public URL, or a `data:{mime};base64,{data}` URI for images. */
|
|
54
|
+
url: string;
|
|
55
|
+
/** URL to an audio file used as voice reference for this media item. */
|
|
56
|
+
referenceVoice?: string | null;
|
|
57
|
+
}> | null;
|
|
58
|
+
/**
|
|
59
|
+
* Aspect ratio (wan2.7 text-to-video and reference-to-video models).
|
|
60
|
+
*/
|
|
61
|
+
ratio?: '16:9' | '9:16' | '1:1' | '4:3' | '3:4' | null;
|
|
44
62
|
/** Polling interval in milliseconds. Defaults to 5000 (5 seconds). */
|
|
45
63
|
pollIntervalMs?: number | null;
|
|
46
64
|
/** Maximum wait time in milliseconds for video generation. Defaults to 600000 (10 minutes). */
|
|
@@ -59,6 +77,20 @@ const alibabaVideoModelOptionsSchema = lazySchema(() =>
|
|
|
59
77
|
watermark: z.boolean().nullish(),
|
|
60
78
|
audio: z.boolean().nullish(),
|
|
61
79
|
referenceUrls: z.array(z.string()).nullish(),
|
|
80
|
+
media: z
|
|
81
|
+
.array(
|
|
82
|
+
z.object({
|
|
83
|
+
type: z.enum([
|
|
84
|
+
'reference_image',
|
|
85
|
+
'reference_video',
|
|
86
|
+
'first_frame',
|
|
87
|
+
]),
|
|
88
|
+
url: z.string(),
|
|
89
|
+
referenceVoice: z.string().nullish(),
|
|
90
|
+
}),
|
|
91
|
+
)
|
|
92
|
+
.nullish(),
|
|
93
|
+
ratio: z.enum(['16:9', '9:16', '1:1', '4:3', '3:4']).nullish(),
|
|
62
94
|
pollIntervalMs: z.number().positive().nullish(),
|
|
63
95
|
pollTimeoutMs: z.number().positive().nullish(),
|
|
64
96
|
})
|
|
@@ -134,6 +166,53 @@ function detectMode(modelId: string): 't2v' | 'i2v' | 'r2v' {
|
|
|
134
166
|
return 't2v';
|
|
135
167
|
}
|
|
136
168
|
|
|
169
|
+
// wan2.7 models use a different protocol than earlier wan models:
|
|
170
|
+
// resolution tiers + ratio instead of size, input.media instead of
|
|
171
|
+
// input.reference_urls (R2V), and no shot_type or audio parameters.
|
|
172
|
+
function isWan27Model(modelId: string): boolean {
|
|
173
|
+
return modelId.startsWith('wan2.7');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Maps SDK "WIDTHxHEIGHT" resolutions to Alibaba resolution tiers.
|
|
177
|
+
const resolutionTierMap: Record<string, string> = {
|
|
178
|
+
'1280x720': '720P',
|
|
179
|
+
'720x1280': '720P',
|
|
180
|
+
'960x960': '720P',
|
|
181
|
+
'1088x832': '720P',
|
|
182
|
+
'832x1088': '720P',
|
|
183
|
+
'1920x1080': '1080P',
|
|
184
|
+
'1080x1920': '1080P',
|
|
185
|
+
'1440x1440': '1080P',
|
|
186
|
+
'1632x1248': '1080P',
|
|
187
|
+
'1248x1632': '1080P',
|
|
188
|
+
'832x480': '480P',
|
|
189
|
+
'480x832': '480P',
|
|
190
|
+
'624x624': '480P',
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const supportedRatios = new Set(['16:9', '9:16', '1:1', '4:3', '3:4']);
|
|
194
|
+
|
|
195
|
+
function deriveRatioFromResolution(
|
|
196
|
+
resolution: `${number}x${number}`,
|
|
197
|
+
): string | undefined {
|
|
198
|
+
const [width, height] = resolution.split('x').map(Number);
|
|
199
|
+
if (
|
|
200
|
+
!Number.isInteger(width) ||
|
|
201
|
+
!Number.isInteger(height) ||
|
|
202
|
+
width <= 0 ||
|
|
203
|
+
height <= 0
|
|
204
|
+
) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
let a = width;
|
|
208
|
+
let b = height;
|
|
209
|
+
while (b !== 0) {
|
|
210
|
+
[a, b] = [b, a % b];
|
|
211
|
+
}
|
|
212
|
+
const ratio = `${width / a}:${height / a}`;
|
|
213
|
+
return supportedRatios.has(ratio) ? ratio : undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
137
216
|
function fileToImageString(file: Experimental_VideoModelV3File): string {
|
|
138
217
|
if (file.type === 'url') {
|
|
139
218
|
return file.url;
|
|
@@ -156,6 +235,61 @@ function resolveStartImage(
|
|
|
156
235
|
return getFirstFrameImage(options) ?? options.image;
|
|
157
236
|
}
|
|
158
237
|
|
|
238
|
+
function isVideoUrl(url: string): boolean {
|
|
239
|
+
return /\.(mp4|mov)([?#]|$)/i.test(url);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Builds the wan2.7 input.media array from inputReferences and frameImages.
|
|
243
|
+
function resolveMedia(
|
|
244
|
+
options: Parameters<Experimental_VideoModelV3['doGenerate']>[0],
|
|
245
|
+
alibabaOptions: AlibabaVideoModelOptions | undefined,
|
|
246
|
+
warnings: SharedV3Warning[],
|
|
247
|
+
): Array<Record<string, unknown>> | undefined {
|
|
248
|
+
if (alibabaOptions?.media != null && alibabaOptions.media.length > 0) {
|
|
249
|
+
return alibabaOptions.media.map(item => ({
|
|
250
|
+
type: item.type,
|
|
251
|
+
url: item.url,
|
|
252
|
+
...(item.referenceVoice != null
|
|
253
|
+
? { reference_voice: item.referenceVoice }
|
|
254
|
+
: {}),
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const media: Array<Record<string, unknown>> = [];
|
|
259
|
+
|
|
260
|
+
for (const reference of options.inputReferences ?? []) {
|
|
261
|
+
if (reference.type === 'url') {
|
|
262
|
+
media.push({
|
|
263
|
+
type: isVideoUrl(reference.url) ? 'reference_video' : 'reference_image',
|
|
264
|
+
url: reference.url,
|
|
265
|
+
});
|
|
266
|
+
} else if (reference.mediaType.startsWith('image/')) {
|
|
267
|
+
media.push({
|
|
268
|
+
type: 'reference_image',
|
|
269
|
+
url: convertImageModelFileToDataUri(reference),
|
|
270
|
+
});
|
|
271
|
+
} else {
|
|
272
|
+
warnings.push({
|
|
273
|
+
type: 'unsupported',
|
|
274
|
+
feature: 'inputReferences',
|
|
275
|
+
details:
|
|
276
|
+
'Alibaba reference-to-video requires URL references for videos. ' +
|
|
277
|
+
'Non-URL video reference was skipped.',
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const firstFrame = getFirstFrameImage(options);
|
|
283
|
+
if (firstFrame != null) {
|
|
284
|
+
media.push({
|
|
285
|
+
type: 'first_frame',
|
|
286
|
+
url: convertImageModelFileToDataUri(firstFrame),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return media.length > 0 ? media : undefined;
|
|
291
|
+
}
|
|
292
|
+
|
|
159
293
|
function resolveReferenceUrls(
|
|
160
294
|
options: Parameters<Experimental_VideoModelV3['doGenerate']>[0],
|
|
161
295
|
alibabaOptions: AlibabaVideoModelOptions | undefined,
|
|
@@ -230,20 +364,34 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
230
364
|
}
|
|
231
365
|
|
|
232
366
|
const startImage = resolveStartImage(options);
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
warnings,
|
|
237
|
-
);
|
|
367
|
+
const wan27 = isWan27Model(this.modelId);
|
|
368
|
+
// wan2.7 T2V and R2V take an explicit aspect ratio (I2V follows the input image)
|
|
369
|
+
const supportsRatio = wan27 && mode !== 'i2v';
|
|
238
370
|
|
|
239
371
|
// Handle image input for I2V mode
|
|
240
372
|
if (mode === 'i2v' && startImage != null) {
|
|
241
373
|
input.img_url = fileToImageString(startImage);
|
|
242
374
|
}
|
|
243
375
|
|
|
244
|
-
// Handle
|
|
245
|
-
if (mode === 'r2v'
|
|
246
|
-
|
|
376
|
+
// Handle references for R2V mode
|
|
377
|
+
if (mode === 'r2v') {
|
|
378
|
+
if (wan27) {
|
|
379
|
+
// wan2.7: input.media
|
|
380
|
+
const media = resolveMedia(options, alibabaOptions, warnings);
|
|
381
|
+
if (media != null) {
|
|
382
|
+
input.media = media;
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
// wan2.6: legacy protocol with input.reference_urls
|
|
386
|
+
const referenceUrls = resolveReferenceUrls(
|
|
387
|
+
options,
|
|
388
|
+
alibabaOptions,
|
|
389
|
+
warnings,
|
|
390
|
+
);
|
|
391
|
+
if (referenceUrls != null && referenceUrls.length > 0) {
|
|
392
|
+
input.reference_urls = referenceUrls;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
247
395
|
}
|
|
248
396
|
|
|
249
397
|
const lastFrame = options.frameImages?.find(
|
|
@@ -287,49 +435,80 @@ export class AlibabaVideoModel implements Experimental_VideoModelV3 {
|
|
|
287
435
|
|
|
288
436
|
// Resolution / Size mapping
|
|
289
437
|
if (options.resolution != null) {
|
|
290
|
-
if (mode === 'i2v') {
|
|
291
|
-
// I2V
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
'624x624': '480P',
|
|
306
|
-
};
|
|
307
|
-
parameters.resolution =
|
|
308
|
-
resolutionMap[options.resolution] || options.resolution;
|
|
438
|
+
if (mode === 'i2v' || wan27) {
|
|
439
|
+
// I2V and wan2.7 models use "720P" / "1080P" format
|
|
440
|
+
const resolutionTier =
|
|
441
|
+
resolutionTierMap[options.resolution] || options.resolution;
|
|
442
|
+
if (wan27 && resolutionTier !== '720P' && resolutionTier !== '1080P') {
|
|
443
|
+
warnings.push({
|
|
444
|
+
type: 'unsupported',
|
|
445
|
+
feature: 'resolution',
|
|
446
|
+
details:
|
|
447
|
+
'wan2.7 models only support 720P and 1080P ' +
|
|
448
|
+
`resolutions. The resolution "${options.resolution}" was ignored.`,
|
|
449
|
+
});
|
|
450
|
+
} else {
|
|
451
|
+
parameters.resolution = resolutionTier;
|
|
452
|
+
}
|
|
309
453
|
} else {
|
|
310
|
-
// T2V and R2V use "WIDTH*HEIGHT" format for the size parameter
|
|
454
|
+
// wan2.6 T2V and R2V use "WIDTH*HEIGHT" format for the size parameter
|
|
311
455
|
// Convert "WIDTHxHEIGHT" (SDK standard) to "WIDTH*HEIGHT" (Alibaba API)
|
|
312
456
|
parameters.size = options.resolution.replace('x', '*');
|
|
313
457
|
}
|
|
314
458
|
}
|
|
315
459
|
|
|
460
|
+
// wan2.7 T2V and R2V support an explicit aspect ratio parameter
|
|
461
|
+
if (supportsRatio) {
|
|
462
|
+
const ratio =
|
|
463
|
+
alibabaOptions?.ratio ??
|
|
464
|
+
options.aspectRatio ??
|
|
465
|
+
(options.resolution != null
|
|
466
|
+
? deriveRatioFromResolution(options.resolution)
|
|
467
|
+
: undefined);
|
|
468
|
+
if (ratio != null) {
|
|
469
|
+
parameters.ratio = ratio;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
316
473
|
// Provider-specific parameters
|
|
317
474
|
if (alibabaOptions?.promptExtend != null) {
|
|
318
475
|
parameters.prompt_extend = alibabaOptions.promptExtend;
|
|
319
476
|
}
|
|
320
477
|
if (alibabaOptions?.shotType != null) {
|
|
321
|
-
|
|
478
|
+
if (wan27) {
|
|
479
|
+
// wan2.7 removed shot_type; shot structure is described in the prompt
|
|
480
|
+
warnings.push({
|
|
481
|
+
type: 'unsupported',
|
|
482
|
+
feature: 'shotType',
|
|
483
|
+
details:
|
|
484
|
+
'wan2.7 models do not support the shotType option. ' +
|
|
485
|
+
'Describe the shot structure in the prompt instead.',
|
|
486
|
+
});
|
|
487
|
+
} else {
|
|
488
|
+
parameters.shot_type = alibabaOptions.shotType;
|
|
489
|
+
}
|
|
322
490
|
}
|
|
323
491
|
if (alibabaOptions?.watermark != null) {
|
|
324
492
|
parameters.watermark = alibabaOptions.watermark;
|
|
325
493
|
}
|
|
326
494
|
const audio = options.generateAudio ?? alibabaOptions?.audio;
|
|
327
495
|
if (audio != null) {
|
|
328
|
-
|
|
496
|
+
if (wan27) {
|
|
497
|
+
// wan2.7 does not have an audio parameter (audio is always generated)
|
|
498
|
+
warnings.push({
|
|
499
|
+
type: 'unsupported',
|
|
500
|
+
feature: 'generateAudio',
|
|
501
|
+
details:
|
|
502
|
+
'wan2.7 models always generate audio. ' +
|
|
503
|
+
'The audio option was ignored.',
|
|
504
|
+
});
|
|
505
|
+
} else {
|
|
506
|
+
parameters.audio = audio;
|
|
507
|
+
}
|
|
329
508
|
}
|
|
330
509
|
|
|
331
510
|
// Warn about unsupported standard options
|
|
332
|
-
if (options.aspectRatio) {
|
|
511
|
+
if (options.aspectRatio && !supportsRatio) {
|
|
333
512
|
warnings.push({
|
|
334
513
|
type: 'unsupported',
|
|
335
514
|
feature: 'aspectRatio',
|
|
@@ -3,10 +3,14 @@ export type AlibabaVideoModelId =
|
|
|
3
3
|
// Text-to-Video
|
|
4
4
|
| 'wan2.6-t2v'
|
|
5
5
|
| 'wan2.5-t2v-preview'
|
|
6
|
+
| 'wan2.7-t2v'
|
|
7
|
+
| 'wan2.7-t2v-2026-06-12'
|
|
6
8
|
// Image-to-Video (first frame)
|
|
7
9
|
| 'wan2.6-i2v'
|
|
8
10
|
| 'wan2.6-i2v-flash'
|
|
9
11
|
// Reference-to-Video
|
|
10
12
|
| 'wan2.6-r2v'
|
|
11
13
|
| 'wan2.6-r2v-flash'
|
|
14
|
+
| 'wan2.7-r2v'
|
|
15
|
+
| 'wan2.7-r2v-2026-06-12'
|
|
12
16
|
| (string & {});
|
|
@@ -167,7 +167,7 @@ export function convertToAlibabaChatMessages({
|
|
|
167
167
|
contentValue = output.value;
|
|
168
168
|
break;
|
|
169
169
|
case 'execution-denied':
|
|
170
|
-
contentValue = output.reason ?? 'Tool execution denied.';
|
|
170
|
+
contentValue = output.reason ?? 'Tool call execution denied.';
|
|
171
171
|
break;
|
|
172
172
|
case 'content':
|
|
173
173
|
case 'json':
|