@ai-sdk/google 3.0.78 → 3.0.80

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @ai-sdk/google
2
2
 
3
+ ## 3.0.80
4
+
5
+ ### Patch Changes
6
+
7
+ - f62ffe0: fix(google): auto-inject `skip_thought_signature_validator` for Gemini 3 tool-call replays without a signature
8
+
9
+ Gemini 3 models reject requests when an assistant `functionCall` part lacks a `thoughtSignature` with HTTP 400 `"Function call is missing a thought_signature in functionCall parts."` This is easy to hit when application code persists/serializes messages and drops `providerOptions.google.thoughtSignature` (custom DB schemas, `useChat` server routes that rebuild messages, synthetic tool-call injection).
10
+
11
+ The provider now detects this case (Gemini 3 model + missing signature under `google`, `googleVertex`, and `vertex` namespaces) and injects the documented `skip_thought_signature_validator` sentinel into the outbound `functionCall`, plus surfaces a one-shot warning per request listing the affected tool names so the developer can find and fix the upstream serialization. Non-Gemini-3 models are unaffected, and real signatures take precedence when present.
12
+
13
+ ## 3.0.79
14
+
15
+ ### Patch Changes
16
+
17
+ - cfa0cb2: feat(provider/google): support Google search grounding when using `generateImage` with Gemini
18
+
3
19
  ## 3.0.78
4
20
 
5
21
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -225,6 +225,17 @@ interface GoogleGenerativeAIImageSettings {
225
225
  declare const googleImageModelOptionsSchema: _ai_sdk_provider_utils.LazySchema<{
226
226
  personGeneration?: "dont_allow" | "allow_adult" | "allow_all" | null | undefined;
227
227
  aspectRatio?: "1:1" | "3:4" | "4:3" | "9:16" | "16:9" | null | undefined;
228
+ googleSearch?: {
229
+ [x: string]: unknown;
230
+ searchTypes?: {
231
+ webSearch?: Record<string, never> | undefined;
232
+ imageSearch?: Record<string, never> | undefined;
233
+ } | undefined;
234
+ timeRangeFilter?: {
235
+ startTime: string;
236
+ endTime: string;
237
+ } | undefined;
238
+ } | undefined;
228
239
  }>;
229
240
  type GoogleImageModelOptions = InferSchema<typeof googleImageModelOptionsSchema>;
230
241
 
package/dist/index.d.ts CHANGED
@@ -225,6 +225,17 @@ interface GoogleGenerativeAIImageSettings {
225
225
  declare const googleImageModelOptionsSchema: _ai_sdk_provider_utils.LazySchema<{
226
226
  personGeneration?: "dont_allow" | "allow_adult" | "allow_all" | null | undefined;
227
227
  aspectRatio?: "1:1" | "3:4" | "4:3" | "9:16" | "16:9" | null | undefined;
228
+ googleSearch?: {
229
+ [x: string]: unknown;
230
+ searchTypes?: {
231
+ webSearch?: Record<string, never> | undefined;
232
+ imageSearch?: Record<string, never> | undefined;
233
+ } | undefined;
234
+ timeRangeFilter?: {
235
+ startTime: string;
236
+ endTime: string;
237
+ } | undefined;
238
+ } | undefined;
228
239
  }>;
229
240
  type GoogleImageModelOptions = InferSchema<typeof googleImageModelOptionsSchema>;
230
241
 
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ module.exports = __toCommonJS(index_exports);
30
30
  var import_provider_utils23 = require("@ai-sdk/provider-utils");
31
31
 
32
32
  // src/version.ts
33
- var VERSION = true ? "3.0.78" : "0.0.0-test";
33
+ var VERSION = true ? "3.0.80" : "0.0.0-test";
34
34
 
35
35
  // src/google-generative-ai-embedding-model.ts
36
36
  var import_provider = require("@ai-sdk/provider");
@@ -406,6 +406,23 @@ function isEmptyObjectSchema(jsonSchema) {
406
406
  // src/convert-to-google-generative-ai-messages.ts
407
407
  var import_provider2 = require("@ai-sdk/provider");
408
408
  var import_provider_utils4 = require("@ai-sdk/provider-utils");
409
+ var SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
410
+ function getGoogleProviderOptions(providerOptions, providerOptionsName) {
411
+ const namespaces = [
412
+ providerOptionsName,
413
+ "google",
414
+ "googleVertex",
415
+ "vertex"
416
+ ].filter((namespace, index, allNamespaces) => {
417
+ return allNamespaces.indexOf(namespace) === index;
418
+ });
419
+ for (const namespace of namespaces) {
420
+ const options = providerOptions == null ? void 0 : providerOptions[namespace];
421
+ if (options != null) {
422
+ return options;
423
+ }
424
+ }
425
+ }
409
426
  var dataUrlRegex = /^data:([^;,]+);base64,(.+)$/s;
410
427
  function parseBase64DataUrl(value) {
411
428
  const match = dataUrlRegex.exec(value);
@@ -513,13 +530,22 @@ function appendLegacyToolResultParts(parts, toolName, outputValue, toolCallId) {
513
530
  }
514
531
  }
515
532
  function convertToGoogleGenerativeAIMessages(prompt, options) {
516
- var _a, _b, _c, _d, _e, _f, _g, _h;
533
+ var _a, _b, _c, _d, _e;
517
534
  const systemInstructionParts = [];
518
535
  const contents = [];
519
536
  let systemMessagesAllowed = true;
520
537
  const isGemmaModel = (_a = options == null ? void 0 : options.isGemmaModel) != null ? _a : false;
521
- const providerOptionsName = (_b = options == null ? void 0 : options.providerOptionsName) != null ? _b : "google";
522
- const supportsFunctionResponseParts = (_c = options == null ? void 0 : options.supportsFunctionResponseParts) != null ? _c : true;
538
+ const isGemini3Model = (_b = options == null ? void 0 : options.isGemini3Model) != null ? _b : false;
539
+ const providerOptionsName = (_c = options == null ? void 0 : options.providerOptionsName) != null ? _c : "google";
540
+ const supportsFunctionResponseParts = (_d = options == null ? void 0 : options.supportsFunctionResponseParts) != null ? _d : true;
541
+ const onWarning = options == null ? void 0 : options.onWarning;
542
+ let sentinelInjected = false;
543
+ const missingSignatureToolNames = [];
544
+ const injectSkipSignature = (toolName) => {
545
+ missingSignatureToolNames.push(toolName);
546
+ sentinelInjected = true;
547
+ return SKIP_THOUGHT_SIGNATURE_VALIDATOR;
548
+ };
523
549
  for (const { role, content } of prompt) {
524
550
  switch (role) {
525
551
  case "system": {
@@ -567,8 +593,10 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
567
593
  contents.push({
568
594
  role: "model",
569
595
  parts: content.map((part) => {
570
- var _a2, _b2, _c2, _d2;
571
- const providerOpts = (_d2 = (_a2 = part.providerOptions) == null ? void 0 : _a2[providerOptionsName]) != null ? _d2 : providerOptionsName !== "google" ? (_b2 = part.providerOptions) == null ? void 0 : _b2.google : (_c2 = part.providerOptions) == null ? void 0 : _c2.vertex;
596
+ const providerOpts = getGoogleProviderOptions(
597
+ part.providerOptions,
598
+ providerOptionsName
599
+ );
572
600
  const thoughtSignature = (providerOpts == null ? void 0 : providerOpts.thoughtSignature) != null ? String(providerOpts.thoughtSignature) : void 0;
573
601
  switch (part.type) {
574
602
  case "text": {
@@ -602,6 +630,7 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
602
630
  case "tool-call": {
603
631
  const serverToolCallId = (providerOpts == null ? void 0 : providerOpts.serverToolCallId) != null ? String(providerOpts.serverToolCallId) : void 0;
604
632
  const serverToolType = (providerOpts == null ? void 0 : providerOpts.serverToolType) != null ? String(providerOpts.serverToolType) : void 0;
633
+ const effectiveThoughtSignature = thoughtSignature != null ? thoughtSignature : isGemini3Model ? injectSkipSignature(part.toolName) : void 0;
605
634
  if (serverToolCallId && serverToolType) {
606
635
  return {
607
636
  toolCall: {
@@ -609,7 +638,7 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
609
638
  args: typeof part.input === "string" ? JSON.parse(part.input) : part.input,
610
639
  id: serverToolCallId
611
640
  },
612
- thoughtSignature
641
+ thoughtSignature: effectiveThoughtSignature
613
642
  };
614
643
  }
615
644
  return {
@@ -618,7 +647,7 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
618
647
  name: part.toolName,
619
648
  args: part.input
620
649
  },
621
- thoughtSignature
650
+ thoughtSignature: effectiveThoughtSignature
622
651
  };
623
652
  }
624
653
  case "tool-result": {
@@ -648,7 +677,10 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
648
677
  if (part.type === "tool-approval-response") {
649
678
  continue;
650
679
  }
651
- const partProviderOpts = (_g = (_d = part.providerOptions) == null ? void 0 : _d[providerOptionsName]) != null ? _g : providerOptionsName !== "google" ? (_e = part.providerOptions) == null ? void 0 : _e.google : (_f = part.providerOptions) == null ? void 0 : _f.vertex;
680
+ const partProviderOpts = getGoogleProviderOptions(
681
+ part.providerOptions,
682
+ providerOptionsName
683
+ );
652
684
  const serverToolCallId = (partProviderOpts == null ? void 0 : partProviderOpts.serverToolCallId) != null ? String(partProviderOpts.serverToolCallId) : void 0;
653
685
  const serverToolType = (partProviderOpts == null ? void 0 : partProviderOpts.serverToolType) != null ? String(partProviderOpts.serverToolType) : void 0;
654
686
  if (serverToolCallId && serverToolType) {
@@ -692,7 +724,7 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
692
724
  name: part.toolName,
693
725
  response: {
694
726
  name: part.toolName,
695
- content: output.type === "execution-denied" ? (_h = output.reason) != null ? _h : "Tool execution denied." : output.value
727
+ content: output.type === "execution-denied" ? (_e = output.reason) != null ? _e : "Tool execution denied." : output.value
696
728
  }
697
729
  }
698
730
  });
@@ -710,6 +742,13 @@ function convertToGoogleGenerativeAIMessages(prompt, options) {
710
742
  const systemText = systemInstructionParts.map((part) => part.text).join("\n\n");
711
743
  contents[0].parts.unshift({ text: systemText + "\n\n" });
712
744
  }
745
+ if (sentinelInjected && onWarning != null) {
746
+ const uniqueToolNames = Array.from(new Set(missingSignatureToolNames));
747
+ onWarning({
748
+ type: "other",
749
+ message: `Replayed ${missingSignatureToolNames.length} \`functionCall\` part(s) for a Gemini 3 model without a \`thoughtSignature\` (tools: ${uniqueToolNames.map((name) => `\`${name}\``).join(", ")}). Injected the documented \`skip_thought_signature_validator\` sentinel to keep the request from failing with HTTP 400. The likely cause is application code that drops \`providerOptions.google.thoughtSignature\` when persisting or serializing assistant tool-call messages. See https://ai.google.dev/gemini-api/docs/thought-signatures.`
750
+ });
751
+ }
713
752
  return {
714
753
  systemInstruction: systemInstructionParts.length > 0 && !isGemmaModel ? { parts: systemInstructionParts } : void 0,
715
754
  contents
@@ -1463,13 +1502,16 @@ var GoogleGenerativeAILanguageModel = class {
1463
1502
  } : void 0;
1464
1503
  const bodyServiceTier = isVertexProvider ? void 0 : googleOptions == null ? void 0 : googleOptions.serviceTier;
1465
1504
  const isGemmaModel = this.modelId.toLowerCase().startsWith("gemma-");
1466
- const supportsFunctionResponseParts = this.modelId.startsWith("gemini-3");
1505
+ const isGemini3Model = /^gemini-3[.-]/.test(this.modelId);
1506
+ const supportsFunctionResponseParts = isGemini3Model;
1467
1507
  const { contents, systemInstruction } = convertToGoogleGenerativeAIMessages(
1468
1508
  prompt,
1469
1509
  {
1470
1510
  isGemmaModel,
1511
+ isGemini3Model,
1471
1512
  providerOptionsName,
1472
- supportsFunctionResponseParts
1513
+ supportsFunctionResponseParts,
1514
+ onWarning: (warning) => warnings.push(warning)
1473
1515
  }
1474
1516
  );
1475
1517
  const {
@@ -2667,7 +2709,15 @@ var GoogleGenerativeAIImageModel = class {
2667
2709
  parameters.aspectRatio = aspectRatio;
2668
2710
  }
2669
2711
  if (googleOptions) {
2670
- Object.assign(parameters, googleOptions);
2712
+ const { googleSearch: imagenGoogleSearch, ...imagenOptions } = googleOptions;
2713
+ if (imagenGoogleSearch != null) {
2714
+ warnings.push({
2715
+ type: "unsupported",
2716
+ feature: "googleSearch",
2717
+ details: "Google Search grounding is only supported on Gemini image models."
2718
+ });
2719
+ }
2720
+ Object.assign(parameters, imagenOptions);
2671
2721
  }
2672
2722
  const body = {
2673
2723
  instances: [{ prompt }],
@@ -2704,7 +2754,7 @@ var GoogleGenerativeAIImageModel = class {
2704
2754
  };
2705
2755
  }
2706
2756
  async doGenerateGemini(options) {
2707
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2757
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2708
2758
  const {
2709
2759
  prompt,
2710
2760
  n,
@@ -2759,12 +2809,18 @@ var GoogleGenerativeAIImageModel = class {
2759
2809
  const languageModelPrompt = [
2760
2810
  { role: "user", content: userContent }
2761
2811
  ];
2812
+ const googleImageOptions = await (0, import_provider_utils14.parseProviderOptions)({
2813
+ provider: "google",
2814
+ providerOptions,
2815
+ schema: googleImageModelOptionsSchema
2816
+ });
2817
+ const { googleSearch: _strippedGoogleSearch, ...passthroughGoogleOptions } = (_a = providerOptions == null ? void 0 : providerOptions.google) != null ? _a : {};
2762
2818
  const languageModel = new GoogleGenerativeAILanguageModel(this.modelId, {
2763
2819
  provider: this.config.provider,
2764
2820
  baseURL: this.config.baseURL,
2765
- headers: (_a = this.config.headers) != null ? _a : {},
2821
+ headers: (_b = this.config.headers) != null ? _b : {},
2766
2822
  fetch: this.config.fetch,
2767
- generateId: (_b = this.config.generateId) != null ? _b : import_provider_utils14.generateId
2823
+ generateId: (_c = this.config.generateId) != null ? _c : import_provider_utils14.generateId
2768
2824
  });
2769
2825
  const result = await languageModel.doGenerate({
2770
2826
  prompt: languageModelPrompt,
@@ -2775,9 +2831,17 @@ var GoogleGenerativeAIImageModel = class {
2775
2831
  imageConfig: aspectRatio ? {
2776
2832
  aspectRatio
2777
2833
  } : void 0,
2778
- ...(_c = providerOptions == null ? void 0 : providerOptions.google) != null ? _c : {}
2834
+ ...passthroughGoogleOptions
2779
2835
  }
2780
2836
  },
2837
+ tools: (googleImageOptions == null ? void 0 : googleImageOptions.googleSearch) != null ? [
2838
+ {
2839
+ type: "provider",
2840
+ id: "google.google_search",
2841
+ name: "google_search",
2842
+ args: googleImageOptions.googleSearch
2843
+ }
2844
+ ] : void 0,
2781
2845
  headers,
2782
2846
  abortSignal
2783
2847
  });
@@ -2788,23 +2852,25 @@ var GoogleGenerativeAIImageModel = class {
2788
2852
  images.push((0, import_provider_utils14.convertToBase64)(part.data));
2789
2853
  }
2790
2854
  }
2855
+ const languageModelGoogleMetadata = (_h = (_g = result.providerMetadata) == null ? void 0 : _g.google) != null ? _h : {};
2791
2856
  return {
2792
2857
  images,
2793
2858
  warnings,
2794
2859
  providerMetadata: {
2795
2860
  google: {
2861
+ ...languageModelGoogleMetadata,
2796
2862
  images: images.map(() => ({}))
2797
2863
  }
2798
2864
  },
2799
2865
  response: {
2800
2866
  timestamp: currentDate,
2801
2867
  modelId: this.modelId,
2802
- headers: (_g = result.response) == null ? void 0 : _g.headers
2868
+ headers: (_i = result.response) == null ? void 0 : _i.headers
2803
2869
  },
2804
2870
  usage: result.usage ? {
2805
2871
  inputTokens: result.usage.inputTokens.total,
2806
2872
  outputTokens: result.usage.outputTokens.total,
2807
- totalTokens: ((_h = result.usage.inputTokens.total) != null ? _h : 0) + ((_i = result.usage.outputTokens.total) != null ? _i : 0)
2873
+ totalTokens: ((_j = result.usage.inputTokens.total) != null ? _j : 0) + ((_k = result.usage.outputTokens.total) != null ? _k : 0)
2808
2874
  } : void 0
2809
2875
  };
2810
2876
  }
@@ -2823,7 +2889,17 @@ var googleImageModelOptionsSchema = (0, import_provider_utils14.lazySchema)(
2823
2889
  () => (0, import_provider_utils14.zodSchema)(
2824
2890
  import_v413.z.object({
2825
2891
  personGeneration: import_v413.z.enum(["dont_allow", "allow_adult", "allow_all"]).nullish(),
2826
- aspectRatio: import_v413.z.enum(["1:1", "3:4", "4:3", "9:16", "16:9"]).nullish()
2892
+ aspectRatio: import_v413.z.enum(["1:1", "3:4", "4:3", "9:16", "16:9"]).nullish(),
2893
+ /**
2894
+ * Enable Google Search grounding for Gemini image models. The value is
2895
+ * forwarded as the args of the `google.tools.googleSearch` provider
2896
+ * tool on the underlying language-model call. Pass `{}` for defaults.
2897
+ *
2898
+ * `generateImage` does not accept a `tools` parameter, so this is the
2899
+ * dedicated escape hatch for grounding image generation the same way
2900
+ * `generateText` does.
2901
+ */
2902
+ googleSearch: googleSearchToolArgsBaseSchema.optional()
2827
2903
  })
2828
2904
  )
2829
2905
  );