@ai-sdk/google 4.0.0-beta.8 → 4.0.0-canary.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +351 -4
  2. package/README.md +6 -4
  3. package/dist/index.d.ts +97 -54
  4. package/dist/index.js +1644 -580
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +66 -26
  7. package/dist/internal/index.js +1258 -450
  8. package/dist/internal/index.js.map +1 -1
  9. package/docs/{15-google-generative-ai.mdx → 15-google.mdx} +46 -40
  10. package/package.json +13 -14
  11. package/src/{convert-google-generative-ai-usage.ts → convert-google-usage.ts} +12 -5
  12. package/src/convert-json-schema-to-openapi-schema.ts +1 -1
  13. package/src/convert-to-google-messages.ts +577 -0
  14. package/src/{google-generative-ai-embedding-options.ts → google-embedding-model-options.ts} +2 -2
  15. package/src/{google-generative-ai-embedding-model.ts → google-embedding-model.ts} +31 -18
  16. package/src/google-error.ts +1 -1
  17. package/src/google-files.ts +225 -0
  18. package/src/google-image-model-options.ts +23 -0
  19. package/src/{google-generative-ai-image-model.ts → google-image-model.ts} +74 -62
  20. package/src/{google-generative-ai-image-settings.ts → google-image-settings.ts} +2 -2
  21. package/src/google-json-accumulator.ts +336 -0
  22. package/src/{google-generative-ai-options.ts → google-language-model-options.ts} +32 -5
  23. package/src/{google-generative-ai-language-model.ts → google-language-model.ts} +609 -214
  24. package/src/google-prepare-tools.ts +72 -12
  25. package/src/google-prompt.ts +82 -0
  26. package/src/google-provider.ts +63 -54
  27. package/src/google-video-model-options.ts +43 -0
  28. package/src/{google-generative-ai-video-model.ts → google-video-model.ts} +17 -56
  29. package/src/{google-generative-ai-video-settings.ts → google-video-settings.ts} +2 -1
  30. package/src/index.ts +28 -9
  31. package/src/internal/index.ts +2 -2
  32. package/src/{map-google-generative-ai-finish-reason.ts → map-google-finish-reason.ts} +3 -3
  33. package/src/tool/code-execution.ts +2 -2
  34. package/src/tool/enterprise-web-search.ts +9 -3
  35. package/src/tool/file-search.ts +5 -7
  36. package/src/tool/google-maps.ts +3 -2
  37. package/src/tool/google-search.ts +10 -11
  38. package/src/tool/url-context.ts +4 -2
  39. package/src/tool/vertex-rag-store.ts +9 -6
  40. package/dist/index.d.mts +0 -384
  41. package/dist/index.mjs +0 -2519
  42. package/dist/index.mjs.map +0 -1
  43. package/dist/internal/index.d.mts +0 -287
  44. package/dist/internal/index.mjs +0 -1708
  45. package/dist/internal/index.mjs.map +0 -1
  46. package/src/convert-to-google-generative-ai-messages.ts +0 -239
  47. package/src/google-generative-ai-prompt.ts +0 -47
@@ -0,0 +1,225 @@
1
+ import {
2
+ AISDKError,
3
+ type FilesV4,
4
+ type FilesV4UploadFileCallOptions,
5
+ type FilesV4UploadFileResult,
6
+ type SharedV4Warning,
7
+ } from '@ai-sdk/provider';
8
+ import {
9
+ combineHeaders,
10
+ convertInlineFileDataToUint8Array,
11
+ createJsonResponseHandler,
12
+ delay,
13
+ lazySchema,
14
+ parseProviderOptions,
15
+ zodSchema,
16
+ getFromApi,
17
+ type FetchFunction,
18
+ } from '@ai-sdk/provider-utils';
19
+ import { z } from 'zod/v4';
20
+ import { googleFailedResponseHandler } from './google-error';
21
+
22
+ export type GoogleFilesUploadOptions = {
23
+ displayName?: string | null;
24
+ pollIntervalMs?: number | null;
25
+ pollTimeoutMs?: number | null;
26
+
27
+ [key: string]: unknown;
28
+ };
29
+
30
+ interface GoogleFilesConfig {
31
+ provider: string;
32
+ baseURL: string;
33
+ headers: () => Record<string, string | undefined>;
34
+ fetch?: FetchFunction;
35
+ }
36
+
37
+ export class GoogleFiles implements FilesV4 {
38
+ readonly specificationVersion = 'v4';
39
+
40
+ get provider(): string {
41
+ return this.config.provider;
42
+ }
43
+
44
+ constructor(private readonly config: GoogleFilesConfig) {}
45
+
46
+ async uploadFile(
47
+ options: FilesV4UploadFileCallOptions,
48
+ ): Promise<FilesV4UploadFileResult> {
49
+ const googleOptions = (await parseProviderOptions({
50
+ provider: 'google',
51
+ providerOptions: options.providerOptions,
52
+ schema: googleFilesUploadOptionsSchema,
53
+ })) as GoogleFilesUploadOptions | undefined;
54
+
55
+ const resolvedHeaders = this.config.headers();
56
+ const fetchFn = this.config.fetch ?? globalThis.fetch;
57
+
58
+ const warnings: Array<SharedV4Warning> = [];
59
+ if (options.filename != null) {
60
+ warnings.push({ type: 'unsupported', feature: 'filename' });
61
+ }
62
+
63
+ const fileBytes = convertInlineFileDataToUint8Array(options.data);
64
+
65
+ const mediaType = options.mediaType;
66
+ const displayName = googleOptions?.displayName;
67
+
68
+ const baseOrigin = this.config.baseURL.replace(/\/v1beta$/, '');
69
+
70
+ const initResponse = await fetchFn(`${baseOrigin}/upload/v1beta/files`, {
71
+ method: 'POST',
72
+ headers: {
73
+ ...resolvedHeaders,
74
+ 'X-Goog-Upload-Protocol': 'resumable',
75
+ 'X-Goog-Upload-Command': 'start',
76
+ 'X-Goog-Upload-Header-Content-Length': String(fileBytes.length),
77
+ 'X-Goog-Upload-Header-Content-Type': mediaType,
78
+ 'Content-Type': 'application/json',
79
+ },
80
+ body: JSON.stringify({
81
+ file: {
82
+ ...(displayName != null ? { display_name: displayName } : {}),
83
+ },
84
+ }),
85
+ });
86
+
87
+ if (!initResponse.ok) {
88
+ const errorBody = await initResponse.text();
89
+ throw new AISDKError({
90
+ name: 'GOOGLE_FILES_UPLOAD_ERROR',
91
+ message: `Failed to initiate resumable upload: ${initResponse.status} ${errorBody}`,
92
+ });
93
+ }
94
+
95
+ const uploadUrl = initResponse.headers.get('x-goog-upload-url');
96
+ if (!uploadUrl) {
97
+ throw new AISDKError({
98
+ name: 'GOOGLE_FILES_UPLOAD_ERROR',
99
+ message: 'No upload URL returned from initiation request',
100
+ });
101
+ }
102
+
103
+ const uploadResponse = await fetchFn(uploadUrl, {
104
+ method: 'POST',
105
+ headers: {
106
+ 'Content-Length': String(fileBytes.length),
107
+ 'X-Goog-Upload-Offset': '0',
108
+ 'X-Goog-Upload-Command': 'upload, finalize',
109
+ },
110
+ body: fileBytes,
111
+ });
112
+
113
+ if (!uploadResponse.ok) {
114
+ const errorBody = await uploadResponse.text();
115
+ throw new AISDKError({
116
+ name: 'GOOGLE_FILES_UPLOAD_ERROR',
117
+ message: `Failed to upload file data: ${uploadResponse.status} ${errorBody}`,
118
+ });
119
+ }
120
+
121
+ const uploadResult = (await uploadResponse.json()) as {
122
+ file: GoogleFileResource;
123
+ };
124
+
125
+ let file = uploadResult.file;
126
+
127
+ const pollIntervalMs = googleOptions?.pollIntervalMs ?? 2000;
128
+ const pollTimeoutMs = googleOptions?.pollTimeoutMs ?? 300000;
129
+ const startTime = Date.now();
130
+
131
+ while (file.state === 'PROCESSING') {
132
+ if (Date.now() - startTime > pollTimeoutMs) {
133
+ throw new AISDKError({
134
+ name: 'GOOGLE_FILES_UPLOAD_TIMEOUT',
135
+ message: `File processing timed out after ${pollTimeoutMs}ms`,
136
+ });
137
+ }
138
+
139
+ await delay(pollIntervalMs);
140
+
141
+ const { value: fileStatus } = await getFromApi({
142
+ url: `${this.config.baseURL}/${file.name}`,
143
+ headers: combineHeaders(resolvedHeaders),
144
+ successfulResponseHandler: createJsonResponseHandler(
145
+ googleFileResponseSchema,
146
+ ),
147
+ failedResponseHandler: googleFailedResponseHandler,
148
+ fetch: this.config.fetch,
149
+ });
150
+
151
+ file = fileStatus;
152
+ }
153
+
154
+ if (file.state === 'FAILED') {
155
+ throw new AISDKError({
156
+ name: 'GOOGLE_FILES_UPLOAD_FAILED',
157
+ message: `File processing failed for ${file.name}`,
158
+ });
159
+ }
160
+
161
+ return {
162
+ warnings,
163
+ providerReference: { google: file.uri },
164
+ mediaType: file.mimeType ?? options.mediaType,
165
+ providerMetadata: {
166
+ google: {
167
+ name: file.name,
168
+ displayName: file.displayName,
169
+ mimeType: file.mimeType,
170
+ sizeBytes: file.sizeBytes,
171
+ state: file.state,
172
+ uri: file.uri,
173
+ ...(file.createTime != null ? { createTime: file.createTime } : {}),
174
+ ...(file.updateTime != null ? { updateTime: file.updateTime } : {}),
175
+ ...(file.expirationTime != null
176
+ ? { expirationTime: file.expirationTime }
177
+ : {}),
178
+ ...(file.sha256Hash != null ? { sha256Hash: file.sha256Hash } : {}),
179
+ },
180
+ },
181
+ };
182
+ }
183
+ }
184
+
185
+ type GoogleFileResource = {
186
+ name: string;
187
+ displayName?: string | null;
188
+ mimeType: string;
189
+ sizeBytes?: string | null;
190
+ createTime?: string | null;
191
+ updateTime?: string | null;
192
+ expirationTime?: string | null;
193
+ sha256Hash?: string | null;
194
+ uri: string;
195
+ state: string;
196
+ };
197
+
198
+ const googleFileResponseSchema = lazySchema(() =>
199
+ zodSchema(
200
+ z.object({
201
+ name: z.string(),
202
+ displayName: z.string().nullish(),
203
+ mimeType: z.string(),
204
+ sizeBytes: z.string().nullish(),
205
+ createTime: z.string().nullish(),
206
+ updateTime: z.string().nullish(),
207
+ expirationTime: z.string().nullish(),
208
+ sha256Hash: z.string().nullish(),
209
+ uri: z.string(),
210
+ state: z.string(),
211
+ }),
212
+ ),
213
+ );
214
+
215
+ const googleFilesUploadOptionsSchema = lazySchema(() =>
216
+ zodSchema(
217
+ z
218
+ .object({
219
+ displayName: z.string().nullish(),
220
+ pollIntervalMs: z.number().positive().nullish(),
221
+ pollTimeoutMs: z.number().positive().nullish(),
222
+ })
223
+ .passthrough(),
224
+ ),
225
+ );
@@ -0,0 +1,23 @@
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod/v4';
7
+
8
+ // Note: For the initial GA launch of Imagen 3, safety filters are not configurable.
9
+ // https://ai.google.dev/gemini-api/docs/imagen#imagen-model
10
+ export const googleImageModelOptionsSchema = lazySchema(() =>
11
+ zodSchema(
12
+ z.object({
13
+ personGeneration: z
14
+ .enum(['dont_allow', 'allow_adult', 'allow_all'])
15
+ .nullish(),
16
+ aspectRatio: z.enum(['1:1', '3:4', '4:3', '9:16', '16:9']).nullish(),
17
+ }),
18
+ ),
19
+ );
20
+
21
+ export type GoogleImageModelOptions = InferSchema<
22
+ typeof googleImageModelOptionsSchema
23
+ >;
@@ -1,32 +1,35 @@
1
- import {
2
- ImageModelV3,
3
- LanguageModelV3Prompt,
4
- SharedV3Warning,
1
+ import type {
2
+ ImageModelV4,
3
+ LanguageModelV4Prompt,
4
+ SharedV4Warning,
5
5
  } from '@ai-sdk/provider';
6
6
  import {
7
7
  combineHeaders,
8
8
  convertToBase64,
9
9
  createJsonResponseHandler,
10
- FetchFunction,
11
10
  generateId as defaultGenerateId,
12
- type InferSchema,
13
11
  lazySchema,
14
12
  parseProviderOptions,
15
13
  postJsonToApi,
16
- Resolvable,
17
14
  resolve,
15
+ serializeModelOptions,
16
+ WORKFLOW_SERIALIZE,
17
+ WORKFLOW_DESERIALIZE,
18
18
  zodSchema,
19
+ type FetchFunction,
20
+ type Resolvable,
19
21
  } from '@ai-sdk/provider-utils';
20
22
  import { z } from 'zod/v4';
21
23
  import { googleFailedResponseHandler } from './google-error';
22
- import {
23
- GoogleGenerativeAIImageModelId,
24
- GoogleGenerativeAIImageSettings,
25
- } from './google-generative-ai-image-settings';
26
- import { GoogleGenerativeAILanguageModel } from './google-generative-ai-language-model';
27
- import type { GoogleLanguageModelOptions } from './google-generative-ai-options';
28
-
29
- interface GoogleGenerativeAIImageModelConfig {
24
+ import { googleImageModelOptionsSchema } from './google-image-model-options';
25
+ import type {
26
+ GoogleImageModelId,
27
+ GoogleImageSettings,
28
+ } from './google-image-settings';
29
+ import { GoogleLanguageModel } from './google-language-model';
30
+ import type { GoogleLanguageModelOptions } from './google-language-model-options';
31
+
32
+ interface GoogleImageModelConfig {
30
33
  provider: string;
31
34
  baseURL: string;
32
35
  headers?: Resolvable<Record<string, string | undefined>>;
@@ -37,8 +40,22 @@ interface GoogleGenerativeAIImageModelConfig {
37
40
  };
38
41
  }
39
42
 
40
- export class GoogleGenerativeAIImageModel implements ImageModelV3 {
41
- readonly specificationVersion = 'v3';
43
+ export class GoogleImageModel implements ImageModelV4 {
44
+ readonly specificationVersion = 'v4';
45
+
46
+ static [WORKFLOW_SERIALIZE](model: GoogleImageModel) {
47
+ return serializeModelOptions({
48
+ modelId: model.modelId,
49
+ config: model.config,
50
+ });
51
+ }
52
+
53
+ static [WORKFLOW_DESERIALIZE](options: {
54
+ modelId: string;
55
+ config: GoogleImageModelConfig;
56
+ }) {
57
+ return new GoogleImageModel(options.modelId, {}, options.config);
58
+ }
42
59
 
43
60
  get maxImagesPerCall(): number {
44
61
  if (this.settings.maxImagesPerCall != null) {
@@ -57,14 +74,14 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
57
74
  }
58
75
 
59
76
  constructor(
60
- readonly modelId: GoogleGenerativeAIImageModelId,
61
- private readonly settings: GoogleGenerativeAIImageSettings,
62
- private readonly config: GoogleGenerativeAIImageModelConfig,
77
+ readonly modelId: GoogleImageModelId,
78
+ private readonly settings: GoogleImageSettings,
79
+ private readonly config: GoogleImageModelConfig,
63
80
  ) {}
64
81
 
65
82
  async doGenerate(
66
- options: Parameters<ImageModelV3['doGenerate']>[0],
67
- ): Promise<Awaited<ReturnType<ImageModelV3['doGenerate']>>> {
83
+ options: Parameters<ImageModelV4['doGenerate']>[0],
84
+ ): Promise<Awaited<ReturnType<ImageModelV4['doGenerate']>>> {
68
85
  // Gemini image models use the language model API internally
69
86
  if (isGeminiModel(this.modelId)) {
70
87
  return this.doGenerateGemini(options);
@@ -73,8 +90,8 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
73
90
  }
74
91
 
75
92
  private async doGenerateImagen(
76
- options: Parameters<ImageModelV3['doGenerate']>[0],
77
- ): Promise<Awaited<ReturnType<ImageModelV3['doGenerate']>>> {
93
+ options: Parameters<ImageModelV4['doGenerate']>[0],
94
+ ): Promise<Awaited<ReturnType<ImageModelV4['doGenerate']>>> {
78
95
  const {
79
96
  prompt,
80
97
  n = 1,
@@ -87,19 +104,19 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
87
104
  files,
88
105
  mask,
89
106
  } = options;
90
- const warnings: Array<SharedV3Warning> = [];
107
+ const warnings: Array<SharedV4Warning> = [];
91
108
 
92
109
  // Imagen API endpoints do not support image editing
93
110
  if (files != null && files.length > 0) {
94
111
  throw new Error(
95
- 'Google Generative AI does not support image editing with Imagen models. ' +
112
+ 'Google Gemini API does not support image editing with Imagen models. ' +
96
113
  'Use Google Vertex AI (@ai-sdk/google-vertex) for image editing capabilities.',
97
114
  );
98
115
  }
99
116
 
100
117
  if (mask != null) {
101
118
  throw new Error(
102
- 'Google Generative AI does not support image editing with masks. ' +
119
+ 'Google Gemini API does not support image editing with masks. ' +
103
120
  'Use Google Vertex AI (@ai-sdk/google-vertex) for image editing capabilities.',
104
121
  );
105
122
  }
@@ -151,7 +168,10 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
151
168
  predictions: Array<{ bytesBase64Encoded: string }>;
152
169
  }>({
153
170
  url: `${this.config.baseURL}/models/${this.modelId}:predict`,
154
- headers: combineHeaders(await resolve(this.config.headers), headers),
171
+ headers: combineHeaders(
172
+ this.config.headers ? await resolve(this.config.headers) : undefined,
173
+ headers,
174
+ ),
155
175
  body,
156
176
  failedResponseHandler: googleFailedResponseHandler,
157
177
  successfulResponseHandler: createJsonResponseHandler(
@@ -181,8 +201,8 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
181
201
  }
182
202
 
183
203
  private async doGenerateGemini(
184
- options: Parameters<ImageModelV3['doGenerate']>[0],
185
- ): Promise<Awaited<ReturnType<ImageModelV3['doGenerate']>>> {
204
+ options: Parameters<ImageModelV4['doGenerate']>[0],
205
+ ): Promise<Awaited<ReturnType<ImageModelV4['doGenerate']>>> {
186
206
  const {
187
207
  prompt,
188
208
  n,
@@ -195,7 +215,7 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
195
215
  files,
196
216
  mask,
197
217
  } = options;
198
- const warnings: Array<SharedV3Warning> = [];
218
+ const warnings: Array<SharedV4Warning> = [];
199
219
 
200
220
  // Gemini does not support mask-based inpainting
201
221
  if (mask != null) {
@@ -220,45 +240,51 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
220
240
  });
221
241
  }
222
242
 
223
- // Build user message content for language model
224
243
  const userContent: Array<
225
244
  | { type: 'text'; text: string }
226
- | { type: 'file'; data: string | Uint8Array | URL; mediaType: string }
245
+ | {
246
+ type: 'file';
247
+ data:
248
+ | { type: 'data'; data: string | Uint8Array }
249
+ | { type: 'url'; url: URL };
250
+ mediaType: string;
251
+ }
227
252
  > = [];
228
253
 
229
- // Add text prompt
230
254
  if (prompt != null) {
231
255
  userContent.push({ type: 'text', text: prompt });
232
256
  }
233
257
 
234
- // Add input images for editing
235
258
  if (files != null && files.length > 0) {
236
259
  for (const file of files) {
237
260
  if (file.type === 'url') {
238
261
  userContent.push({
239
262
  type: 'file',
240
- data: new URL(file.url),
263
+ data: { type: 'url', url: new URL(file.url) },
241
264
  mediaType: 'image/*',
242
265
  });
243
266
  } else {
244
267
  userContent.push({
245
268
  type: 'file',
246
- data:
247
- typeof file.data === 'string'
248
- ? file.data
249
- : new Uint8Array(file.data),
269
+ data: {
270
+ type: 'data',
271
+ data:
272
+ typeof file.data === 'string'
273
+ ? file.data
274
+ : new Uint8Array(file.data),
275
+ },
250
276
  mediaType: file.mediaType,
251
277
  });
252
278
  }
253
279
  }
254
280
  }
255
281
 
256
- const languageModelPrompt: LanguageModelV3Prompt = [
282
+ const languageModelPrompt: LanguageModelV4Prompt = [
257
283
  { role: 'user', content: userContent },
258
284
  ];
259
285
 
260
286
  // Instantiate language model
261
- const languageModel = new GoogleGenerativeAILanguageModel(this.modelId, {
287
+ const languageModel = new GoogleLanguageModel(this.modelId, {
262
288
  provider: this.config.provider,
263
289
  baseURL: this.config.baseURL,
264
290
  headers: this.config.headers ?? {},
@@ -292,11 +318,14 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
292
318
 
293
319
  const currentDate = this.config._internal?.currentDate?.() ?? new Date();
294
320
 
295
- // Extract images from language model response
296
321
  const images: string[] = [];
297
322
  for (const part of result.content) {
298
- if (part.type === 'file' && part.mediaType.startsWith('image/')) {
299
- images.push(convertToBase64(part.data));
323
+ if (
324
+ part.type === 'file' &&
325
+ part.mediaType.startsWith('image/') &&
326
+ part.data.type === 'data'
327
+ ) {
328
+ images.push(convertToBase64(part.data.data));
300
329
  }
301
330
  }
302
331
 
@@ -340,20 +369,3 @@ const googleImageResponseSchema = lazySchema(() =>
340
369
  }),
341
370
  ),
342
371
  );
343
-
344
- // Note: For the initial GA launch of Imagen 3, safety filters are not configurable.
345
- // https://ai.google.dev/gemini-api/docs/imagen#imagen-model
346
- const googleImageModelOptionsSchema = lazySchema(() =>
347
- zodSchema(
348
- z.object({
349
- personGeneration: z
350
- .enum(['dont_allow', 'allow_adult', 'allow_all'])
351
- .nullish(),
352
- aspectRatio: z.enum(['1:1', '3:4', '4:3', '9:16', '16:9']).nullish(),
353
- }),
354
- ),
355
- );
356
-
357
- export type GoogleImageModelOptions = InferSchema<
358
- typeof googleImageModelOptionsSchema
359
- >;
@@ -1,4 +1,4 @@
1
- export type GoogleGenerativeAIImageModelId =
1
+ export type GoogleImageModelId =
2
2
  // Imagen models (use :predict API)
3
3
  | 'imagen-4.0-generate-001'
4
4
  | 'imagen-4.0-ultra-generate-001'
@@ -9,7 +9,7 @@ export type GoogleGenerativeAIImageModelId =
9
9
  | 'gemini-3.1-flash-image-preview'
10
10
  | (string & {});
11
11
 
12
- export interface GoogleGenerativeAIImageSettings {
12
+ export interface GoogleImageSettings {
13
13
  /**
14
14
  * Override the maximum number of images per call (default 4)
15
15
  */