@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,72 +1,95 @@
1
- import {
2
- LanguageModelV3,
3
- LanguageModelV3CallOptions,
4
- LanguageModelV3Content,
5
- LanguageModelV3FinishReason,
6
- LanguageModelV3GenerateResult,
7
- LanguageModelV3Source,
8
- LanguageModelV3StreamPart,
9
- LanguageModelV3StreamResult,
10
- SharedV3ProviderMetadata,
11
- SharedV3Warning,
1
+ import type {
2
+ LanguageModelV4,
3
+ LanguageModelV4CallOptions,
4
+ LanguageModelV4Content,
5
+ LanguageModelV4FinishReason,
6
+ LanguageModelV4GenerateResult,
7
+ LanguageModelV4Source,
8
+ LanguageModelV4StreamPart,
9
+ LanguageModelV4StreamResult,
10
+ JSONObject,
11
+ SharedV4ProviderMetadata,
12
+ SharedV4Warning,
12
13
  } from '@ai-sdk/provider';
13
14
  import {
14
15
  combineHeaders,
15
16
  createEventSourceResponseHandler,
16
17
  createJsonResponseHandler,
17
- FetchFunction,
18
18
  generateId,
19
- InferSchema,
19
+ isCustomReasoning,
20
20
  lazySchema,
21
+ mapReasoningToProviderBudget,
22
+ mapReasoningToProviderEffort,
21
23
  parseProviderOptions,
22
- ParseResult,
23
24
  postJsonToApi,
24
- Resolvable,
25
25
  resolve,
26
+ serializeModelOptions,
27
+ WORKFLOW_SERIALIZE,
28
+ WORKFLOW_DESERIALIZE,
26
29
  zodSchema,
30
+ type FetchFunction,
31
+ type InferSchema,
32
+ type ParseResult,
33
+ type Resolvable,
27
34
  } from '@ai-sdk/provider-utils';
28
35
  import { z } from 'zod/v4';
29
36
  import {
30
- convertGoogleGenerativeAIUsage,
31
- GoogleGenerativeAIUsageMetadata,
32
- } from './convert-google-generative-ai-usage';
37
+ convertGoogleUsage,
38
+ type GoogleUsageMetadata,
39
+ } from './convert-google-usage';
33
40
  import { convertJSONSchemaToOpenAPISchema } from './convert-json-schema-to-openapi-schema';
34
- import { convertToGoogleGenerativeAIMessages } from './convert-to-google-generative-ai-messages';
41
+ import { convertToGoogleMessages } from './convert-to-google-messages';
35
42
  import { getModelPath } from './get-model-path';
36
43
  import { googleFailedResponseHandler } from './google-error';
37
44
  import {
38
- GoogleGenerativeAIModelId,
39
45
  googleLanguageModelOptions,
40
- } from './google-generative-ai-options';
41
- import { GoogleGenerativeAIContentPart } from './google-generative-ai-prompt';
46
+ type GoogleLanguageModelOptions,
47
+ type GoogleModelId,
48
+ } from './google-language-model-options';
49
+ import type { GoogleProviderMetadata } from './google-prompt';
42
50
  import { prepareTools } from './google-prepare-tools';
43
- import { mapGoogleGenerativeAIFinishReason } from './map-google-generative-ai-finish-reason';
51
+ import {
52
+ GoogleJSONAccumulator,
53
+ type PartialArg,
54
+ } from './google-json-accumulator';
55
+ import { mapGoogleFinishReason } from './map-google-finish-reason';
44
56
 
45
- type GoogleGenerativeAIConfig = {
57
+ type GoogleConfig = {
46
58
  provider: string;
47
59
  baseURL: string;
48
- headers: Resolvable<Record<string, string | undefined>>;
60
+ headers?: Resolvable<Record<string, string | undefined>>;
49
61
  fetch?: FetchFunction;
50
62
  generateId: () => string;
51
63
 
52
64
  /**
53
65
  * The supported URLs for the model.
54
66
  */
55
- supportedUrls?: () => LanguageModelV3['supportedUrls'];
67
+ supportedUrls?: () => LanguageModelV4['supportedUrls'];
56
68
  };
57
69
 
58
- export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
59
- readonly specificationVersion = 'v3';
70
+ export class GoogleLanguageModel implements LanguageModelV4 {
71
+ readonly specificationVersion = 'v4';
60
72
 
61
- readonly modelId: GoogleGenerativeAIModelId;
73
+ readonly modelId: GoogleModelId;
62
74
 
63
- private readonly config: GoogleGenerativeAIConfig;
75
+ private readonly config: GoogleConfig;
64
76
  private readonly generateId: () => string;
65
77
 
66
- constructor(
67
- modelId: GoogleGenerativeAIModelId,
68
- config: GoogleGenerativeAIConfig,
69
- ) {
78
+ static [WORKFLOW_SERIALIZE](model: GoogleLanguageModel) {
79
+ return serializeModelOptions({
80
+ modelId: model.modelId,
81
+ config: model.config,
82
+ });
83
+ }
84
+
85
+ static [WORKFLOW_DESERIALIZE](options: {
86
+ modelId: string;
87
+ config: GoogleConfig;
88
+ }) {
89
+ return new GoogleLanguageModel(options.modelId, options.config);
90
+ }
91
+
92
+ constructor(modelId: GoogleModelId, config: GoogleConfig) {
70
93
  this.modelId = modelId;
71
94
  this.config = config;
72
95
  this.generateId = config.generateId ?? generateId;
@@ -80,33 +103,49 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
80
103
  return this.config.supportedUrls?.() ?? {};
81
104
  }
82
105
 
83
- private async getArgs({
84
- prompt,
85
- maxOutputTokens,
86
- temperature,
87
- topP,
88
- topK,
89
- frequencyPenalty,
90
- presencePenalty,
91
- stopSequences,
92
- responseFormat,
93
- seed,
94
- tools,
95
- toolChoice,
96
- providerOptions,
97
- }: LanguageModelV3CallOptions) {
98
- const warnings: SharedV3Warning[] = [];
99
-
100
- const providerOptionsName = this.config.provider.includes('vertex')
101
- ? 'vertex'
102
- : 'google';
103
- let googleOptions = await parseProviderOptions({
104
- provider: providerOptionsName,
106
+ private async getArgs(
107
+ {
108
+ prompt,
109
+ maxOutputTokens,
110
+ temperature,
111
+ topP,
112
+ topK,
113
+ frequencyPenalty,
114
+ presencePenalty,
115
+ stopSequences,
116
+ responseFormat,
117
+ seed,
118
+ tools,
119
+ toolChoice,
120
+ reasoning,
105
121
  providerOptions,
106
- schema: googleLanguageModelOptions,
107
- });
122
+ }: LanguageModelV4CallOptions,
123
+ { isStreaming = false }: { isStreaming?: boolean } = {},
124
+ ) {
125
+ const warnings: SharedV4Warning[] = [];
126
+
127
+ // Names to look up in providerOptions and to write into providerMetadata.
128
+ // For the Vertex provider we read both the new `googleVertex` key and the
129
+ // legacy `vertex` key (new takes precedence) and write under both for
130
+ // backward compatibility. For other Google providers we use just `google`.
131
+ const providerOptionsNames: readonly string[] =
132
+ this.config.provider.includes('vertex')
133
+ ? (['googleVertex', 'vertex'] as const)
134
+ : (['google'] as const);
135
+
136
+ let googleOptions: GoogleLanguageModelOptions | undefined;
137
+ for (const name of providerOptionsNames) {
138
+ googleOptions = await parseProviderOptions({
139
+ provider: name,
140
+ providerOptions,
141
+ schema: googleLanguageModelOptions,
142
+ });
143
+ if (googleOptions != null) break;
144
+ }
108
145
 
109
- if (googleOptions == null && providerOptionsName !== 'google') {
146
+ // Cross-namespace fallback: a Vertex provider may receive options under
147
+ // the `google` key (e.g. via the AI Gateway).
148
+ if (googleOptions == null && !providerOptionsNames.includes('google')) {
110
149
  googleOptions = await parseProviderOptions({
111
150
  provider: 'google',
112
151
  providerOptions,
@@ -115,12 +154,14 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
115
154
  }
116
155
 
117
156
  // Add warning if Vertex rag tools are used with a non-Vertex Google provider
157
+ const isVertexProvider = this.config.provider.startsWith('google.vertex.');
158
+
118
159
  if (
119
160
  tools?.some(
120
161
  tool =>
121
162
  tool.type === 'provider' && tool.id === 'google.vertex_rag_store',
122
163
  ) &&
123
- !this.config.provider.startsWith('google.vertex.')
164
+ !isVertexProvider
124
165
  ) {
125
166
  warnings.push({
126
167
  type: 'other',
@@ -131,12 +172,65 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
131
172
  });
132
173
  }
133
174
 
134
- const isGemmaModel = this.modelId.toLowerCase().startsWith('gemma-');
175
+ if (googleOptions?.streamFunctionCallArguments && !isVertexProvider) {
176
+ warnings.push({
177
+ type: 'other',
178
+ message:
179
+ "'streamFunctionCallArguments' is only supported on the Vertex AI API " +
180
+ 'and will be ignored with the current Google provider ' +
181
+ `(${this.config.provider}). See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#streaming-fc`,
182
+ });
183
+ }
135
184
 
136
- const { contents, systemInstruction } = convertToGoogleGenerativeAIMessages(
137
- prompt,
138
- { isGemmaModel, providerOptionsName },
139
- );
185
+ if (googleOptions?.serviceTier && isVertexProvider) {
186
+ warnings.push({
187
+ type: 'other',
188
+ message:
189
+ "'serviceTier' is a Gemini API option and is not supported on Vertex AI. " +
190
+ "Use 'sharedRequestType' (and optionally 'requestType') instead. See " +
191
+ 'https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo',
192
+ });
193
+ }
194
+ if (
195
+ (googleOptions?.sharedRequestType || googleOptions?.requestType) &&
196
+ !isVertexProvider
197
+ ) {
198
+ warnings.push({
199
+ type: 'other',
200
+ message:
201
+ "'sharedRequestType' and 'requestType' are Vertex AI options and " +
202
+ `are ignored with the current Google provider (${this.config.provider}).`,
203
+ });
204
+ }
205
+
206
+ const vertexPaygoHeaders: Record<string, string> | undefined =
207
+ isVertexProvider &&
208
+ (googleOptions?.sharedRequestType || googleOptions?.requestType)
209
+ ? {
210
+ ...(googleOptions.sharedRequestType && {
211
+ 'X-Vertex-AI-LLM-Shared-Request-Type':
212
+ googleOptions.sharedRequestType,
213
+ }),
214
+ ...(googleOptions.requestType && {
215
+ 'X-Vertex-AI-LLM-Request-Type': googleOptions.requestType,
216
+ }),
217
+ }
218
+ : undefined;
219
+ const bodyServiceTier = isVertexProvider
220
+ ? undefined
221
+ : googleOptions?.serviceTier;
222
+
223
+ const isGemmaModel = this.modelId.toLowerCase().startsWith('gemma-');
224
+ const isGemini3Model = /^gemini-3[.-]/.test(this.modelId);
225
+ const supportsFunctionResponseParts = isGemini3Model;
226
+
227
+ const { contents, systemInstruction } = convertToGoogleMessages(prompt, {
228
+ isGemmaModel,
229
+ isGemini3Model,
230
+ onWarning: warning => warnings.push(warning),
231
+ providerOptionsNames,
232
+ supportsFunctionResponseParts,
233
+ });
140
234
 
141
235
  const {
142
236
  tools: googleTools,
@@ -146,8 +240,42 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
146
240
  tools,
147
241
  toolChoice,
148
242
  modelId: this.modelId,
243
+ isVertexProvider,
149
244
  });
150
245
 
246
+ const resolvedThinking = resolveThinkingConfig({
247
+ reasoning,
248
+ modelId: this.modelId,
249
+ warnings,
250
+ });
251
+ const thinkingConfig =
252
+ googleOptions?.thinkingConfig || resolvedThinking
253
+ ? { ...resolvedThinking, ...googleOptions?.thinkingConfig }
254
+ : undefined;
255
+
256
+ const streamFunctionCallArguments =
257
+ isStreaming && isVertexProvider
258
+ ? (googleOptions?.streamFunctionCallArguments ?? false)
259
+ : undefined;
260
+
261
+ const toolConfig =
262
+ googleToolConfig ||
263
+ streamFunctionCallArguments ||
264
+ googleOptions?.retrievalConfig
265
+ ? {
266
+ ...googleToolConfig,
267
+ ...(streamFunctionCallArguments && {
268
+ functionCallingConfig: {
269
+ ...googleToolConfig?.functionCallingConfig,
270
+ streamFunctionCallArguments: true as const,
271
+ },
272
+ }),
273
+ ...(googleOptions?.retrievalConfig && {
274
+ retrievalConfig: googleOptions.retrievalConfig,
275
+ }),
276
+ }
277
+ : undefined;
278
+
151
279
  return {
152
280
  args: {
153
281
  generationConfig: {
@@ -179,7 +307,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
179
307
 
180
308
  // provider options:
181
309
  responseModalities: googleOptions?.responseModalities,
182
- thinkingConfig: googleOptions?.thinkingConfig,
310
+ thinkingConfig,
183
311
  ...(googleOptions?.mediaResolution && {
184
312
  mediaResolution: googleOptions.mediaResolution,
185
313
  }),
@@ -191,28 +319,31 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
191
319
  systemInstruction: isGemmaModel ? undefined : systemInstruction,
192
320
  safetySettings: googleOptions?.safetySettings,
193
321
  tools: googleTools,
194
- toolConfig: googleOptions?.retrievalConfig
195
- ? {
196
- ...googleToolConfig,
197
- retrievalConfig: googleOptions.retrievalConfig,
198
- }
199
- : googleToolConfig,
322
+ toolConfig,
200
323
  cachedContent: googleOptions?.cachedContent,
201
324
  labels: googleOptions?.labels,
325
+ serviceTier: bodyServiceTier,
202
326
  },
203
327
  warnings: [...warnings, ...toolWarnings],
204
- providerOptionsName,
328
+ providerOptionsNames,
329
+ extraHeaders: vertexPaygoHeaders,
205
330
  };
206
331
  }
207
332
 
208
333
  async doGenerate(
209
- options: LanguageModelV3CallOptions,
210
- ): Promise<LanguageModelV3GenerateResult> {
211
- const { args, warnings, providerOptionsName } = await this.getArgs(options);
334
+ options: LanguageModelV4CallOptions,
335
+ ): Promise<LanguageModelV4GenerateResult> {
336
+ const { args, warnings, providerOptionsNames, extraHeaders } =
337
+ await this.getArgs(options);
338
+ const wrapProviderMetadata = (payload: Record<string, unknown>) =>
339
+ Object.fromEntries(
340
+ providerOptionsNames.map(name => [name, payload]),
341
+ ) as SharedV4ProviderMetadata;
212
342
 
213
343
  const mergedHeaders = combineHeaders(
214
- await resolve(this.config.headers),
344
+ this.config.headers ? await resolve(this.config.headers) : undefined,
215
345
  options.headers,
346
+ extraHeaders,
216
347
  );
217
348
 
218
349
  const {
@@ -232,7 +363,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
232
363
  });
233
364
 
234
365
  const candidate = response.candidates[0];
235
- const content: Array<LanguageModelV3Content> = [];
366
+ const content: Array<LanguageModelV4Content> = [];
236
367
 
237
368
  // map ordered parts to content:
238
369
  const parts = candidate.content?.parts ?? [];
@@ -241,6 +372,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
241
372
 
242
373
  // Associates a code execution result with its preceding call.
243
374
  let lastCodeExecutionToolCallId: string | undefined;
375
+ // Associates a server-side tool response with its preceding call (tool combination).
376
+ let lastServerToolCallId: string | undefined;
244
377
 
245
378
  // Build content array from all parts
246
379
  for (const part of parts) {
@@ -270,11 +403,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
270
403
  lastCodeExecutionToolCallId = undefined;
271
404
  } else if ('text' in part && part.text != null) {
272
405
  const thoughtSignatureMetadata = part.thoughtSignature
273
- ? {
274
- [providerOptionsName]: {
275
- thoughtSignature: part.thoughtSignature,
276
- },
277
- }
406
+ ? wrapProviderMetadata({
407
+ thoughtSignature: part.thoughtSignature,
408
+ })
278
409
  : undefined;
279
410
 
280
411
  if (part.text.length === 0) {
@@ -289,39 +420,74 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
289
420
  providerMetadata: thoughtSignatureMetadata,
290
421
  });
291
422
  }
292
- } else if ('functionCall' in part) {
423
+ } else if ('functionCall' in part && part.functionCall.name != null) {
293
424
  content.push({
294
425
  type: 'tool-call' as const,
295
- toolCallId: this.config.generateId(),
426
+ toolCallId: part.functionCall.id ?? this.config.generateId(),
296
427
  toolName: part.functionCall.name,
297
- input: JSON.stringify(part.functionCall.args),
428
+ input: JSON.stringify(part.functionCall.args ?? {}),
298
429
  providerMetadata: part.thoughtSignature
299
- ? {
300
- [providerOptionsName]: {
301
- thoughtSignature: part.thoughtSignature,
302
- },
303
- }
430
+ ? wrapProviderMetadata({
431
+ thoughtSignature: part.thoughtSignature,
432
+ })
304
433
  : undefined,
305
434
  });
306
435
  } else if ('inlineData' in part) {
307
436
  const hasThought = part.thought === true;
308
437
  const hasThoughtSignature = !!part.thoughtSignature;
309
438
  content.push({
310
- type: 'file' as const,
311
- data: part.inlineData.data,
439
+ type: hasThought ? 'reasoning-file' : 'file',
440
+ data: { type: 'data', data: part.inlineData.data },
312
441
  mediaType: part.inlineData.mimeType,
313
- providerMetadata:
314
- hasThought || hasThoughtSignature
315
- ? {
316
- [providerOptionsName]: {
317
- ...(hasThought ? { thought: true } : {}),
318
- ...(hasThoughtSignature
319
- ? { thoughtSignature: part.thoughtSignature }
320
- : {}),
321
- },
322
- }
323
- : undefined,
442
+ providerMetadata: hasThoughtSignature
443
+ ? wrapProviderMetadata({
444
+ thoughtSignature: part.thoughtSignature,
445
+ })
446
+ : undefined,
324
447
  });
448
+ } else if ('toolCall' in part && part.toolCall) {
449
+ const toolCallId = part.toolCall.id ?? this.config.generateId();
450
+ lastServerToolCallId = toolCallId;
451
+ content.push({
452
+ type: 'tool-call',
453
+ toolCallId,
454
+ toolName: `server:${part.toolCall.toolType}`,
455
+ input: JSON.stringify(part.toolCall.args ?? {}),
456
+ providerExecuted: true,
457
+ dynamic: true,
458
+ providerMetadata: part.thoughtSignature
459
+ ? wrapProviderMetadata({
460
+ thoughtSignature: part.thoughtSignature,
461
+ serverToolCallId: toolCallId,
462
+ serverToolType: part.toolCall.toolType,
463
+ })
464
+ : wrapProviderMetadata({
465
+ serverToolCallId: toolCallId,
466
+ serverToolType: part.toolCall.toolType,
467
+ }),
468
+ });
469
+ } else if ('toolResponse' in part && part.toolResponse) {
470
+ const responseToolCallId =
471
+ lastServerToolCallId ??
472
+ part.toolResponse.id ??
473
+ this.config.generateId();
474
+ content.push({
475
+ type: 'tool-result',
476
+ toolCallId: responseToolCallId,
477
+ toolName: `server:${part.toolResponse.toolType}`,
478
+ result: (part.toolResponse.response ?? {}) as JSONObject,
479
+ providerMetadata: part.thoughtSignature
480
+ ? wrapProviderMetadata({
481
+ thoughtSignature: part.thoughtSignature,
482
+ serverToolCallId: responseToolCallId,
483
+ serverToolType: part.toolResponse.toolType,
484
+ })
485
+ : wrapProviderMetadata({
486
+ serverToolCallId: responseToolCallId,
487
+ serverToolType: part.toolResponse.toolType,
488
+ }),
489
+ });
490
+ lastServerToolCallId = undefined;
325
491
  }
326
492
  }
327
493
 
@@ -337,7 +503,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
337
503
  return {
338
504
  content,
339
505
  finishReason: {
340
- unified: mapGoogleGenerativeAIFinishReason({
506
+ unified: mapGoogleFinishReason({
341
507
  finishReason: candidate.finishReason,
342
508
  // Only count client-executed tool calls for finish reason determination.
343
509
  hasToolCalls: content.some(
@@ -346,17 +512,17 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
346
512
  }),
347
513
  raw: candidate.finishReason ?? undefined,
348
514
  },
349
- usage: convertGoogleGenerativeAIUsage(usageMetadata),
515
+ usage: convertGoogleUsage(usageMetadata),
350
516
  warnings,
351
- providerMetadata: {
352
- [providerOptionsName]: {
353
- promptFeedback: response.promptFeedback ?? null,
354
- groundingMetadata: candidate.groundingMetadata ?? null,
355
- urlContextMetadata: candidate.urlContextMetadata ?? null,
356
- safetyRatings: candidate.safetyRatings ?? null,
357
- usageMetadata: usageMetadata ?? null,
358
- },
359
- },
517
+ providerMetadata: wrapProviderMetadata({
518
+ promptFeedback: response.promptFeedback ?? null,
519
+ groundingMetadata: candidate.groundingMetadata ?? null,
520
+ urlContextMetadata: candidate.urlContextMetadata ?? null,
521
+ safetyRatings: candidate.safetyRatings ?? null,
522
+ usageMetadata: usageMetadata ?? null,
523
+ finishMessage: candidate.finishMessage ?? null,
524
+ serviceTier: usageMetadata?.serviceTier ?? null,
525
+ } satisfies GoogleProviderMetadata),
360
526
  request: { body: args },
361
527
  response: {
362
528
  // TODO timestamp, model id, id
@@ -367,13 +533,19 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
367
533
  }
368
534
 
369
535
  async doStream(
370
- options: LanguageModelV3CallOptions,
371
- ): Promise<LanguageModelV3StreamResult> {
372
- const { args, warnings, providerOptionsName } = await this.getArgs(options);
536
+ options: LanguageModelV4CallOptions,
537
+ ): Promise<LanguageModelV4StreamResult> {
538
+ const { args, warnings, providerOptionsNames, extraHeaders } =
539
+ await this.getArgs(options, { isStreaming: true });
540
+ const wrapProviderMetadata = (payload: Record<string, unknown>) =>
541
+ Object.fromEntries(
542
+ providerOptionsNames.map(name => [name, payload]),
543
+ ) as SharedV4ProviderMetadata;
373
544
 
374
545
  const headers = combineHeaders(
375
- await resolve(this.config.headers),
546
+ this.config.headers ? await resolve(this.config.headers) : undefined,
376
547
  options.headers,
548
+ extraHeaders,
377
549
  );
378
550
 
379
551
  const { responseHeaders, value: response } = await postJsonToApi({
@@ -388,12 +560,12 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
388
560
  fetch: this.config.fetch,
389
561
  });
390
562
 
391
- let finishReason: LanguageModelV3FinishReason = {
563
+ let finishReason: LanguageModelV4FinishReason = {
392
564
  unified: 'other',
393
565
  raw: undefined,
394
566
  };
395
- let usage: GoogleGenerativeAIUsageMetadata | undefined = undefined;
396
- let providerMetadata: SharedV3ProviderMetadata | undefined = undefined;
567
+ let usage: GoogleUsageMetadata | undefined = undefined;
568
+ let providerMetadata: SharedV4ProviderMetadata | undefined = undefined;
397
569
  let lastGroundingMetadata: GroundingMetadataSchema | null = null;
398
570
  let lastUrlContextMetadata: UrlContextMetadataSchema | null = null;
399
571
 
@@ -409,12 +581,57 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
409
581
  const emittedSourceUrls = new Set<string>();
410
582
  // Associates a code execution result with its preceding call.
411
583
  let lastCodeExecutionToolCallId: string | undefined;
584
+ // Associates a server-side tool response with its preceding call (tool combination).
585
+ let lastServerToolCallId: string | undefined;
586
+
587
+ const activeStreamingToolCalls: Array<{
588
+ toolCallId: string;
589
+ toolName: string;
590
+ accumulator: GoogleJSONAccumulator;
591
+ providerMetadata?: SharedV4ProviderMetadata;
592
+ }> = [];
593
+
594
+ const finishActiveStreamingToolCall = (
595
+ controller: TransformStreamDefaultController<LanguageModelV4StreamPart>,
596
+ ) => {
597
+ const active = activeStreamingToolCalls.pop();
598
+ if (active == null) {
599
+ return;
600
+ }
601
+
602
+ const { finalJSON, closingDelta } = active.accumulator.finalize();
603
+
604
+ if (closingDelta.length > 0) {
605
+ controller.enqueue({
606
+ type: 'tool-input-delta',
607
+ id: active.toolCallId,
608
+ delta: closingDelta,
609
+ providerMetadata: active.providerMetadata,
610
+ });
611
+ }
612
+
613
+ controller.enqueue({
614
+ type: 'tool-input-end',
615
+ id: active.toolCallId,
616
+ providerMetadata: active.providerMetadata,
617
+ });
618
+
619
+ controller.enqueue({
620
+ type: 'tool-call',
621
+ toolCallId: active.toolCallId,
622
+ toolName: active.toolName,
623
+ input: finalJSON,
624
+ providerMetadata: active.providerMetadata,
625
+ });
626
+
627
+ hasToolCalls = true;
628
+ };
412
629
 
413
630
  return {
414
631
  stream: response.pipeThrough(
415
632
  new TransformStream<
416
633
  ParseResult<ChunkSchema>,
417
- LanguageModelV3StreamPart
634
+ LanguageModelV4StreamPart
418
635
  >({
419
636
  start(controller) {
420
637
  controller.enqueue({ type: 'stream-start', warnings });
@@ -508,11 +725,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
508
725
  }
509
726
  } else if ('text' in part && part.text != null) {
510
727
  const thoughtSignatureMetadata = part.thoughtSignature
511
- ? {
512
- [providerOptionsName]: {
513
- thoughtSignature: part.thoughtSignature,
514
- },
515
- }
728
+ ? wrapProviderMetadata({
729
+ thoughtSignature: part.thoughtSignature,
730
+ })
516
731
  : undefined;
517
732
 
518
733
  if (part.text.length === 0) {
@@ -598,60 +813,222 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
598
813
 
599
814
  const hasThought = part.thought === true;
600
815
  const hasThoughtSignature = !!part.thoughtSignature;
601
- const fileMeta =
602
- hasThought || hasThoughtSignature
603
- ? {
604
- [providerOptionsName]: {
605
- ...(hasThought ? { thought: true } : {}),
606
- ...(hasThoughtSignature
607
- ? { thoughtSignature: part.thoughtSignature }
608
- : {}),
609
- },
610
- }
611
- : undefined;
816
+ const fileMeta = hasThoughtSignature
817
+ ? wrapProviderMetadata({
818
+ thoughtSignature: part.thoughtSignature,
819
+ })
820
+ : undefined;
612
821
  controller.enqueue({
613
- type: 'file',
822
+ type: hasThought ? 'reasoning-file' : 'file',
614
823
  mediaType: part.inlineData.mimeType,
615
- data: part.inlineData.data,
824
+ data: { type: 'data', data: part.inlineData.data },
616
825
  providerMetadata: fileMeta,
617
826
  });
827
+ } else if ('toolCall' in part && part.toolCall) {
828
+ const toolCallId = part.toolCall.id ?? generateId();
829
+ lastServerToolCallId = toolCallId;
830
+ const serverMeta = wrapProviderMetadata({
831
+ ...(part.thoughtSignature
832
+ ? { thoughtSignature: part.thoughtSignature }
833
+ : {}),
834
+ serverToolCallId: toolCallId,
835
+ serverToolType: part.toolCall.toolType,
836
+ });
837
+
838
+ controller.enqueue({
839
+ type: 'tool-call',
840
+ toolCallId,
841
+ toolName: `server:${part.toolCall.toolType}`,
842
+ input: JSON.stringify(part.toolCall.args ?? {}),
843
+ providerExecuted: true,
844
+ dynamic: true,
845
+ providerMetadata: serverMeta,
846
+ });
847
+ } else if ('toolResponse' in part && part.toolResponse) {
848
+ const responseToolCallId =
849
+ lastServerToolCallId ??
850
+ part.toolResponse.id ??
851
+ generateId();
852
+ const serverMeta = wrapProviderMetadata({
853
+ ...(part.thoughtSignature
854
+ ? { thoughtSignature: part.thoughtSignature }
855
+ : {}),
856
+ serverToolCallId: responseToolCallId,
857
+ serverToolType: part.toolResponse.toolType,
858
+ });
859
+
860
+ controller.enqueue({
861
+ type: 'tool-result',
862
+ toolCallId: responseToolCallId,
863
+ toolName: `server:${part.toolResponse.toolType}`,
864
+ result: (part.toolResponse.response ?? {}) as JSONObject,
865
+ providerMetadata: serverMeta,
866
+ });
867
+ lastServerToolCallId = undefined;
618
868
  }
619
869
  }
620
870
 
621
- const toolCallDeltas = getToolCallsFromParts({
622
- parts: content.parts,
623
- generateId,
624
- providerOptionsName,
625
- });
871
+ // Handle streaming and complete function calls
872
+ for (const part of parts) {
873
+ if (!('functionCall' in part)) continue;
874
+
875
+ const providerMeta = part.thoughtSignature
876
+ ? wrapProviderMetadata({
877
+ thoughtSignature: part.thoughtSignature,
878
+ })
879
+ : undefined;
880
+
881
+ const isStreamingChunk =
882
+ part.functionCall.partialArgs != null ||
883
+ (part.functionCall.name != null &&
884
+ part.functionCall.willContinue === true);
885
+ const isTerminalChunk =
886
+ part.functionCall.name == null &&
887
+ part.functionCall.args == null &&
888
+ part.functionCall.partialArgs == null &&
889
+ part.functionCall.willContinue == null;
890
+ const isCompleteCall =
891
+ part.functionCall.name != null &&
892
+ part.functionCall.args != null &&
893
+ part.functionCall.partialArgs == null;
894
+ // Single-chunk no-args call: `{ name: 'X' }` with no `args`,
895
+ // `partialArgs`, or `willContinue`. Carries `thoughtSignature`.
896
+ const isNoArgsCompleteCall =
897
+ part.functionCall.name != null &&
898
+ part.functionCall.args == null &&
899
+ part.functionCall.partialArgs == null &&
900
+ part.functionCall.willContinue !== true;
901
+
902
+ if (isStreamingChunk) {
903
+ if (part.functionCall.name != null) {
904
+ const toolCallId = part.functionCall.id ?? generateId();
905
+ const accumulator = new GoogleJSONAccumulator();
906
+ activeStreamingToolCalls.push({
907
+ toolCallId,
908
+ toolName: part.functionCall.name,
909
+ accumulator,
910
+ providerMetadata: providerMeta,
911
+ });
912
+
913
+ controller.enqueue({
914
+ type: 'tool-input-start',
915
+ id: toolCallId,
916
+ toolName: part.functionCall.name,
917
+ providerMetadata: providerMeta,
918
+ });
919
+
920
+ if (part.functionCall.partialArgs != null) {
921
+ const partialArgs = part.functionCall
922
+ .partialArgs as PartialArg[];
923
+ const { textDelta } =
924
+ accumulator.processPartialArgs(partialArgs);
925
+ if (textDelta.length > 0) {
926
+ controller.enqueue({
927
+ type: 'tool-input-delta',
928
+ id: toolCallId,
929
+ delta: textDelta,
930
+ providerMetadata: providerMeta,
931
+ });
932
+ }
933
+ if (
934
+ part.functionCall.willContinue !== true &&
935
+ partialArgs.every(arg => arg.willContinue !== true)
936
+ ) {
937
+ finishActiveStreamingToolCall(controller);
938
+ }
939
+ }
940
+ } else if (
941
+ part.functionCall.partialArgs != null &&
942
+ activeStreamingToolCalls.length > 0
943
+ ) {
944
+ const active =
945
+ activeStreamingToolCalls[
946
+ activeStreamingToolCalls.length - 1
947
+ ];
948
+ const partialArgs = part.functionCall
949
+ .partialArgs as PartialArg[];
950
+ const { textDelta } =
951
+ active.accumulator.processPartialArgs(partialArgs);
952
+ if (textDelta.length > 0) {
953
+ controller.enqueue({
954
+ type: 'tool-input-delta',
955
+ id: active.toolCallId,
956
+ delta: textDelta,
957
+ providerMetadata: providerMeta,
958
+ });
959
+ }
960
+ if (
961
+ part.functionCall.willContinue !== true &&
962
+ partialArgs.every(arg => arg.willContinue !== true)
963
+ ) {
964
+ finishActiveStreamingToolCall(controller);
965
+ }
966
+ }
967
+ } else if (
968
+ isTerminalChunk &&
969
+ activeStreamingToolCalls.length > 0
970
+ ) {
971
+ finishActiveStreamingToolCall(controller);
972
+ } else if (isCompleteCall) {
973
+ const toolCallId = part.functionCall.id ?? generateId();
974
+ const toolName = part.functionCall.name!;
975
+ const args =
976
+ typeof part.functionCall.args === 'string'
977
+ ? part.functionCall.args
978
+ : JSON.stringify(part.functionCall.args ?? {});
626
979
 
627
- if (toolCallDeltas != null) {
628
- for (const toolCall of toolCallDeltas) {
629
980
  controller.enqueue({
630
981
  type: 'tool-input-start',
631
- id: toolCall.toolCallId,
632
- toolName: toolCall.toolName,
633
- providerMetadata: toolCall.providerMetadata,
982
+ id: toolCallId,
983
+ toolName,
984
+ providerMetadata: providerMeta,
634
985
  });
635
986
 
636
987
  controller.enqueue({
637
988
  type: 'tool-input-delta',
638
- id: toolCall.toolCallId,
639
- delta: toolCall.args,
640
- providerMetadata: toolCall.providerMetadata,
989
+ id: toolCallId,
990
+ delta: args,
991
+ providerMetadata: providerMeta,
992
+ });
993
+
994
+ controller.enqueue({
995
+ type: 'tool-input-end',
996
+ id: toolCallId,
997
+ providerMetadata: providerMeta,
998
+ });
999
+
1000
+ controller.enqueue({
1001
+ type: 'tool-call',
1002
+ toolCallId,
1003
+ toolName,
1004
+ input: args,
1005
+ providerMetadata: providerMeta,
1006
+ });
1007
+
1008
+ hasToolCalls = true;
1009
+ } else if (isNoArgsCompleteCall) {
1010
+ const toolCallId = part.functionCall.id ?? generateId();
1011
+ const toolName = part.functionCall.name!;
1012
+
1013
+ controller.enqueue({
1014
+ type: 'tool-input-start',
1015
+ id: toolCallId,
1016
+ toolName,
1017
+ providerMetadata: providerMeta,
641
1018
  });
642
1019
 
643
1020
  controller.enqueue({
644
1021
  type: 'tool-input-end',
645
- id: toolCall.toolCallId,
646
- providerMetadata: toolCall.providerMetadata,
1022
+ id: toolCallId,
1023
+ providerMetadata: providerMeta,
647
1024
  });
648
1025
 
649
1026
  controller.enqueue({
650
1027
  type: 'tool-call',
651
- toolCallId: toolCall.toolCallId,
652
- toolName: toolCall.toolName,
653
- input: toolCall.args,
654
- providerMetadata: toolCall.providerMetadata,
1028
+ toolCallId,
1029
+ toolName,
1030
+ input: '{}',
1031
+ providerMetadata: providerMeta,
655
1032
  });
656
1033
 
657
1034
  hasToolCalls = true;
@@ -661,29 +1038,22 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
661
1038
 
662
1039
  if (candidate.finishReason != null) {
663
1040
  finishReason = {
664
- unified: mapGoogleGenerativeAIFinishReason({
1041
+ unified: mapGoogleFinishReason({
665
1042
  finishReason: candidate.finishReason,
666
1043
  hasToolCalls,
667
1044
  }),
668
1045
  raw: candidate.finishReason,
669
1046
  };
670
1047
 
671
- providerMetadata = {
672
- [providerOptionsName]: {
673
- promptFeedback: value.promptFeedback ?? null,
674
- groundingMetadata: lastGroundingMetadata,
675
- urlContextMetadata: lastUrlContextMetadata,
676
- safetyRatings: candidate.safetyRatings ?? null,
677
- },
678
- };
679
- if (usageMetadata != null) {
680
- (
681
- providerMetadata[providerOptionsName] as Record<
682
- string,
683
- unknown
684
- >
685
- ).usageMetadata = usageMetadata;
686
- }
1048
+ providerMetadata = wrapProviderMetadata({
1049
+ promptFeedback: value.promptFeedback ?? null,
1050
+ groundingMetadata: lastGroundingMetadata,
1051
+ urlContextMetadata: lastUrlContextMetadata,
1052
+ safetyRatings: candidate.safetyRatings ?? null,
1053
+ usageMetadata: usageMetadata ?? null,
1054
+ finishMessage: candidate.finishMessage ?? null,
1055
+ serviceTier: usage?.serviceTier ?? null,
1056
+ } satisfies GoogleProviderMetadata);
687
1057
  }
688
1058
  },
689
1059
 
@@ -704,7 +1074,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
704
1074
  controller.enqueue({
705
1075
  type: 'finish',
706
1076
  finishReason,
707
- usage: convertGoogleGenerativeAIUsage(usage),
1077
+ usage: convertGoogleUsage(usage),
708
1078
  providerMetadata,
709
1079
  });
710
1080
  },
@@ -716,39 +1086,107 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
716
1086
  }
717
1087
  }
718
1088
 
719
- function getToolCallsFromParts({
720
- parts,
721
- generateId,
722
- providerOptionsName,
1089
+ function isGemini3Model(modelId: string): boolean {
1090
+ return /gemini-3[\.\-]/i.test(modelId) || /gemini-3$/i.test(modelId);
1091
+ }
1092
+
1093
+ function getMaxOutputTokensForGemini25Model(): number {
1094
+ return 65536;
1095
+ }
1096
+
1097
+ function getMaxThinkingTokensForGemini25Model(modelId: string): number {
1098
+ const id = modelId.toLowerCase();
1099
+ if (id.includes('2.5-pro') || id.includes('gemini-3-pro-image')) {
1100
+ return 32768;
1101
+ }
1102
+ return 24576;
1103
+ }
1104
+
1105
+ type GoogleThinkingConfig = NonNullable<
1106
+ InferSchema<typeof googleLanguageModelOptions>['thinkingConfig']
1107
+ >;
1108
+
1109
+ function resolveThinkingConfig({
1110
+ reasoning,
1111
+ modelId,
1112
+ warnings,
723
1113
  }: {
724
- parts: ContentSchema['parts'];
725
- generateId: () => string;
726
- providerOptionsName: string;
727
- }) {
728
- const functionCallParts = parts?.filter(
729
- part => 'functionCall' in part,
730
- ) as Array<
731
- GoogleGenerativeAIContentPart & {
732
- functionCall: { name: string; args: unknown };
733
- thoughtSignature?: string | null;
734
- }
1114
+ reasoning: LanguageModelV4CallOptions['reasoning'];
1115
+ modelId: string;
1116
+ warnings: SharedV4Warning[];
1117
+ }): Omit<GoogleThinkingConfig, 'includeThoughts'> | undefined {
1118
+ if (!isCustomReasoning(reasoning)) {
1119
+ return undefined;
1120
+ }
1121
+
1122
+ if (isGemini3Model(modelId) && !modelId.includes('gemini-3-pro-image')) {
1123
+ return resolveGemini3ThinkingConfig({ reasoning, warnings });
1124
+ }
1125
+
1126
+ return resolveGemini25ThinkingConfig({ reasoning, modelId, warnings });
1127
+ }
1128
+
1129
+ function resolveGemini3ThinkingConfig({
1130
+ reasoning,
1131
+ warnings,
1132
+ }: {
1133
+ reasoning: Exclude<
1134
+ LanguageModelV4CallOptions['reasoning'],
1135
+ 'provider-default' | undefined
735
1136
  >;
1137
+ warnings: SharedV4Warning[];
1138
+ }): Pick<GoogleThinkingConfig, 'thinkingLevel'> | undefined {
1139
+ if (reasoning === 'none') {
1140
+ // It's not possible to fully disable thinking with Gemini 3.
1141
+ return { thinkingLevel: 'minimal' };
1142
+ }
736
1143
 
737
- return functionCallParts == null || functionCallParts.length === 0
738
- ? undefined
739
- : functionCallParts.map(part => ({
740
- type: 'tool-call' as const,
741
- toolCallId: generateId(),
742
- toolName: part.functionCall.name,
743
- args: JSON.stringify(part.functionCall.args),
744
- providerMetadata: part.thoughtSignature
745
- ? {
746
- [providerOptionsName]: {
747
- thoughtSignature: part.thoughtSignature,
748
- },
749
- }
750
- : undefined,
751
- }));
1144
+ const thinkingLevel = mapReasoningToProviderEffort({
1145
+ reasoning,
1146
+ effortMap: {
1147
+ minimal: 'minimal',
1148
+ low: 'low',
1149
+ medium: 'medium',
1150
+ high: 'high',
1151
+ xhigh: 'high',
1152
+ },
1153
+ warnings,
1154
+ });
1155
+
1156
+ if (thinkingLevel == null) {
1157
+ return undefined;
1158
+ }
1159
+
1160
+ return { thinkingLevel };
1161
+ }
1162
+
1163
+ function resolveGemini25ThinkingConfig({
1164
+ reasoning,
1165
+ modelId,
1166
+ warnings,
1167
+ }: {
1168
+ reasoning: Exclude<
1169
+ LanguageModelV4CallOptions['reasoning'],
1170
+ 'provider-default' | undefined
1171
+ >;
1172
+ modelId: string;
1173
+ warnings: SharedV4Warning[];
1174
+ }): Pick<GoogleThinkingConfig, 'thinkingBudget'> | undefined {
1175
+ if (reasoning === 'none') {
1176
+ return { thinkingBudget: 0 };
1177
+ }
1178
+
1179
+ const thinkingBudget = mapReasoningToProviderBudget({
1180
+ reasoning,
1181
+ maxOutputTokens: getMaxOutputTokensForGemini25Model(),
1182
+ maxReasoningBudget: getMaxThinkingTokensForGemini25Model(modelId),
1183
+ minReasoningBudget: 0,
1184
+ warnings,
1185
+ });
1186
+ if (thinkingBudget == null) {
1187
+ return undefined;
1188
+ }
1189
+ return { thinkingBudget };
752
1190
  }
753
1191
 
754
1192
  function extractSources({
@@ -757,12 +1195,12 @@ function extractSources({
757
1195
  }: {
758
1196
  groundingMetadata: GroundingMetadataSchema | undefined | null;
759
1197
  generateId: () => string;
760
- }): undefined | LanguageModelV3Source[] {
1198
+ }): undefined | LanguageModelV4Source[] {
761
1199
  if (!groundingMetadata?.groundingChunks) {
762
1200
  return undefined;
763
1201
  }
764
1202
 
765
- const sources: LanguageModelV3Source[] = [];
1203
+ const sources: LanguageModelV4Source[] = [];
766
1204
 
767
1205
  for (const chunk of groundingMetadata.groundingChunks) {
768
1206
  if (chunk.web != null) {
@@ -928,6 +1366,15 @@ export const getGroundingMetadataSchema = () =>
928
1366
  .nullish(),
929
1367
  });
930
1368
 
1369
+ const partialArgSchema = z.object({
1370
+ jsonPath: z.string(),
1371
+ stringValue: z.string().nullish(),
1372
+ numberValue: z.number().nullish(),
1373
+ boolValue: z.boolean().nullish(),
1374
+ nullValue: z.unknown().nullish(),
1375
+ willContinue: z.boolean().nullish(),
1376
+ });
1377
+
931
1378
  const getContentSchema = () =>
932
1379
  z.object({
933
1380
  parts: z
@@ -936,8 +1383,11 @@ const getContentSchema = () =>
936
1383
  // note: order matters since text can be fully empty
937
1384
  z.object({
938
1385
  functionCall: z.object({
939
- name: z.string(),
940
- args: z.unknown(),
1386
+ id: z.string().nullish(),
1387
+ name: z.string().nullish(),
1388
+ args: z.unknown().nullish(),
1389
+ partialArgs: z.array(partialArgSchema).nullish(),
1390
+ willContinue: z.boolean().nullish(),
941
1391
  }),
942
1392
  thoughtSignature: z.string().nullish(),
943
1393
  }),
@@ -949,6 +1399,22 @@ const getContentSchema = () =>
949
1399
  thought: z.boolean().nullish(),
950
1400
  thoughtSignature: z.string().nullish(),
951
1401
  }),
1402
+ z.object({
1403
+ toolCall: z.object({
1404
+ toolType: z.string(),
1405
+ args: z.unknown().nullish(),
1406
+ id: z.string(),
1407
+ }),
1408
+ thoughtSignature: z.string().nullish(),
1409
+ }),
1410
+ z.object({
1411
+ toolResponse: z.object({
1412
+ toolType: z.string(),
1413
+ response: z.unknown().nullish(),
1414
+ id: z.string(),
1415
+ }),
1416
+ thoughtSignature: z.string().nullish(),
1417
+ }),
952
1418
  z.object({
953
1419
  executableCode: z
954
1420
  .object({
@@ -982,6 +1448,15 @@ const getSafetyRatingSchema = () =>
982
1448
  blocked: z.boolean().nullish(),
983
1449
  });
984
1450
 
1451
+ const tokenDetailsSchema = z
1452
+ .array(
1453
+ z.object({
1454
+ modality: z.string(),
1455
+ tokenCount: z.number(),
1456
+ }),
1457
+ )
1458
+ .nullish();
1459
+
985
1460
  const usageSchema = z.object({
986
1461
  cachedContentTokenCount: z.number().nullish(),
987
1462
  thoughtsTokenCount: z.number().nullish(),
@@ -990,6 +1465,10 @@ const usageSchema = z.object({
990
1465
  totalTokenCount: z.number().nullish(),
991
1466
  // https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
992
1467
  trafficType: z.string().nullish(),
1468
+ serviceTier: z.string().nullish(),
1469
+ // https://ai.google.dev/api/generate-content#Modality
1470
+ promptTokensDetails: tokenDetailsSchema,
1471
+ candidatesTokensDetails: tokenDetailsSchema,
993
1472
  });
994
1473
 
995
1474
  // https://ai.google.dev/api/generate-content#UrlRetrievalMetadata
@@ -1012,6 +1491,7 @@ const responseSchema = lazySchema(() =>
1012
1491
  z.object({
1013
1492
  content: getContentSchema().nullish().or(z.object({}).strict()),
1014
1493
  finishReason: z.string().nullish(),
1494
+ finishMessage: z.string().nullish(),
1015
1495
  safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1016
1496
  groundingMetadata: getGroundingMetadataSchema().nullish(),
1017
1497
  urlContextMetadata: getUrlContextMetadataSchema().nullish(),
@@ -1028,17 +1508,10 @@ const responseSchema = lazySchema(() =>
1028
1508
  ),
1029
1509
  );
1030
1510
 
1031
- type ContentSchema = NonNullable<
1032
- InferSchema<typeof responseSchema>['candidates'][number]['content']
1033
- >;
1034
1511
  export type GroundingMetadataSchema = NonNullable<
1035
1512
  InferSchema<typeof responseSchema>['candidates'][number]['groundingMetadata']
1036
1513
  >;
1037
1514
 
1038
- type GroundingChunkSchema = NonNullable<
1039
- GroundingMetadataSchema['groundingChunks']
1040
- >[number];
1041
-
1042
1515
  export type UrlContextMetadataSchema = NonNullable<
1043
1516
  InferSchema<typeof responseSchema>['candidates'][number]['urlContextMetadata']
1044
1517
  >;
@@ -1047,6 +1520,14 @@ export type SafetyRatingSchema = NonNullable<
1047
1520
  InferSchema<typeof responseSchema>['candidates'][number]['safetyRatings']
1048
1521
  >[number];
1049
1522
 
1523
+ export type PromptFeedbackSchema = NonNullable<
1524
+ InferSchema<typeof responseSchema>['promptFeedback']
1525
+ >;
1526
+
1527
+ export type UsageMetadataSchema = NonNullable<
1528
+ InferSchema<typeof responseSchema>['usageMetadata']
1529
+ >;
1530
+
1050
1531
  // limited version of the schema, focussed on what is needed for the implementation
1051
1532
  // this approach limits breakages when the API changes and increases efficiency
1052
1533
  const chunkSchema = lazySchema(() =>
@@ -1057,6 +1538,7 @@ const chunkSchema = lazySchema(() =>
1057
1538
  z.object({
1058
1539
  content: getContentSchema().nullish(),
1059
1540
  finishReason: z.string().nullish(),
1541
+ finishMessage: z.string().nullish(),
1060
1542
  safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1061
1543
  groundingMetadata: getGroundingMetadataSchema().nullish(),
1062
1544
  urlContextMetadata: getUrlContextMetadataSchema().nullish(),