@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
@@ -0,0 +1,647 @@
1
+ import {
2
+ UnsupportedFunctionalityError,
3
+ type LanguageModelV4Prompt,
4
+ type LanguageModelV4ToolResultOutput,
5
+ type SharedV4Warning,
6
+ } from '@ai-sdk/provider';
7
+ import {
8
+ convertToBase64,
9
+ getTopLevelMediaType,
10
+ isFullMediaType,
11
+ resolveFullMediaType,
12
+ resolveProviderReference,
13
+ } from '@ai-sdk/provider-utils';
14
+ import type {
15
+ GoogleContent,
16
+ GoogleContentPart,
17
+ GoogleFunctionResponsePart,
18
+ GooglePrompt,
19
+ } from './google-prompt';
20
+
21
+ /**
22
+ * Sentinel value Google documents for replaying functionCall parts whose
23
+ * original thoughtSignature is not available to the client.
24
+ *
25
+ * See https://ai.google.dev/gemini-api/docs/thought-signatures.
26
+ */
27
+ export const SKIP_THOUGHT_SIGNATURE_VALIDATOR =
28
+ 'skip_thought_signature_validator';
29
+
30
+ const dataUrlRegex = /^data:([^;,]+);base64,(.+)$/s;
31
+
32
+ function parseBase64DataUrl(
33
+ value: string,
34
+ ): { mediaType: string; data: string } | undefined {
35
+ const match = dataUrlRegex.exec(value);
36
+ if (match == null) {
37
+ return undefined;
38
+ }
39
+
40
+ return {
41
+ mediaType: match[1],
42
+ data: match[2],
43
+ };
44
+ }
45
+
46
+ function convertUrlToolResultPart(
47
+ url: string,
48
+ ): GoogleFunctionResponsePart | undefined {
49
+ // Per https://ai.google.dev/api/caching#FunctionResponsePart, only inline data is supported.
50
+ // https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/function-calling#functionresponsepart suggests that this
51
+ // may be different for Vertex, but this needs to be confirmed and further tested for both APIs.
52
+ const parsedDataUrl = parseBase64DataUrl(url);
53
+ if (parsedDataUrl == null) {
54
+ return undefined;
55
+ }
56
+
57
+ return {
58
+ inlineData: {
59
+ mimeType: parsedDataUrl.mediaType,
60
+ data: parsedDataUrl.data,
61
+ },
62
+ };
63
+ }
64
+
65
+ /*
66
+ * Appends tool result content parts to the message using the functionResponse
67
+ * format with support for multimodal parts (e.g. inline images/files alongside
68
+ * text). This format is supported by Gemini 3+ models.
69
+ */
70
+ function appendToolResultParts(
71
+ parts: GoogleContentPart[],
72
+ toolName: string,
73
+ outputValue: Extract<
74
+ LanguageModelV4ToolResultOutput,
75
+ { type: 'content' }
76
+ >['value'],
77
+ toolCallId?: string,
78
+ ): void {
79
+ const functionResponseParts: GoogleFunctionResponsePart[] = [];
80
+ const responseTextParts: string[] = [];
81
+
82
+ for (const contentPart of outputValue) {
83
+ switch (contentPart.type) {
84
+ case 'text': {
85
+ responseTextParts.push(contentPart.text);
86
+ break;
87
+ }
88
+ case 'file': {
89
+ if (contentPart.data.type === 'data') {
90
+ functionResponseParts.push({
91
+ inlineData: {
92
+ mimeType: resolveFullMediaType({ part: contentPart }),
93
+ data: convertToBase64(contentPart.data.data),
94
+ },
95
+ });
96
+ } else if (contentPart.data.type === 'url') {
97
+ const functionResponsePart = convertUrlToolResultPart(
98
+ contentPart.data.url.toString(),
99
+ );
100
+
101
+ if (functionResponsePart != null) {
102
+ functionResponseParts.push(functionResponsePart);
103
+ } else {
104
+ responseTextParts.push(JSON.stringify(contentPart));
105
+ }
106
+ } else {
107
+ responseTextParts.push(JSON.stringify(contentPart));
108
+ }
109
+ break;
110
+ }
111
+ default: {
112
+ responseTextParts.push(JSON.stringify(contentPart));
113
+ break;
114
+ }
115
+ }
116
+ }
117
+
118
+ parts.push({
119
+ functionResponse: {
120
+ ...(toolCallId != null ? { id: toolCallId } : {}),
121
+ name: toolName,
122
+ response: {
123
+ name: toolName,
124
+ content:
125
+ responseTextParts.length > 0
126
+ ? responseTextParts.join('\n')
127
+ : 'Tool executed successfully.',
128
+ },
129
+ ...(functionResponseParts.length > 0
130
+ ? { parts: functionResponseParts }
131
+ : {}),
132
+ },
133
+ });
134
+ }
135
+
136
+ /*
137
+ * Appends tool result content parts using a legacy format for pre-Gemini 3
138
+ * models that do not support multimodal parts within functionResponse. Instead,
139
+ * non-text content like images is sent as separate top-level inlineData parts.
140
+ */
141
+ function appendLegacyToolResultParts(
142
+ parts: GoogleContentPart[],
143
+ toolName: string,
144
+ outputValue: Extract<
145
+ LanguageModelV4ToolResultOutput,
146
+ { type: 'content' }
147
+ >['value'],
148
+ toolCallId?: string,
149
+ ): void {
150
+ for (const contentPart of outputValue) {
151
+ switch (contentPart.type) {
152
+ case 'text':
153
+ parts.push({
154
+ functionResponse: {
155
+ ...(toolCallId != null ? { id: toolCallId } : {}),
156
+ name: toolName,
157
+ response: {
158
+ name: toolName,
159
+ content: contentPart.text,
160
+ },
161
+ },
162
+ });
163
+ break;
164
+ case 'file': {
165
+ if (
166
+ contentPart.data.type === 'data' &&
167
+ getTopLevelMediaType(contentPart.mediaType) === 'image'
168
+ ) {
169
+ parts.push(
170
+ {
171
+ inlineData: {
172
+ mimeType: resolveFullMediaType({ part: contentPart }),
173
+ data: convertToBase64(contentPart.data.data),
174
+ },
175
+ },
176
+ {
177
+ text: 'Tool executed successfully and returned this image as a response',
178
+ },
179
+ );
180
+ } else {
181
+ parts.push({ text: JSON.stringify(contentPart) });
182
+ }
183
+ break;
184
+ }
185
+ default:
186
+ parts.push({ text: JSON.stringify(contentPart) });
187
+ break;
188
+ }
189
+ }
190
+ }
191
+
192
+ export function convertToGoogleMessages(
193
+ prompt: LanguageModelV4Prompt,
194
+ options?: {
195
+ isGemmaModel?: boolean;
196
+ isGemini3Model?: boolean;
197
+ onWarning?: (warning: SharedV4Warning) => void;
198
+ /**
199
+ * Names to look up under `providerOptions` when reading per-part metadata
200
+ * (e.g. thought signatures). Tried in order; first match wins. For the
201
+ * Vertex provider this is `['googleVertex', 'vertex']` (new key first,
202
+ * legacy key as fallback) and for the Google provider it is `['google']`.
203
+ */
204
+ providerOptionsNames?: readonly string[];
205
+ supportsFunctionResponseParts?: boolean;
206
+ },
207
+ ): GooglePrompt {
208
+ const systemInstructionParts: Array<{ text: string }> = [];
209
+ const contents: Array<GoogleContent> = [];
210
+ let systemMessagesAllowed = true;
211
+ const isGemmaModel = options?.isGemmaModel ?? false;
212
+ const isGemini3Model = options?.isGemini3Model ?? false;
213
+ const onWarning = options?.onWarning;
214
+ const providerOptionsNames = options?.providerOptionsNames ?? ['google'];
215
+ const isVertexLike = !providerOptionsNames.includes('google');
216
+ const supportsFunctionResponseParts =
217
+ options?.supportsFunctionResponseParts ?? true;
218
+
219
+ let sentinelInjected = false;
220
+ const missingSignatureToolNames: string[] = [];
221
+ const injectSkipSignature = (toolName: string) => {
222
+ missingSignatureToolNames.push(toolName);
223
+ sentinelInjected = true;
224
+ return SKIP_THOUGHT_SIGNATURE_VALIDATOR;
225
+ };
226
+
227
+ const readProviderOpts = (part: {
228
+ providerOptions?: Record<string, unknown> | undefined;
229
+ }): Record<string, unknown> | undefined => {
230
+ for (const name of providerOptionsNames) {
231
+ const v = part.providerOptions?.[name];
232
+ if (v != null) return v as Record<string, unknown>;
233
+ }
234
+ // Cross-namespace fallback (gateway interop): Vertex providers may receive
235
+ // metadata under `google`, and the Google provider may receive metadata
236
+ // under `googleVertex`/`vertex`.
237
+ if (isVertexLike) {
238
+ return part.providerOptions?.google as
239
+ | Record<string, unknown>
240
+ | undefined;
241
+ }
242
+ return (part.providerOptions?.googleVertex ??
243
+ part.providerOptions?.vertex) as Record<string, unknown> | undefined;
244
+ };
245
+
246
+ for (const { role, content } of prompt) {
247
+ switch (role) {
248
+ case 'system': {
249
+ if (!systemMessagesAllowed) {
250
+ throw new UnsupportedFunctionalityError({
251
+ functionality:
252
+ 'system messages are only supported at the beginning of the conversation',
253
+ });
254
+ }
255
+
256
+ systemInstructionParts.push({ text: content });
257
+ break;
258
+ }
259
+
260
+ case 'user': {
261
+ systemMessagesAllowed = false;
262
+
263
+ const parts: GoogleContentPart[] = [];
264
+
265
+ for (const part of content) {
266
+ switch (part.type) {
267
+ case 'text': {
268
+ parts.push({ text: part.text });
269
+ break;
270
+ }
271
+
272
+ case 'file': {
273
+ switch (part.data.type) {
274
+ case 'url': {
275
+ parts.push({
276
+ fileData: {
277
+ mimeType: resolveFullMediaType({ part }),
278
+ fileUri: part.data.url.toString(),
279
+ },
280
+ });
281
+ break;
282
+ }
283
+ case 'reference': {
284
+ if (isVertexLike) {
285
+ throw new UnsupportedFunctionalityError({
286
+ functionality: 'file parts with provider references',
287
+ });
288
+ }
289
+
290
+ parts.push({
291
+ fileData: {
292
+ mimeType: resolveFullMediaType({ part }),
293
+ fileUri: resolveProviderReference({
294
+ reference: part.data.reference,
295
+ provider: 'google',
296
+ }),
297
+ },
298
+ });
299
+ break;
300
+ }
301
+ case 'text': {
302
+ parts.push({
303
+ inlineData: {
304
+ mimeType: isFullMediaType(part.mediaType)
305
+ ? part.mediaType
306
+ : 'text/plain',
307
+ data: convertToBase64(
308
+ new TextEncoder().encode(part.data.text),
309
+ ),
310
+ },
311
+ });
312
+ break;
313
+ }
314
+ case 'data': {
315
+ parts.push({
316
+ inlineData: {
317
+ mimeType: resolveFullMediaType({ part }),
318
+ data: convertToBase64(part.data.data),
319
+ },
320
+ });
321
+ break;
322
+ }
323
+ }
324
+
325
+ break;
326
+ }
327
+ }
328
+ }
329
+
330
+ contents.push({ role: 'user', parts });
331
+ break;
332
+ }
333
+
334
+ case 'assistant': {
335
+ systemMessagesAllowed = false;
336
+
337
+ contents.push({
338
+ role: 'model',
339
+ parts: content
340
+ .map(part => {
341
+ const providerOpts = readProviderOpts(part);
342
+ const thoughtSignature =
343
+ providerOpts?.thoughtSignature != null
344
+ ? String(providerOpts.thoughtSignature)
345
+ : undefined;
346
+
347
+ switch (part.type) {
348
+ case 'text': {
349
+ return part.text.length === 0
350
+ ? undefined
351
+ : {
352
+ text: part.text,
353
+ thoughtSignature,
354
+ };
355
+ }
356
+
357
+ case 'reasoning': {
358
+ return part.text.length === 0
359
+ ? undefined
360
+ : {
361
+ text: part.text,
362
+ thought: true,
363
+ thoughtSignature,
364
+ };
365
+ }
366
+
367
+ case 'reasoning-file': {
368
+ switch (part.data.type) {
369
+ case 'url': {
370
+ throw new UnsupportedFunctionalityError({
371
+ functionality:
372
+ 'File data URLs in assistant messages are not supported',
373
+ });
374
+ }
375
+ case 'data': {
376
+ return {
377
+ inlineData: {
378
+ mimeType: part.mediaType,
379
+ data: convertToBase64(part.data.data),
380
+ },
381
+ thought: true,
382
+ thoughtSignature,
383
+ };
384
+ }
385
+ }
386
+ break;
387
+ }
388
+
389
+ case 'file': {
390
+ switch (part.data.type) {
391
+ case 'url': {
392
+ throw new UnsupportedFunctionalityError({
393
+ functionality:
394
+ 'File data URLs in assistant messages are not supported',
395
+ });
396
+ }
397
+ case 'reference': {
398
+ if (isVertexLike) {
399
+ throw new UnsupportedFunctionalityError({
400
+ functionality: 'file parts with provider references',
401
+ });
402
+ }
403
+
404
+ return {
405
+ fileData: {
406
+ mimeType: part.mediaType,
407
+ fileUri: resolveProviderReference({
408
+ reference: part.data.reference,
409
+ provider: 'google',
410
+ }),
411
+ },
412
+ ...(providerOpts?.thought === true
413
+ ? { thought: true }
414
+ : {}),
415
+ thoughtSignature,
416
+ };
417
+ }
418
+ case 'text': {
419
+ return {
420
+ inlineData: {
421
+ mimeType: isFullMediaType(part.mediaType)
422
+ ? part.mediaType
423
+ : 'text/plain',
424
+ data: convertToBase64(
425
+ new TextEncoder().encode(part.data.text),
426
+ ),
427
+ },
428
+ ...(providerOpts?.thought === true
429
+ ? { thought: true }
430
+ : {}),
431
+ thoughtSignature,
432
+ };
433
+ }
434
+ case 'data': {
435
+ return {
436
+ inlineData: {
437
+ mimeType: part.mediaType,
438
+ data: convertToBase64(part.data.data),
439
+ },
440
+ ...(providerOpts?.thought === true
441
+ ? { thought: true }
442
+ : {}),
443
+ thoughtSignature,
444
+ };
445
+ }
446
+ }
447
+ break;
448
+ }
449
+
450
+ case 'tool-call': {
451
+ const serverToolCallId =
452
+ providerOpts?.serverToolCallId != null
453
+ ? String(providerOpts.serverToolCallId)
454
+ : undefined;
455
+ const serverToolType =
456
+ providerOpts?.serverToolType != null
457
+ ? String(providerOpts.serverToolType)
458
+ : undefined;
459
+ const effectiveThoughtSignature =
460
+ thoughtSignature ??
461
+ (isGemini3Model
462
+ ? injectSkipSignature(part.toolName)
463
+ : undefined);
464
+
465
+ if (serverToolCallId && serverToolType) {
466
+ return {
467
+ toolCall: {
468
+ toolType: serverToolType,
469
+ args:
470
+ typeof part.input === 'string'
471
+ ? JSON.parse(part.input)
472
+ : part.input,
473
+ id: serverToolCallId,
474
+ },
475
+ thoughtSignature: effectiveThoughtSignature,
476
+ };
477
+ }
478
+
479
+ return {
480
+ functionCall: {
481
+ ...(part.toolCallId != null
482
+ ? { id: part.toolCallId }
483
+ : {}),
484
+ name: part.toolName,
485
+ args: part.input,
486
+ },
487
+ thoughtSignature: effectiveThoughtSignature,
488
+ };
489
+ }
490
+
491
+ case 'tool-result': {
492
+ const serverToolCallId =
493
+ providerOpts?.serverToolCallId != null
494
+ ? String(providerOpts.serverToolCallId)
495
+ : undefined;
496
+ const serverToolType =
497
+ providerOpts?.serverToolType != null
498
+ ? String(providerOpts.serverToolType)
499
+ : undefined;
500
+
501
+ if (serverToolCallId && serverToolType) {
502
+ return {
503
+ toolResponse: {
504
+ toolType: serverToolType,
505
+ response:
506
+ part.output.type === 'json' ? part.output.value : {},
507
+ id: serverToolCallId,
508
+ },
509
+ thoughtSignature,
510
+ };
511
+ }
512
+
513
+ return undefined;
514
+ }
515
+ }
516
+ })
517
+ .filter(part => part !== undefined),
518
+ });
519
+
520
+ break;
521
+ }
522
+
523
+ case 'tool': {
524
+ systemMessagesAllowed = false;
525
+
526
+ const parts: GoogleContentPart[] = [];
527
+
528
+ for (const part of content) {
529
+ if (part.type === 'tool-approval-response') {
530
+ continue;
531
+ }
532
+
533
+ const partProviderOpts = readProviderOpts(part);
534
+ const serverToolCallId =
535
+ partProviderOpts?.serverToolCallId != null
536
+ ? String(partProviderOpts.serverToolCallId)
537
+ : undefined;
538
+ const serverToolType =
539
+ partProviderOpts?.serverToolType != null
540
+ ? String(partProviderOpts.serverToolType)
541
+ : undefined;
542
+
543
+ if (serverToolCallId && serverToolType) {
544
+ const serverThoughtSignature =
545
+ partProviderOpts?.thoughtSignature != null
546
+ ? String(partProviderOpts.thoughtSignature)
547
+ : undefined;
548
+
549
+ if (contents.length > 0) {
550
+ const lastContent = contents[contents.length - 1];
551
+ if (lastContent.role === 'model') {
552
+ lastContent.parts.push({
553
+ toolResponse: {
554
+ toolType: serverToolType,
555
+ response:
556
+ part.output.type === 'json' ? part.output.value : {},
557
+ id: serverToolCallId,
558
+ },
559
+ thoughtSignature: serverThoughtSignature,
560
+ });
561
+ continue;
562
+ }
563
+ }
564
+ }
565
+
566
+ const output = part.output;
567
+
568
+ if (output.type === 'content') {
569
+ if (supportsFunctionResponseParts) {
570
+ appendToolResultParts(
571
+ parts,
572
+ part.toolName,
573
+ output.value,
574
+ part.toolCallId,
575
+ );
576
+ } else {
577
+ appendLegacyToolResultParts(
578
+ parts,
579
+ part.toolName,
580
+ output.value,
581
+ part.toolCallId,
582
+ );
583
+ }
584
+ } else {
585
+ parts.push({
586
+ functionResponse: {
587
+ ...(part.toolCallId != null ? { id: part.toolCallId } : {}),
588
+ name: part.toolName,
589
+ response: {
590
+ name: part.toolName,
591
+ content:
592
+ output.type === 'execution-denied'
593
+ ? (output.reason ?? 'Tool call execution denied.')
594
+ : output.value,
595
+ },
596
+ },
597
+ });
598
+ }
599
+ }
600
+
601
+ contents.push({
602
+ role: 'user',
603
+ parts,
604
+ });
605
+ break;
606
+ }
607
+ }
608
+ }
609
+
610
+ if (
611
+ isGemmaModel &&
612
+ systemInstructionParts.length > 0 &&
613
+ contents.length > 0 &&
614
+ contents[0].role === 'user'
615
+ ) {
616
+ const systemText = systemInstructionParts
617
+ .map(part => part.text)
618
+ .join('\n\n');
619
+
620
+ contents[0].parts.unshift({ text: systemText + '\n\n' });
621
+ }
622
+
623
+ if (sentinelInjected && onWarning != null) {
624
+ const uniqueToolNames = Array.from(new Set(missingSignatureToolNames));
625
+ onWarning({
626
+ type: 'other',
627
+ message:
628
+ `Replayed ${missingSignatureToolNames.length} \`functionCall\` part(s) ` +
629
+ `for a Gemini 3 model without a \`thoughtSignature\` ` +
630
+ `(tools: ${uniqueToolNames.map(name => `\`${name}\``).join(', ')}). ` +
631
+ `Injected the documented \`skip_thought_signature_validator\` sentinel ` +
632
+ `to keep the request from failing with HTTP 400. ` +
633
+ `The likely cause is application code that drops ` +
634
+ '`providerOptions.google.thoughtSignature` when persisting or ' +
635
+ 'serializing assistant tool-call messages. ' +
636
+ 'See https://ai.google.dev/gemini-api/docs/thought-signatures.',
637
+ });
638
+ }
639
+
640
+ return {
641
+ systemInstruction:
642
+ systemInstructionParts.length > 0 && !isGemmaModel
643
+ ? { parts: systemInstructionParts }
644
+ : undefined,
645
+ contents,
646
+ };
647
+ }
@@ -1,12 +1,13 @@
1
1
  import {
2
- type InferSchema,
3
2
  lazySchema,
4
3
  zodSchema,
4
+ type InferSchema,
5
5
  } from '@ai-sdk/provider-utils';
6
6
  import { z } from 'zod/v4';
7
7
 
8
- export type GoogleGenerativeAIEmbeddingModelId =
8
+ export type GoogleEmbeddingModelId =
9
9
  | 'gemini-embedding-001'
10
+ | 'gemini-embedding-2'
10
11
  | 'gemini-embedding-2-preview'
11
12
  | (string & {});
12
13
 
@@ -18,6 +19,12 @@ const googleEmbeddingContentPartSchema = z.union([
18
19
  data: z.string(),
19
20
  }),
20
21
  }),
22
+ z.object({
23
+ fileData: z.object({
24
+ fileUri: z.string(),
25
+ mimeType: z.string(),
26
+ }),
27
+ }),
21
28
  ]);
22
29
 
23
30
  export const googleEmbeddingModelOptions = lazySchema(() =>