@ai-sdk/google 4.0.0-beta.7 → 4.0.0-beta.82

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 (71) hide show
  1. package/CHANGELOG.md +614 -5
  2. package/README.md +6 -4
  3. package/dist/index.d.ts +301 -50
  4. package/dist/index.js +5410 -639
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +100 -26
  7. package/dist/internal/index.js +1653 -451
  8. package/dist/internal/index.js.map +1 -1
  9. package/docs/{15-google-generative-ai.mdx → 15-google.mdx} +784 -69
  10. package/package.json +16 -17
  11. package/src/{convert-google-generative-ai-usage.ts → convert-google-usage.ts} +13 -5
  12. package/src/convert-json-schema-to-openapi-schema.ts +1 -1
  13. package/src/convert-to-google-messages.ts +647 -0
  14. package/src/{google-generative-ai-embedding-options.ts → google-embedding-model-options.ts} +9 -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 +35 -0
  19. package/src/{google-generative-ai-image-model.ts → google-image-model.ts} +116 -65
  20. package/src/{google-generative-ai-image-settings.ts → google-image-settings.ts} +2 -2
  21. package/src/google-json-accumulator.ts +371 -0
  22. package/src/{google-generative-ai-options.ts → google-language-model-options.ts} +50 -5
  23. package/src/{google-generative-ai-language-model.ts → google-language-model.ts} +701 -219
  24. package/src/google-prepare-tools.ts +72 -12
  25. package/src/google-prompt.ts +86 -0
  26. package/src/google-provider.ts +157 -53
  27. package/src/google-speech-api.ts +36 -0
  28. package/src/google-speech-model-options.ts +48 -0
  29. package/src/google-speech-model.ts +311 -0
  30. package/src/google-video-model-options.ts +43 -0
  31. package/src/{google-generative-ai-video-model.ts → google-video-model.ts} +25 -60
  32. package/src/{google-generative-ai-video-settings.ts → google-video-settings.ts} +2 -1
  33. package/src/index.ts +40 -9
  34. package/src/interactions/build-google-interactions-stream-transform.ts +818 -0
  35. package/src/interactions/cancel-google-interaction.ts +60 -0
  36. package/src/interactions/convert-google-interactions-usage.ts +47 -0
  37. package/src/interactions/convert-to-google-interactions-input.ts +557 -0
  38. package/src/interactions/extract-google-interactions-sources.ts +252 -0
  39. package/src/interactions/google-interactions-agent.ts +15 -0
  40. package/src/interactions/google-interactions-api.ts +530 -0
  41. package/src/interactions/google-interactions-language-model-options.ts +262 -0
  42. package/src/interactions/google-interactions-language-model.ts +776 -0
  43. package/src/interactions/google-interactions-prompt.ts +582 -0
  44. package/src/interactions/google-interactions-provider-metadata.ts +23 -0
  45. package/src/interactions/map-google-interactions-finish-reason.ts +31 -0
  46. package/src/interactions/parse-google-interactions-outputs.ts +252 -0
  47. package/src/interactions/poll-google-interactions.ts +129 -0
  48. package/src/interactions/prepare-google-interactions-tools.ts +245 -0
  49. package/src/interactions/stream-google-interactions.ts +242 -0
  50. package/src/interactions/synthesize-google-interactions-agent-stream.ts +185 -0
  51. package/src/internal/index.ts +3 -2
  52. package/src/{map-google-generative-ai-finish-reason.ts → map-google-finish-reason.ts} +3 -3
  53. package/src/realtime/google-realtime-event-mapper.ts +383 -0
  54. package/src/realtime/google-realtime-model-options.ts +3 -0
  55. package/src/realtime/google-realtime-model.ts +160 -0
  56. package/src/realtime/index.ts +2 -0
  57. package/src/tool/code-execution.ts +2 -2
  58. package/src/tool/enterprise-web-search.ts +9 -3
  59. package/src/tool/file-search.ts +5 -7
  60. package/src/tool/google-maps.ts +3 -2
  61. package/src/tool/google-search.ts +11 -12
  62. package/src/tool/url-context.ts +4 -2
  63. package/src/tool/vertex-rag-store.ts +9 -6
  64. package/dist/index.d.mts +0 -376
  65. package/dist/index.mjs +0 -2517
  66. package/dist/index.mjs.map +0 -1
  67. package/dist/internal/index.d.mts +0 -284
  68. package/dist/internal/index.mjs +0 -1706
  69. package/dist/internal/index.mjs.map +0 -1
  70. package/src/convert-to-google-generative-ai-messages.ts +0 -239
  71. package/src/google-generative-ai-prompt.ts +0 -38
@@ -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
  */
@@ -0,0 +1,371 @@
1
+ export type PartialArg = {
2
+ jsonPath: string;
3
+ stringValue?: string | null;
4
+ numberValue?: number | null;
5
+ boolValue?: boolean | null;
6
+ nullValue?: unknown;
7
+ willContinue?: boolean | null;
8
+ };
9
+
10
+ type PathSegment = string | number;
11
+
12
+ type StackEntry = {
13
+ segment: PathSegment;
14
+ isArray: boolean;
15
+ childCount: number;
16
+ };
17
+
18
+ /**
19
+ * Incrementally builds a JSON object from Google's streaming `partialArgs`
20
+ * chunks emitted during tool-call function calling. Tracks both the structured
21
+ * object and a running JSON text representation so callers can emit text deltas
22
+ * that, when concatenated, form valid nested JSON matching JSON.stringify output.
23
+ *
24
+ * Input: [{jsonPath:"$.location",stringValue:"Boston"}]
25
+ * Output: '{"location":"Boston"', then finalize() → closingDelta='}'
26
+ */
27
+ export class GoogleJSONAccumulator {
28
+ private accumulatedArgs: Record<string, unknown> = {};
29
+ private jsonText = '';
30
+
31
+ /**
32
+ * Stack representing the currently "open" containers in the JSON output.
33
+ * Entry 0 is always the root `{` object once the first value is written.
34
+ */
35
+ private pathStack: StackEntry[] = [];
36
+
37
+ /**
38
+ * Whether a string value is currently "open" (willContinue was true),
39
+ * meaning the closing quote has not yet been emitted.
40
+ */
41
+ private stringOpen = false;
42
+
43
+ /**
44
+ * Input: [{jsonPath:"$.brightness",numberValue:50}]
45
+ * Output: { currentJSON:{brightness:50}, textDelta:'{"brightness":50' }
46
+ */
47
+ processPartialArgs(partialArgs: PartialArg[]): {
48
+ currentJSON: Record<string, unknown>;
49
+ textDelta: string;
50
+ } {
51
+ let delta = '';
52
+
53
+ for (const arg of partialArgs) {
54
+ const rawPath = arg.jsonPath.replace(/^\$\./, '');
55
+ if (!rawPath) continue;
56
+
57
+ const segments = parsePath(rawPath);
58
+
59
+ const existingValue = getNestedValue(this.accumulatedArgs, segments);
60
+ const isStringContinuation =
61
+ arg.stringValue != null && existingValue !== undefined;
62
+
63
+ if (isStringContinuation) {
64
+ const escaped = JSON.stringify(arg.stringValue).slice(1, -1);
65
+ setNestedValue(
66
+ this.accumulatedArgs,
67
+ segments,
68
+ (existingValue as string) + arg.stringValue,
69
+ );
70
+ delta += escaped;
71
+ continue;
72
+ }
73
+
74
+ const resolved = resolvePartialArgValue(arg);
75
+ if (resolved == null) continue;
76
+
77
+ setNestedValue(this.accumulatedArgs, segments, resolved.value);
78
+ delta += this.emitNavigationTo(segments, arg, resolved.json);
79
+ }
80
+
81
+ this.jsonText += delta;
82
+
83
+ return {
84
+ currentJSON: this.accumulatedArgs,
85
+ textDelta: delta,
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Input: jsonText='{"brightness":50', accumulatedArgs={brightness:50}
91
+ * Output: { finalJSON:'{"brightness":50}', closingDelta:'}' }
92
+ */
93
+ finalize(): { finalJSON: string; closingDelta: string } {
94
+ const finalArgs = JSON.stringify(this.accumulatedArgs);
95
+ const closingDelta = finalArgs.slice(this.jsonText.length);
96
+ return { finalJSON: finalArgs, closingDelta };
97
+ }
98
+
99
+ /**
100
+ * Input: pathStack=[] (first call) or pathStack=[root,...] (subsequent calls)
101
+ * Output: '{' (first call) or '' (subsequent calls)
102
+ */
103
+ private ensureRoot(): string {
104
+ if (this.pathStack.length === 0) {
105
+ this.pathStack.push({ segment: '', isArray: false, childCount: 0 });
106
+ return '{';
107
+ }
108
+ return '';
109
+ }
110
+
111
+ /**
112
+ * Emits the JSON text fragment needed to navigate from the current open
113
+ * path to the new leaf at `targetSegments`, then writes the value.
114
+ *
115
+ * Input: targetSegments=["recipe","name"], arg={jsonPath:"$.recipe.name",stringValue:"Lasagna"}, valueJson='"Lasagna"'
116
+ * Output: '{"recipe":{"name":"Lasagna"'
117
+ */
118
+ private emitNavigationTo(
119
+ targetSegments: PathSegment[],
120
+ arg: PartialArg,
121
+ valueJson: string,
122
+ ): string {
123
+ let fragment = '';
124
+
125
+ if (this.stringOpen) {
126
+ fragment += '"';
127
+ this.stringOpen = false;
128
+ }
129
+
130
+ fragment += this.ensureRoot();
131
+
132
+ const targetContainerSegments = targetSegments.slice(0, -1);
133
+ const leafSegment = targetSegments[targetSegments.length - 1];
134
+
135
+ const commonDepth = this.findCommonStackDepth(targetContainerSegments);
136
+
137
+ fragment += this.closeDownTo(commonDepth);
138
+ fragment += this.openDownTo(targetContainerSegments, leafSegment);
139
+ fragment += this.emitLeaf(leafSegment, arg, valueJson);
140
+
141
+ return fragment;
142
+ }
143
+
144
+ /**
145
+ * Returns the stack depth to preserve when navigating to a new target
146
+ * container path. Always >= 1 (the root is never popped).
147
+ *
148
+ * Input: stack=[root,"recipe","ingredients",0], target=["recipe","ingredients",1]
149
+ * Output: 3 (keep root+"recipe"+"ingredients")
150
+ */
151
+ private findCommonStackDepth(targetContainer: PathSegment[]): number {
152
+ const maxDepth = Math.min(
153
+ this.pathStack.length - 1,
154
+ targetContainer.length,
155
+ );
156
+ let common = 0;
157
+ for (let i = 0; i < maxDepth; i++) {
158
+ if (this.pathStack[i + 1].segment === targetContainer[i]) {
159
+ common++;
160
+ } else {
161
+ break;
162
+ }
163
+ }
164
+ return common + 1;
165
+ }
166
+
167
+ /**
168
+ * Closes containers from the current stack depth back down to `targetDepth`.
169
+ *
170
+ * Input: this.pathStack=[root,"recipe","ingredients",0], targetDepth=3
171
+ * Output: '}'
172
+ */
173
+ private closeDownTo(targetDepth: number): string {
174
+ let fragment = '';
175
+ while (this.pathStack.length > targetDepth) {
176
+ const entry = this.pathStack.pop()!;
177
+ fragment += entry.isArray ? ']' : '}';
178
+ }
179
+ return fragment;
180
+ }
181
+
182
+ /**
183
+ * Opens containers from the current stack depth down to the full target
184
+ * container path, emitting opening `{`, `[`, keys, and commas as needed.
185
+ * `leafSegment` is used to determine if the innermost container is an array.
186
+ *
187
+ * Input: this.pathStack=[root], targetContainer=["recipe","ingredients"], leafSegment=0
188
+ * Output: '"recipe":{"ingredients":['
189
+ */
190
+ private openDownTo(
191
+ targetContainer: PathSegment[],
192
+ leafSegment: PathSegment,
193
+ ): string {
194
+ let fragment = '';
195
+
196
+ const startIdx = this.pathStack.length - 1;
197
+
198
+ for (let i = startIdx; i < targetContainer.length; i++) {
199
+ const pathSegment = targetContainer[i];
200
+ const parentEntry = this.pathStack[this.pathStack.length - 1];
201
+
202
+ if (parentEntry.childCount > 0) {
203
+ fragment += ',';
204
+ }
205
+ parentEntry.childCount++;
206
+
207
+ if (typeof pathSegment === 'string') {
208
+ fragment += `${JSON.stringify(pathSegment)}:`;
209
+ }
210
+
211
+ const childSeg =
212
+ i + 1 < targetContainer.length ? targetContainer[i + 1] : leafSegment;
213
+ const isArray = typeof childSeg === 'number';
214
+
215
+ fragment += isArray ? '[' : '{';
216
+
217
+ this.pathStack.push({ segment: pathSegment, isArray, childCount: 0 });
218
+ }
219
+
220
+ return fragment;
221
+ }
222
+
223
+ /**
224
+ * Emits the comma, key, and value for a leaf entry in the current container.
225
+ *
226
+ * Input: leafSegment="name", arg={stringValue:"Lasagna"}, valueJson='"Lasagna"'
227
+ * Output: '"name":"Lasagna"' (or ',"name":"Lasagna"' if container.childCount > 0)
228
+ */
229
+ private emitLeaf(
230
+ leafSegment: PathSegment,
231
+ arg: PartialArg,
232
+ valueJson: string,
233
+ ): string {
234
+ let fragment = '';
235
+ const container = this.pathStack[this.pathStack.length - 1];
236
+
237
+ if (container.childCount > 0) {
238
+ fragment += ',';
239
+ }
240
+ container.childCount++;
241
+
242
+ if (typeof leafSegment === 'string') {
243
+ fragment += `${JSON.stringify(leafSegment)}:`;
244
+ }
245
+
246
+ if (arg.stringValue != null && arg.willContinue) {
247
+ fragment += valueJson.slice(0, -1);
248
+ this.stringOpen = true;
249
+ } else {
250
+ fragment += valueJson;
251
+ }
252
+
253
+ return fragment;
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Splits a dotted/bracketed JSON path like `recipe.ingredients[0].name` into segments.
259
+ *
260
+ * Input: "recipe.ingredients[0].name"
261
+ * Output: ["recipe", "ingredients", 0, "name"]
262
+ */
263
+ function parsePath(rawPath: string): Array<string | number> {
264
+ const segments: Array<string | number> = [];
265
+ for (const part of rawPath.split('.')) {
266
+ const bracketIdx = part.indexOf('[');
267
+ if (bracketIdx === -1) {
268
+ segments.push(part);
269
+ } else {
270
+ if (bracketIdx > 0) segments.push(part.slice(0, bracketIdx));
271
+ for (const m of part.matchAll(/\[(\d+)\]/g)) {
272
+ segments.push(parseInt(m[1], 10));
273
+ }
274
+ }
275
+ }
276
+ return segments;
277
+ }
278
+
279
+ const hasOwn = Object.prototype.hasOwnProperty;
280
+
281
+ /**
282
+ * Checks only direct properties so path traversal never follows the prototype chain.
283
+ */
284
+ function hasOwnProperty(
285
+ obj: Record<string | number, unknown>,
286
+ key: string | number,
287
+ ): boolean {
288
+ return hasOwn.call(obj, key);
289
+ }
290
+
291
+ /**
292
+ * Defines path values as own data properties so special keys like `__proto__`
293
+ * cannot invoke prototype setters while accumulating streamed arguments.
294
+ */
295
+ function defineOwnProperty(
296
+ obj: Record<string | number, unknown>,
297
+ key: string | number,
298
+ value: unknown,
299
+ ): void {
300
+ Object.defineProperty(obj, key, {
301
+ value,
302
+ enumerable: true,
303
+ configurable: true,
304
+ writable: true,
305
+ });
306
+ }
307
+
308
+ /**
309
+ * Traverses a nested object along the given path segments and returns the leaf value.
310
+ *
311
+ * Input: ({recipe:{name:"Lasagna"}}, ["recipe","name"])
312
+ * Output: "Lasagna"
313
+ */
314
+ function getNestedValue(
315
+ obj: Record<string, unknown>,
316
+ segments: Array<string | number>,
317
+ ): unknown {
318
+ let current: unknown = obj;
319
+ for (const pathSegment of segments) {
320
+ if (current == null || typeof current !== 'object') return undefined;
321
+ const currentRecord = current as Record<string | number, unknown>;
322
+ if (!hasOwnProperty(currentRecord, pathSegment)) return undefined;
323
+ current = currentRecord[pathSegment];
324
+ }
325
+ return current;
326
+ }
327
+
328
+ /**
329
+ * Sets a value at a nested path, creating intermediate objects or arrays as needed.
330
+ *
331
+ * Input: obj={}, segments=["recipe","ingredients",0,"name"], value="Noodles"
332
+ * Output: {recipe:{ingredients:[{name:"Noodles"}]}}
333
+ */
334
+ function setNestedValue(
335
+ obj: Record<string, unknown>,
336
+ segments: Array<string | number>,
337
+ value: unknown,
338
+ ): void {
339
+ let current: Record<string | number, unknown> = obj;
340
+ for (let i = 0; i < segments.length - 1; i++) {
341
+ const pathSegment = segments[i];
342
+ const nextSeg = segments[i + 1];
343
+ if (!hasOwnProperty(current, pathSegment) || current[pathSegment] == null) {
344
+ defineOwnProperty(
345
+ current,
346
+ pathSegment,
347
+ typeof nextSeg === 'number' ? [] : {},
348
+ );
349
+ }
350
+ current = current[pathSegment] as Record<string | number, unknown>;
351
+ }
352
+ defineOwnProperty(current, segments[segments.length - 1], value);
353
+ }
354
+
355
+ /**
356
+ * Extracts the first non-null typed value from a partial arg and returns it with its JSON representation.
357
+ *
358
+ * Input: arg={stringValue:"Boston"} or arg={numberValue:50}
359
+ * Output: {value:"Boston", json:'"Boston"'} or {value:50, json:'50'}
360
+ */
361
+ function resolvePartialArgValue(arg: {
362
+ stringValue?: string | null;
363
+ numberValue?: number | null;
364
+ boolValue?: boolean | null;
365
+ nullValue?: unknown;
366
+ }): { value: unknown; json: string } | undefined {
367
+ const value = arg.stringValue ?? arg.numberValue ?? arg.boolValue;
368
+ if (value != null) return { value, json: JSON.stringify(value) };
369
+ if ('nullValue' in arg) return { value: null, json: 'null' };
370
+ return undefined;
371
+ }
@@ -1,19 +1,21 @@
1
- import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
2
6
  import { z } from 'zod/v4';
3
7
 
4
- export type GoogleGenerativeAIModelId =
8
+ export type GoogleModelId =
5
9
  // Stable models
6
10
  // https://ai.google.dev/gemini-api/docs/models/gemini
7
11
  | 'gemini-2.0-flash'
8
12
  | 'gemini-2.0-flash-001'
9
13
  | 'gemini-2.0-flash-lite'
10
- | 'gemini-2.0-flash-exp-image-generation'
11
14
  | 'gemini-2.0-flash-lite-001'
12
15
  | 'gemini-2.5-pro'
13
16
  | 'gemini-2.5-flash'
14
17
  | 'gemini-2.5-flash-image'
15
18
  | 'gemini-2.5-flash-lite'
16
- | 'gemini-2.5-flash-lite-preview-09-2025'
17
19
  | 'gemini-2.5-flash-preview-tts'
18
20
  | 'gemini-2.5-pro-preview-tts'
19
21
  | 'gemini-2.5-flash-native-audio-latest'
@@ -27,12 +29,16 @@ export type GoogleGenerativeAIModelId =
27
29
  | 'gemini-3.1-pro-preview-customtools'
28
30
  | 'gemini-3.1-flash-image-preview'
29
31
  | 'gemini-3.1-flash-lite-preview'
32
+ | 'gemini-3.1-flash-tts-preview'
33
+ | 'gemini-3.5-flash'
30
34
  // latest version
31
35
  // https://ai.google.dev/gemini-api/docs/models#latest
32
36
  | 'gemini-pro-latest'
33
37
  | 'gemini-flash-latest'
34
38
  | 'gemini-flash-lite-latest'
35
39
  | 'deep-research-pro-preview-12-2025'
40
+ | 'deep-research-max-preview-04-2026'
41
+ | 'deep-research-preview-04-2026'
36
42
  | 'nano-banana-pro-preview'
37
43
  | 'aqa'
38
44
  // Experimental models
@@ -74,7 +80,7 @@ export const googleLanguageModelOptions = lazySchema(() =>
74
80
  *
75
81
  * This is useful when the JSON Schema contains elements that are
76
82
  * not supported by the OpenAPI schema version that
77
- * Google Generative AI uses. You can use this to disable
83
+ * Google uses. You can use this to disable
78
84
  * structured outputs if you need to.
79
85
  */
80
86
  structuredOutputs: z.boolean().optional(),
@@ -189,6 +195,45 @@ export const googleLanguageModelOptions = lazySchema(() =>
189
195
  .optional(),
190
196
  })
191
197
  .optional(),
198
+
199
+ /**
200
+ * Optional. When set to true, function call arguments will be streamed
201
+ * incrementally via partialArgs in streaming responses. Only supported
202
+ * on the Vertex AI API (not the Gemini API) and only for Gemini 3+
203
+ * models.
204
+ *
205
+ * @default false
206
+ *
207
+ * https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#streaming-fc
208
+ */
209
+ streamFunctionCallArguments: z.boolean().optional(),
210
+
211
+ /**
212
+ * Optional. The service tier to use for the request. Sent as the
213
+ * `serviceTier` body field. Gemini API only.
214
+ */
215
+ serviceTier: z.enum(['standard', 'flex', 'priority']).optional(),
216
+
217
+ /**
218
+ * Optional. Vertex AI only. Sent as the
219
+ * `X-Vertex-AI-LLM-Shared-Request-Type` request header to select a
220
+ * shared (PayGo) tier. With Provisioned Throughput allocated and
221
+ * `requestType` unset, the request falls back to this tier only if
222
+ * PT capacity is exhausted.
223
+ *
224
+ * https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo
225
+ * https://docs.cloud.google.com/vertex-ai/generative-ai/docs/flex-paygo
226
+ */
227
+ sharedRequestType: z.enum(['priority', 'flex', 'standard']).optional(),
228
+
229
+ /**
230
+ * Optional. Vertex AI only. Sent as the `X-Vertex-AI-LLM-Request-Type`
231
+ * request header. Set to `'shared'` together with `sharedRequestType`
232
+ * to bypass Provisioned Throughput entirely.
233
+ *
234
+ * https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo
235
+ */
236
+ requestType: z.enum(['shared']).optional(),
192
237
  }),
193
238
  ),
194
239
  );