@ai-sdk/google 4.0.2 → 4.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,16 +35,16 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.0",
39
- "@ai-sdk/provider-utils": "5.0.1"
38
+ "@ai-sdk/provider": "4.0.1",
39
+ "@ai-sdk/provider-utils": "5.0.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "22.19.19",
43
43
  "tsup": "^8.5.1",
44
44
  "typescript": "5.8.3",
45
45
  "zod": "3.25.76",
46
- "@vercel/ai-tsconfig": "0.0.0",
47
- "@ai-sdk/test-server": "2.0.0"
46
+ "@ai-sdk/test-server": "2.0.0",
47
+ "@vercel/ai-tsconfig": "0.0.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "zod": "^3.25.76 || ^4.1.8"
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AISDKError,
3
3
  type Experimental_VideoModelV4,
4
+ type Experimental_VideoModelV4File,
4
5
  type SharedV4Warning,
5
6
  } from '@ai-sdk/provider';
6
7
  import {
@@ -35,6 +36,103 @@ interface GoogleVideoModelConfig {
35
36
  };
36
37
  }
37
38
 
39
+ function getFirstFrameImage(
40
+ options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
41
+ ): Experimental_VideoModelV4File | undefined {
42
+ return options.frameImages?.find(frame => frame.frameType === 'first_frame')
43
+ ?.image;
44
+ }
45
+
46
+ function resolveStartImage(
47
+ options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
48
+ ): Experimental_VideoModelV4File | undefined {
49
+ return getFirstFrameImage(options) ?? options.image;
50
+ }
51
+
52
+ function getLastFrameImage(
53
+ options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
54
+ ): Experimental_VideoModelV4File | undefined {
55
+ return options.frameImages?.find(frame => frame.frameType === 'last_frame')
56
+ ?.image;
57
+ }
58
+
59
+ function getInputReferences(
60
+ options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
61
+ ): Array<Experimental_VideoModelV4File> | undefined {
62
+ if (options.frameImages != null && options.frameImages.length > 0) {
63
+ return undefined;
64
+ }
65
+
66
+ return options.inputReferences != null && options.inputReferences.length > 0
67
+ ? options.inputReferences
68
+ : undefined;
69
+ }
70
+
71
+ function convertFileToGoogleImage(
72
+ file: Experimental_VideoModelV4File,
73
+ warnings: SharedV4Warning[],
74
+ ): Record<string, unknown> | undefined {
75
+ if (file.type === 'url') {
76
+ if (file.url.startsWith('gs://')) {
77
+ return {
78
+ gcsUri: file.url,
79
+ mimeType: 'image/png',
80
+ };
81
+ }
82
+
83
+ warnings.push({
84
+ type: 'unsupported',
85
+ feature: 'URL-based image input',
86
+ details:
87
+ 'Google Generative AI video models require base64-encoded images or GCS URIs. URL will be ignored.',
88
+ });
89
+ return undefined;
90
+ }
91
+
92
+ const base64Data =
93
+ typeof file.data === 'string'
94
+ ? file.data
95
+ : convertUint8ArrayToBase64(file.data);
96
+
97
+ // The Gemini Developer API (generativelanguage.googleapis.com) requires
98
+ // inline image bytes wrapped as `inlineData`.
99
+ return {
100
+ inlineData: {
101
+ mimeType: file.mediaType || 'image/png',
102
+ data: base64Data,
103
+ },
104
+ };
105
+ }
106
+
107
+ function convertProviderReferenceImage(
108
+ refImg: NonNullable<GoogleVideoModelOptions['referenceImages']>[number],
109
+ ): Record<string, unknown> {
110
+ if (refImg.bytesBase64Encoded) {
111
+ return {
112
+ inlineData: {
113
+ mimeType: 'image/png',
114
+ data: refImg.bytesBase64Encoded,
115
+ },
116
+ };
117
+ }
118
+
119
+ if (refImg.gcsUri) {
120
+ return {
121
+ gcsUri: refImg.gcsUri,
122
+ };
123
+ }
124
+
125
+ return refImg;
126
+ }
127
+
128
+ function convertInputReferenceImage(
129
+ file: Experimental_VideoModelV4File,
130
+ warnings: SharedV4Warning[],
131
+ ): Record<string, unknown> | undefined {
132
+ const image = convertFileToGoogleImage(file, warnings);
133
+ return image != null ? { image, referenceType: 'asset' } : undefined;
134
+ }
135
+
38
136
  export class GoogleVideoModel implements Experimental_VideoModelV4 {
39
137
  readonly specificationVersion = 'v4';
40
138
 
@@ -71,46 +169,32 @@ export class GoogleVideoModel implements Experimental_VideoModelV4 {
71
169
  instance.prompt = options.prompt;
72
170
  }
73
171
 
74
- // Handle image-to-video: convert image to base64
75
- if (options.image != null) {
76
- if (options.image.type === 'url') {
77
- warnings.push({
78
- type: 'unsupported',
79
- feature: 'URL-based image input',
80
- details:
81
- 'Google Generative AI video models require base64-encoded images. URL will be ignored.',
82
- });
83
- } else {
84
- const base64Data =
85
- typeof options.image.data === 'string'
86
- ? options.image.data
87
- : convertUint8ArrayToBase64(options.image.data);
88
-
89
- instance.image = {
90
- inlineData: {
91
- mimeType: options.image.mediaType || 'image/png',
92
- data: base64Data,
93
- },
94
- };
172
+ const startImage = resolveStartImage(options);
173
+ if (startImage != null) {
174
+ const image = convertFileToGoogleImage(startImage, warnings);
175
+ if (image != null) {
176
+ instance.image = image;
95
177
  }
96
178
  }
97
179
 
98
- if (googleOptions?.referenceImages != null) {
99
- instance.referenceImages = googleOptions.referenceImages.map(refImg => {
100
- if (refImg.bytesBase64Encoded) {
101
- return {
102
- inlineData: {
103
- mimeType: 'image/png',
104
- data: refImg.bytesBase64Encoded,
105
- },
106
- };
107
- } else if (refImg.gcsUri) {
108
- return {
109
- gcsUri: refImg.gcsUri,
110
- };
111
- }
112
- return refImg;
180
+ const lastFrameImage = getLastFrameImage(options);
181
+ if (lastFrameImage != null) {
182
+ const lastFrame = convertFileToGoogleImage(lastFrameImage, warnings);
183
+ if (lastFrame != null) {
184
+ instance.lastFrame = lastFrame;
185
+ }
186
+ }
187
+
188
+ const inputReferences = getInputReferences(options);
189
+ if (inputReferences != null) {
190
+ instance.referenceImages = inputReferences.flatMap(reference => {
191
+ const converted = convertInputReferenceImage(reference, warnings);
192
+ return converted != null ? [converted] : [];
113
193
  });
194
+ } else if (googleOptions?.referenceImages != null) {
195
+ instance.referenceImages = googleOptions.referenceImages.map(refImg =>
196
+ convertProviderReferenceImage(refImg),
197
+ );
114
198
  }
115
199
 
116
200
  const parameters: Record<string, unknown> = {