@ai-sdk/google 4.0.0-beta.8 → 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 +608 -5
  2. package/README.md +6 -4
  3. package/dist/index.d.ts +297 -54
  4. package/dist/index.js +5409 -640
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +97 -26
  7. package/dist/internal/index.js +1653 -453
  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} +691 -217
  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 -384
  65. package/dist/index.mjs +0 -2519
  66. package/dist/index.mjs.map +0 -1
  67. package/dist/internal/index.d.mts +0 -287
  68. package/dist/internal/index.mjs +0 -1708
  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 -47
@@ -1,75 +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 {
42
- GoogleGenerativeAIContentPart,
43
- GoogleGenerativeAIProviderMetadata,
44
- } from './google-generative-ai-prompt';
46
+ type GoogleLanguageModelOptions,
47
+ type GoogleModelId,
48
+ } from './google-language-model-options';
49
+ import type { GoogleProviderMetadata } from './google-prompt';
45
50
  import { prepareTools } from './google-prepare-tools';
46
- 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';
47
56
 
48
- type GoogleGenerativeAIConfig = {
57
+ type GoogleConfig = {
49
58
  provider: string;
50
59
  baseURL: string;
51
- headers: Resolvable<Record<string, string | undefined>>;
60
+ headers?: Resolvable<Record<string, string | undefined>>;
52
61
  fetch?: FetchFunction;
53
62
  generateId: () => string;
54
63
 
55
64
  /**
56
65
  * The supported URLs for the model.
57
66
  */
58
- supportedUrls?: () => LanguageModelV3['supportedUrls'];
67
+ supportedUrls?: () => LanguageModelV4['supportedUrls'];
59
68
  };
60
69
 
61
- export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
62
- readonly specificationVersion = 'v3';
70
+ export class GoogleLanguageModel implements LanguageModelV4 {
71
+ readonly specificationVersion = 'v4';
63
72
 
64
- readonly modelId: GoogleGenerativeAIModelId;
73
+ readonly modelId: GoogleModelId;
65
74
 
66
- private readonly config: GoogleGenerativeAIConfig;
75
+ private readonly config: GoogleConfig;
67
76
  private readonly generateId: () => string;
68
77
 
69
- constructor(
70
- modelId: GoogleGenerativeAIModelId,
71
- config: GoogleGenerativeAIConfig,
72
- ) {
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) {
73
93
  this.modelId = modelId;
74
94
  this.config = config;
75
95
  this.generateId = config.generateId ?? generateId;
@@ -83,33 +103,49 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
83
103
  return this.config.supportedUrls?.() ?? {};
84
104
  }
85
105
 
86
- private async getArgs({
87
- prompt,
88
- maxOutputTokens,
89
- temperature,
90
- topP,
91
- topK,
92
- frequencyPenalty,
93
- presencePenalty,
94
- stopSequences,
95
- responseFormat,
96
- seed,
97
- tools,
98
- toolChoice,
99
- providerOptions,
100
- }: LanguageModelV3CallOptions) {
101
- const warnings: SharedV3Warning[] = [];
102
-
103
- const providerOptionsName = this.config.provider.includes('vertex')
104
- ? 'vertex'
105
- : 'google';
106
- let googleOptions = await parseProviderOptions({
107
- 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,
108
121
  providerOptions,
109
- schema: googleLanguageModelOptions,
110
- });
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
+ }
111
145
 
112
- 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')) {
113
149
  googleOptions = await parseProviderOptions({
114
150
  provider: 'google',
115
151
  providerOptions,
@@ -118,12 +154,14 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
118
154
  }
119
155
 
120
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
+
121
159
  if (
122
160
  tools?.some(
123
161
  tool =>
124
162
  tool.type === 'provider' && tool.id === 'google.vertex_rag_store',
125
163
  ) &&
126
- !this.config.provider.startsWith('google.vertex.')
164
+ !isVertexProvider
127
165
  ) {
128
166
  warnings.push({
129
167
  type: 'other',
@@ -134,12 +172,65 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
134
172
  });
135
173
  }
136
174
 
137
- 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
+ }
138
184
 
139
- const { contents, systemInstruction } = convertToGoogleGenerativeAIMessages(
140
- prompt,
141
- { isGemmaModel, providerOptionsName },
142
- );
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
+ });
143
234
 
144
235
  const {
145
236
  tools: googleTools,
@@ -149,8 +240,42 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
149
240
  tools,
150
241
  toolChoice,
151
242
  modelId: this.modelId,
243
+ isVertexProvider,
152
244
  });
153
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
+
154
279
  return {
155
280
  args: {
156
281
  generationConfig: {
@@ -182,7 +307,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
182
307
 
183
308
  // provider options:
184
309
  responseModalities: googleOptions?.responseModalities,
185
- thinkingConfig: googleOptions?.thinkingConfig,
310
+ thinkingConfig,
186
311
  ...(googleOptions?.mediaResolution && {
187
312
  mediaResolution: googleOptions.mediaResolution,
188
313
  }),
@@ -194,28 +319,31 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
194
319
  systemInstruction: isGemmaModel ? undefined : systemInstruction,
195
320
  safetySettings: googleOptions?.safetySettings,
196
321
  tools: googleTools,
197
- toolConfig: googleOptions?.retrievalConfig
198
- ? {
199
- ...googleToolConfig,
200
- retrievalConfig: googleOptions.retrievalConfig,
201
- }
202
- : googleToolConfig,
322
+ toolConfig,
203
323
  cachedContent: googleOptions?.cachedContent,
204
324
  labels: googleOptions?.labels,
325
+ serviceTier: bodyServiceTier,
205
326
  },
206
327
  warnings: [...warnings, ...toolWarnings],
207
- providerOptionsName,
328
+ providerOptionsNames,
329
+ extraHeaders: vertexPaygoHeaders,
208
330
  };
209
331
  }
210
332
 
211
333
  async doGenerate(
212
- options: LanguageModelV3CallOptions,
213
- ): Promise<LanguageModelV3GenerateResult> {
214
- 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;
215
342
 
216
343
  const mergedHeaders = combineHeaders(
217
- await resolve(this.config.headers),
344
+ this.config.headers ? await resolve(this.config.headers) : undefined,
218
345
  options.headers,
346
+ extraHeaders,
219
347
  );
220
348
 
221
349
  const {
@@ -235,7 +363,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
235
363
  });
236
364
 
237
365
  const candidate = response.candidates[0];
238
- const content: Array<LanguageModelV3Content> = [];
366
+ const content: Array<LanguageModelV4Content> = [];
239
367
 
240
368
  // map ordered parts to content:
241
369
  const parts = candidate.content?.parts ?? [];
@@ -244,6 +372,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
244
372
 
245
373
  // Associates a code execution result with its preceding call.
246
374
  let lastCodeExecutionToolCallId: string | undefined;
375
+ // Associates a server-side tool response with its preceding call (tool combination).
376
+ let lastServerToolCallId: string | undefined;
247
377
 
248
378
  // Build content array from all parts
249
379
  for (const part of parts) {
@@ -273,11 +403,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
273
403
  lastCodeExecutionToolCallId = undefined;
274
404
  } else if ('text' in part && part.text != null) {
275
405
  const thoughtSignatureMetadata = part.thoughtSignature
276
- ? {
277
- [providerOptionsName]: {
278
- thoughtSignature: part.thoughtSignature,
279
- },
280
- }
406
+ ? wrapProviderMetadata({
407
+ thoughtSignature: part.thoughtSignature,
408
+ })
281
409
  : undefined;
282
410
 
283
411
  if (part.text.length === 0) {
@@ -292,39 +420,74 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
292
420
  providerMetadata: thoughtSignatureMetadata,
293
421
  });
294
422
  }
295
- } else if ('functionCall' in part) {
423
+ } else if ('functionCall' in part && part.functionCall.name != null) {
296
424
  content.push({
297
425
  type: 'tool-call' as const,
298
- toolCallId: this.config.generateId(),
426
+ toolCallId: part.functionCall.id ?? this.config.generateId(),
299
427
  toolName: part.functionCall.name,
300
- input: JSON.stringify(part.functionCall.args),
428
+ input: JSON.stringify(part.functionCall.args ?? {}),
301
429
  providerMetadata: part.thoughtSignature
302
- ? {
303
- [providerOptionsName]: {
304
- thoughtSignature: part.thoughtSignature,
305
- },
306
- }
430
+ ? wrapProviderMetadata({
431
+ thoughtSignature: part.thoughtSignature,
432
+ })
307
433
  : undefined,
308
434
  });
309
435
  } else if ('inlineData' in part) {
310
436
  const hasThought = part.thought === true;
311
437
  const hasThoughtSignature = !!part.thoughtSignature;
312
438
  content.push({
313
- type: 'file' as const,
314
- data: part.inlineData.data,
439
+ type: hasThought ? 'reasoning-file' : 'file',
440
+ data: { type: 'data', data: part.inlineData.data },
315
441
  mediaType: part.inlineData.mimeType,
316
- providerMetadata:
317
- hasThought || hasThoughtSignature
318
- ? {
319
- [providerOptionsName]: {
320
- ...(hasThought ? { thought: true } : {}),
321
- ...(hasThoughtSignature
322
- ? { thoughtSignature: part.thoughtSignature }
323
- : {}),
324
- },
325
- }
326
- : undefined,
442
+ providerMetadata: hasThoughtSignature
443
+ ? wrapProviderMetadata({
444
+ thoughtSignature: part.thoughtSignature,
445
+ })
446
+ : undefined,
327
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;
328
491
  }
329
492
  }
330
493
 
@@ -340,7 +503,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
340
503
  return {
341
504
  content,
342
505
  finishReason: {
343
- unified: mapGoogleGenerativeAIFinishReason({
506
+ unified: mapGoogleFinishReason({
344
507
  finishReason: candidate.finishReason,
345
508
  // Only count client-executed tool calls for finish reason determination.
346
509
  hasToolCalls: content.some(
@@ -349,18 +512,17 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
349
512
  }),
350
513
  raw: candidate.finishReason ?? undefined,
351
514
  },
352
- usage: convertGoogleGenerativeAIUsage(usageMetadata),
515
+ usage: convertGoogleUsage(usageMetadata),
353
516
  warnings,
354
- providerMetadata: {
355
- [providerOptionsName]: {
356
- promptFeedback: response.promptFeedback ?? null,
357
- groundingMetadata: candidate.groundingMetadata ?? null,
358
- urlContextMetadata: candidate.urlContextMetadata ?? null,
359
- safetyRatings: candidate.safetyRatings ?? null,
360
- usageMetadata: usageMetadata ?? null,
361
- finishMessage: candidate.finishMessage ?? null,
362
- } satisfies GoogleGenerativeAIProviderMetadata,
363
- },
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),
364
526
  request: { body: args },
365
527
  response: {
366
528
  // TODO timestamp, model id, id
@@ -371,13 +533,19 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
371
533
  }
372
534
 
373
535
  async doStream(
374
- options: LanguageModelV3CallOptions,
375
- ): Promise<LanguageModelV3StreamResult> {
376
- 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;
377
544
 
378
545
  const headers = combineHeaders(
379
- await resolve(this.config.headers),
546
+ this.config.headers ? await resolve(this.config.headers) : undefined,
380
547
  options.headers,
548
+ extraHeaders,
381
549
  );
382
550
 
383
551
  const { responseHeaders, value: response } = await postJsonToApi({
@@ -392,12 +560,12 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
392
560
  fetch: this.config.fetch,
393
561
  });
394
562
 
395
- let finishReason: LanguageModelV3FinishReason = {
563
+ let finishReason: LanguageModelV4FinishReason = {
396
564
  unified: 'other',
397
565
  raw: undefined,
398
566
  };
399
- let usage: GoogleGenerativeAIUsageMetadata | undefined = undefined;
400
- let providerMetadata: SharedV3ProviderMetadata | undefined = undefined;
567
+ let usage: GoogleUsageMetadata | undefined = undefined;
568
+ let providerMetadata: SharedV4ProviderMetadata | undefined = undefined;
401
569
  let lastGroundingMetadata: GroundingMetadataSchema | null = null;
402
570
  let lastUrlContextMetadata: UrlContextMetadataSchema | null = null;
403
571
 
@@ -413,12 +581,57 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
413
581
  const emittedSourceUrls = new Set<string>();
414
582
  // Associates a code execution result with its preceding call.
415
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
+ };
416
629
 
417
630
  return {
418
631
  stream: response.pipeThrough(
419
632
  new TransformStream<
420
633
  ParseResult<ChunkSchema>,
421
- LanguageModelV3StreamPart
634
+ LanguageModelV4StreamPart
422
635
  >({
423
636
  start(controller) {
424
637
  controller.enqueue({ type: 'stream-start', warnings });
@@ -512,11 +725,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
512
725
  }
513
726
  } else if ('text' in part && part.text != null) {
514
727
  const thoughtSignatureMetadata = part.thoughtSignature
515
- ? {
516
- [providerOptionsName]: {
517
- thoughtSignature: part.thoughtSignature,
518
- },
519
- }
728
+ ? wrapProviderMetadata({
729
+ thoughtSignature: part.thoughtSignature,
730
+ })
520
731
  : undefined;
521
732
 
522
733
  if (part.text.length === 0) {
@@ -602,60 +813,222 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
602
813
 
603
814
  const hasThought = part.thought === true;
604
815
  const hasThoughtSignature = !!part.thoughtSignature;
605
- const fileMeta =
606
- hasThought || hasThoughtSignature
607
- ? {
608
- [providerOptionsName]: {
609
- ...(hasThought ? { thought: true } : {}),
610
- ...(hasThoughtSignature
611
- ? { thoughtSignature: part.thoughtSignature }
612
- : {}),
613
- },
614
- }
615
- : undefined;
816
+ const fileMeta = hasThoughtSignature
817
+ ? wrapProviderMetadata({
818
+ thoughtSignature: part.thoughtSignature,
819
+ })
820
+ : undefined;
616
821
  controller.enqueue({
617
- type: 'file',
822
+ type: hasThought ? 'reasoning-file' : 'file',
618
823
  mediaType: part.inlineData.mimeType,
619
- data: part.inlineData.data,
824
+ data: { type: 'data', data: part.inlineData.data },
620
825
  providerMetadata: fileMeta,
621
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;
622
868
  }
623
869
  }
624
870
 
625
- const toolCallDeltas = getToolCallsFromParts({
626
- parts: content.parts,
627
- generateId,
628
- providerOptionsName,
629
- });
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 ?? {});
630
979
 
631
- if (toolCallDeltas != null) {
632
- for (const toolCall of toolCallDeltas) {
633
980
  controller.enqueue({
634
981
  type: 'tool-input-start',
635
- id: toolCall.toolCallId,
636
- toolName: toolCall.toolName,
637
- providerMetadata: toolCall.providerMetadata,
982
+ id: toolCallId,
983
+ toolName,
984
+ providerMetadata: providerMeta,
638
985
  });
639
986
 
640
987
  controller.enqueue({
641
988
  type: 'tool-input-delta',
642
- id: toolCall.toolCallId,
643
- delta: toolCall.args,
644
- 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,
645
1018
  });
646
1019
 
647
1020
  controller.enqueue({
648
1021
  type: 'tool-input-end',
649
- id: toolCall.toolCallId,
650
- providerMetadata: toolCall.providerMetadata,
1022
+ id: toolCallId,
1023
+ providerMetadata: providerMeta,
651
1024
  });
652
1025
 
653
1026
  controller.enqueue({
654
1027
  type: 'tool-call',
655
- toolCallId: toolCall.toolCallId,
656
- toolName: toolCall.toolName,
657
- input: toolCall.args,
658
- providerMetadata: toolCall.providerMetadata,
1028
+ toolCallId,
1029
+ toolName,
1030
+ input: '{}',
1031
+ providerMetadata: providerMeta,
659
1032
  });
660
1033
 
661
1034
  hasToolCalls = true;
@@ -665,23 +1038,22 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
665
1038
 
666
1039
  if (candidate.finishReason != null) {
667
1040
  finishReason = {
668
- unified: mapGoogleGenerativeAIFinishReason({
1041
+ unified: mapGoogleFinishReason({
669
1042
  finishReason: candidate.finishReason,
670
1043
  hasToolCalls,
671
1044
  }),
672
1045
  raw: candidate.finishReason,
673
1046
  };
674
1047
 
675
- providerMetadata = {
676
- [providerOptionsName]: {
677
- promptFeedback: value.promptFeedback ?? null,
678
- groundingMetadata: lastGroundingMetadata,
679
- urlContextMetadata: lastUrlContextMetadata,
680
- safetyRatings: candidate.safetyRatings ?? null,
681
- usageMetadata: usageMetadata ?? null,
682
- finishMessage: candidate.finishMessage ?? null,
683
- } satisfies GoogleGenerativeAIProviderMetadata,
684
- };
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);
685
1057
  }
686
1058
  },
687
1059
 
@@ -702,7 +1074,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
702
1074
  controller.enqueue({
703
1075
  type: 'finish',
704
1076
  finishReason,
705
- usage: convertGoogleGenerativeAIUsage(usage),
1077
+ usage: convertGoogleUsage(usage),
706
1078
  providerMetadata,
707
1079
  });
708
1080
  },
@@ -714,39 +1086,107 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
714
1086
  }
715
1087
  }
716
1088
 
717
- function getToolCallsFromParts({
718
- parts,
719
- generateId,
720
- 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,
721
1113
  }: {
722
- parts: ContentSchema['parts'];
723
- generateId: () => string;
724
- providerOptionsName: string;
725
- }) {
726
- const functionCallParts = parts?.filter(
727
- part => 'functionCall' in part,
728
- ) as Array<
729
- GoogleGenerativeAIContentPart & {
730
- functionCall: { name: string; args: unknown };
731
- thoughtSignature?: string | null;
732
- }
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
733
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
+ }
734
1143
 
735
- return functionCallParts == null || functionCallParts.length === 0
736
- ? undefined
737
- : functionCallParts.map(part => ({
738
- type: 'tool-call' as const,
739
- toolCallId: generateId(),
740
- toolName: part.functionCall.name,
741
- args: JSON.stringify(part.functionCall.args),
742
- providerMetadata: part.thoughtSignature
743
- ? {
744
- [providerOptionsName]: {
745
- thoughtSignature: part.thoughtSignature,
746
- },
747
- }
748
- : undefined,
749
- }));
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 };
750
1190
  }
751
1191
 
752
1192
  function extractSources({
@@ -755,12 +1195,12 @@ function extractSources({
755
1195
  }: {
756
1196
  groundingMetadata: GroundingMetadataSchema | undefined | null;
757
1197
  generateId: () => string;
758
- }): undefined | LanguageModelV3Source[] {
1198
+ }): undefined | LanguageModelV4Source[] {
759
1199
  if (!groundingMetadata?.groundingChunks) {
760
1200
  return undefined;
761
1201
  }
762
1202
 
763
- const sources: LanguageModelV3Source[] = [];
1203
+ const sources: LanguageModelV4Source[] = [];
764
1204
 
765
1205
  for (const chunk of groundingMetadata.groundingChunks) {
766
1206
  if (chunk.web != null) {
@@ -926,6 +1366,15 @@ export const getGroundingMetadataSchema = () =>
926
1366
  .nullish(),
927
1367
  });
928
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
+
929
1378
  const getContentSchema = () =>
930
1379
  z.object({
931
1380
  parts: z
@@ -934,8 +1383,11 @@ const getContentSchema = () =>
934
1383
  // note: order matters since text can be fully empty
935
1384
  z.object({
936
1385
  functionCall: z.object({
937
- name: z.string(),
938
- 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(),
939
1391
  }),
940
1392
  thoughtSignature: z.string().nullish(),
941
1393
  }),
@@ -947,6 +1399,22 @@ const getContentSchema = () =>
947
1399
  thought: z.boolean().nullish(),
948
1400
  thoughtSignature: z.string().nullish(),
949
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
+ }),
950
1418
  z.object({
951
1419
  executableCode: z
952
1420
  .object({
@@ -980,6 +1448,15 @@ const getSafetyRatingSchema = () =>
980
1448
  blocked: z.boolean().nullish(),
981
1449
  });
982
1450
 
1451
+ const tokenDetailsSchema = z
1452
+ .array(
1453
+ z.object({
1454
+ modality: z.string(),
1455
+ tokenCount: z.number(),
1456
+ }),
1457
+ )
1458
+ .nullish();
1459
+
983
1460
  const usageSchema = z.object({
984
1461
  cachedContentTokenCount: z.number().nullish(),
985
1462
  thoughtsTokenCount: z.number().nullish(),
@@ -988,6 +1465,10 @@ const usageSchema = z.object({
988
1465
  totalTokenCount: z.number().nullish(),
989
1466
  // https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
990
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,
991
1472
  });
992
1473
 
993
1474
  // https://ai.google.dev/api/generate-content#UrlRetrievalMetadata
@@ -1027,17 +1508,10 @@ const responseSchema = lazySchema(() =>
1027
1508
  ),
1028
1509
  );
1029
1510
 
1030
- type ContentSchema = NonNullable<
1031
- InferSchema<typeof responseSchema>['candidates'][number]['content']
1032
- >;
1033
1511
  export type GroundingMetadataSchema = NonNullable<
1034
1512
  InferSchema<typeof responseSchema>['candidates'][number]['groundingMetadata']
1035
1513
  >;
1036
1514
 
1037
- type GroundingChunkSchema = NonNullable<
1038
- GroundingMetadataSchema['groundingChunks']
1039
- >[number];
1040
-
1041
1515
  export type UrlContextMetadataSchema = NonNullable<
1042
1516
  InferSchema<typeof responseSchema>['candidates'][number]['urlContextMetadata']
1043
1517
  >;