ai 7.0.7 → 7.0.9

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.
@@ -83,7 +83,7 @@ import {
83
83
  } from "@ai-sdk/provider-utils";
84
84
 
85
85
  // src/version.ts
86
- var VERSION = true ? "7.0.7" : "0.0.0-test";
86
+ var VERSION = true ? "7.0.9" : "0.0.0-test";
87
87
 
88
88
  // src/util/download/download.ts
89
89
  var download = async ({
@@ -133,6 +133,7 @@ Here are the capabilities of popular models:
133
133
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5.1` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
134
134
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-codex` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
135
135
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-chat-latest` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
136
+ | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-sonnet-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
136
137
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-fable-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
137
138
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-opus-4-8` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
138
139
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-opus-4-7` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
@@ -171,6 +171,46 @@ const { video } = await generateVideo({
171
171
  });
172
172
  ```
173
173
 
174
+ ### First and Last Frame
175
+
176
+ Some video models support first-last-frame generation, where you provide the
177
+ starting and/or ending frames of the video. Use the `frameImages` option to pass
178
+ role-tagged images in a provider-agnostic way:
179
+
180
+ ```tsx highlight={"5-16"}
181
+ const { video } = await generateVideo({
182
+ model: __VIDEO_MODEL__,
183
+ prompt: 'The cat walks across the scene and transforms into a dog by the end',
184
+ frameImages: [
185
+ {
186
+ image: 'https://example.com/first-frame.png',
187
+ frameType: 'first_frame',
188
+ },
189
+ {
190
+ image: 'https://example.com/last-frame.png',
191
+ frameType: 'last_frame',
192
+ },
193
+ ],
194
+ });
195
+ ```
196
+
197
+ ### Reference Images
198
+
199
+ Some video models support reference-to-video generation, where you provide one or
200
+ more reference images that the model incorporates into the generated video. Use the `inputReferences` option to pass
201
+ these images in a provider-agnostic way:
202
+
203
+ ```tsx highlight={"5-8"}
204
+ const { video } = await generateVideo({
205
+ model: __VIDEO_MODEL__,
206
+ prompt: 'The two characters meet in a bustling market',
207
+ inputReferences: [
208
+ 'https://example.com/character-1.png',
209
+ 'https://example.com/character-2.png',
210
+ ],
211
+ });
212
+ ```
213
+
174
214
  ### Providing a Seed
175
215
 
176
216
  You can provide a seed to the `experimental_generateVideo` function to control the output of the video generation process.
@@ -40,7 +40,7 @@ You can create a GitHub repository from within your terminal, or on [github.com]
40
40
 
41
41
  To create your GitHub repository:
42
42
 
43
- 1. Navigate to [github.com](http://github.com/)
43
+ 1. Navigate to [github.com](https://github.com/)
44
44
  2. In the top right corner, click the "plus" icon and select "New repository"
45
45
  3. Pick a name for your repository (this can be anything)
46
46
  4. Click "Create repository"
@@ -110,6 +110,18 @@ console.log(videos);
110
110
  isOptional: true,
111
111
  description: 'Seed for the video generation.',
112
112
  },
113
+ {
114
+ name: 'frameImages',
115
+ type: 'Array<{ image: DataContent; frameType: "first_frame" | "last_frame" }>',
116
+ isOptional: true,
117
+ description: 'Role-tagged image inputs for first-last-frame generation.',
118
+ },
119
+ {
120
+ name: 'inputReferences',
121
+ type: 'Array<DataContent>',
122
+ isOptional: true,
123
+ description: 'Reference image inputs for reference-to-video generation.',
124
+ },
113
125
  {
114
126
  name: 'generateAudio',
115
127
  type: 'boolean',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.7",
3
+ "version": "7.0.9",
4
4
  "type": "module",
5
5
  "description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
6
6
  "license": "Apache-2.0",
@@ -42,9 +42,9 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.5",
46
- "@ai-sdk/provider": "4.0.0",
47
- "@ai-sdk/provider-utils": "5.0.1"
45
+ "@ai-sdk/gateway": "4.0.7",
46
+ "@ai-sdk/provider": "4.0.1",
47
+ "@ai-sdk/provider-utils": "5.0.2"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@edge-runtime/vm": "^5.0.0",
@@ -2,6 +2,8 @@ import type {
2
2
  Experimental_VideoModelV4,
3
3
  Experimental_VideoModelV4CallOptions,
4
4
  Experimental_VideoModelV4File,
5
+ Experimental_VideoModelV4FrameImage,
6
+ Experimental_VideoModelV4FrameType,
5
7
  SharedV4ProviderMetadata,
6
8
  } from '@ai-sdk/provider';
7
9
  import {
@@ -26,6 +28,7 @@ import { prepareRetries } from '../util/prepare-retries';
26
28
  import { VERSION } from '../version';
27
29
  import type { GenerateVideoResult } from './generate-video-result';
28
30
  import { splitDataUrl } from '../prompt/split-data-url';
31
+ import { convertDataContentToUint8Array } from '../prompt/data-content';
29
32
 
30
33
  export type GenerateVideoPrompt =
31
34
  | string
@@ -45,6 +48,8 @@ export type GenerateVideoPrompt =
45
48
  * @param duration - Duration of the video in seconds.
46
49
  * @param fps - Frames per second for the video.
47
50
  * @param seed - Seed for the video generation.
51
+ * @param frameImages - Role-tagged image inputs for image-to-video and first-last-frame generation.
52
+ * @param inputReferences - Reference image inputs for reference-to-video generation.
48
53
  * @param generateAudio - Whether the model should generate audio alongside the video.
49
54
  * @param providerOptions - Additional provider-specific options that are passed through to the provider
50
55
  * as body parameters.
@@ -66,6 +71,8 @@ export async function experimental_generateVideo({
66
71
  duration,
67
72
  fps,
68
73
  seed,
74
+ frameImages,
75
+ inputReferences,
69
76
  generateAudio,
70
77
  providerOptions,
71
78
  maxRetries: maxRetriesArg,
@@ -118,6 +125,26 @@ export async function experimental_generateVideo({
118
125
  */
119
126
  seed?: number;
120
127
 
128
+ /**
129
+ * Role-tagged image inputs for image-to-video and first-last-frame generation.
130
+ */
131
+ frameImages?: Array<{
132
+ /**
133
+ * The image for this frame.
134
+ */
135
+ image: DataContent;
136
+
137
+ /**
138
+ * Which frame this image represents.
139
+ */
140
+ frameType: Experimental_VideoModelV4FrameType;
141
+ }>;
142
+
143
+ /**
144
+ * Reference image inputs for reference-to-video generation.
145
+ */
146
+ inputReferences?: Array<DataContent>;
147
+
121
148
  /**
122
149
  * Whether the model should generate audio alongside the video.
123
150
  */
@@ -172,6 +199,55 @@ export async function experimental_generateVideo({
172
199
 
173
200
  const { prompt, image } = normalizePrompt(promptArg);
174
201
 
202
+ const normalizedFrameImages:
203
+ | Array<Experimental_VideoModelV4FrameImage>
204
+ | undefined = frameImages?.map(frame => ({
205
+ image: normalizeImageData(frame.image),
206
+ frameType: frame.frameType,
207
+ }));
208
+
209
+ const normalizedInputReferences:
210
+ | Array<Experimental_VideoModelV4File>
211
+ | undefined = inputReferences?.map(reference =>
212
+ normalizeImageData(reference),
213
+ );
214
+
215
+ const effectiveInputReferences =
216
+ normalizedFrameImages != null && normalizedFrameImages.length > 0
217
+ ? undefined
218
+ : normalizedInputReferences;
219
+
220
+ const warnings: Array<Warning> = [];
221
+
222
+ if (
223
+ normalizedFrameImages != null &&
224
+ normalizedFrameImages.length > 0 &&
225
+ normalizedInputReferences != null &&
226
+ normalizedInputReferences.length > 0
227
+ ) {
228
+ warnings.push({
229
+ type: 'other',
230
+ message:
231
+ 'inputReferences were ignored because frameImages were provided; ' +
232
+ 'frameImages and inputReferences cannot be combined.',
233
+ });
234
+ }
235
+
236
+ const firstFrameImage = normalizedFrameImages?.find(
237
+ frame => frame.frameType === 'first_frame',
238
+ )?.image;
239
+
240
+ if (image != null && firstFrameImage != null) {
241
+ warnings.push({
242
+ type: 'other',
243
+ message:
244
+ 'prompt.image was ignored because a first_frame frameImage was provided; ' +
245
+ 'the first_frame frameImage takes precedence as the start image.',
246
+ });
247
+ }
248
+
249
+ const resolvedImage = firstFrameImage ?? image;
250
+
175
251
  const maxVideosPerCallWithDefault =
176
252
  maxVideosPerCall ?? (await invokeModelMaxVideosPerCall(model)) ?? 1;
177
253
 
@@ -194,8 +270,10 @@ export async function experimental_generateVideo({
194
270
  duration,
195
271
  fps,
196
272
  seed,
273
+ image: resolvedImage,
274
+ frameImages: normalizedFrameImages,
275
+ inputReferences: effectiveInputReferences,
197
276
  generateAudio,
198
- image,
199
277
  providerOptions: providerOptions ?? {},
200
278
  headers: headersWithUserAgent,
201
279
  abortSignal,
@@ -206,7 +284,6 @@ export async function experimental_generateVideo({
206
284
 
207
285
  // collect result videos, warnings, and response metadata
208
286
  const videos: Array<GeneratedFile> = [];
209
- const warnings: Array<Warning> = [];
210
287
  const responses: Array<VideoModelResponseMetadata> = [];
211
288
  const providerMetadata: SharedV4ProviderMetadata = {};
212
289
 
@@ -342,59 +419,55 @@ function normalizePrompt(promptArg: GenerateVideoPrompt): {
342
419
  };
343
420
  }
344
421
 
345
- let image: Experimental_VideoModelV4File | undefined;
346
-
347
- if (promptArg.image != null) {
348
- const dataContent = promptArg.image;
349
-
350
- if (typeof dataContent === 'string') {
351
- if (
352
- dataContent.startsWith('http://') ||
353
- dataContent.startsWith('https://')
354
- ) {
355
- image = {
356
- type: 'url',
357
- url: dataContent,
358
- };
359
- } else if (dataContent.startsWith('data:')) {
360
- const { mediaType, base64Content } = splitDataUrl(dataContent);
361
- image = {
362
- type: 'file',
363
- mediaType: mediaType ?? 'image/png',
364
- data: convertBase64ToUint8Array(base64Content ?? ''),
365
- };
366
- } else {
367
- const bytes = convertBase64ToUint8Array(dataContent);
368
- const mediaType =
369
- detectMediaType({
370
- data: bytes,
371
- topLevelType: 'image',
372
- }) ?? 'image/png';
373
-
374
- image = {
375
- type: 'file',
376
- mediaType,
377
- data: bytes,
378
- };
379
- }
380
- } else if (dataContent instanceof Uint8Array) {
381
- const mediaType =
382
- detectMediaType({
383
- data: dataContent,
384
- topLevelType: 'image',
385
- }) ?? 'image/png';
386
-
387
- image = {
422
+ return {
423
+ prompt: promptArg.text,
424
+ image:
425
+ promptArg.image != null ? normalizeImageData(promptArg.image) : undefined,
426
+ };
427
+ }
428
+
429
+ /**
430
+ * Normalizes a {@link DataContent} image into a {@link Experimental_VideoModelV4File}.
431
+ * Accepts a URL string, a data URL, a base64 string, or binary image data.
432
+ */
433
+ function normalizeImageData(
434
+ dataContent: DataContent,
435
+ ): Experimental_VideoModelV4File {
436
+ if (typeof dataContent === 'string') {
437
+ if (
438
+ dataContent.startsWith('http://') ||
439
+ dataContent.startsWith('https://')
440
+ ) {
441
+ return {
442
+ type: 'url',
443
+ url: dataContent,
444
+ };
445
+ }
446
+
447
+ if (dataContent.startsWith('data:')) {
448
+ const { mediaType, base64Content } = splitDataUrl(dataContent);
449
+ return {
388
450
  type: 'file',
389
- mediaType,
390
- data: dataContent,
451
+ mediaType: mediaType ?? 'image/png',
452
+ data: convertBase64ToUint8Array(base64Content ?? ''),
391
453
  };
392
454
  }
455
+
456
+ const bytes = convertBase64ToUint8Array(dataContent);
457
+ return {
458
+ type: 'file',
459
+ mediaType:
460
+ detectMediaType({ data: bytes, topLevelType: 'image' }) ?? 'image/png',
461
+ data: bytes,
462
+ };
393
463
  }
394
464
 
465
+ const bytes = convertDataContentToUint8Array(dataContent);
395
466
  return {
396
- prompt: promptArg.text,
397
- image,
467
+ type: 'file',
468
+ mediaType:
469
+ detectMediaType({ data: bytes, topLevelType: 'image' }) ?? 'image/png',
470
+ data: bytes,
398
471
  };
399
472
  }
400
473