@ai-sdk/google 3.0.37 → 3.0.39

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.
@@ -564,11 +564,36 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
564
564
  });
565
565
  }
566
566
  } else if ('inlineData' in part) {
567
+ // End any active text or reasoning block before starting file output.
568
+ // Relevant for multimodal output models.
569
+ if (currentTextBlockId !== null) {
570
+ controller.enqueue({
571
+ type: 'text-end',
572
+ id: currentTextBlockId,
573
+ });
574
+ currentTextBlockId = null;
575
+ }
576
+ if (currentReasoningBlockId !== null) {
577
+ controller.enqueue({
578
+ type: 'reasoning-end',
579
+ id: currentReasoningBlockId,
580
+ });
581
+ currentReasoningBlockId = null;
582
+ }
583
+
567
584
  // Process file parts inline to preserve order with text
585
+ const thoughtSignatureMetadata = part.thoughtSignature
586
+ ? {
587
+ [providerOptionsName]: {
588
+ thoughtSignature: part.thoughtSignature,
589
+ },
590
+ }
591
+ : undefined;
568
592
  controller.enqueue({
569
593
  type: 'file',
570
594
  mediaType: part.inlineData.mimeType,
571
595
  data: part.inlineData.data,
596
+ providerMetadata: thoughtSignatureMetadata,
572
597
  });
573
598
  }
574
599
  }
@@ -729,6 +754,17 @@ function extractSources({
729
754
  url: chunk.web.uri,
730
755
  title: chunk.web.title ?? undefined,
731
756
  });
757
+ } else if (chunk.image != null) {
758
+ // Handle image chunks as image sources
759
+ sources.push({
760
+ type: 'source',
761
+ sourceType: 'url',
762
+ id: generateId(),
763
+ // Google requires attribution to the source URI, not the actual image URI.
764
+ // TODO: add another type in v7 to allow both the image and source URL to be included separately
765
+ url: chunk.image.sourceUri,
766
+ title: chunk.image.title ?? undefined,
767
+ });
732
768
  } else if (chunk.retrievedContext != null) {
733
769
  // Handle retrievedContext chunks from RAG operations
734
770
  const uri = chunk.retrievedContext.uri;
@@ -808,6 +844,7 @@ function extractSources({
808
844
  export const getGroundingMetadataSchema = () =>
809
845
  z.object({
810
846
  webSearchQueries: z.array(z.string()).nullish(),
847
+ imageSearchQueries: z.array(z.string()).nullish(),
811
848
  retrievalQueries: z.array(z.string()).nullish(),
812
849
  searchEntryPoint: z.object({ renderedContent: z.string() }).nullish(),
813
850
  groundingChunks: z
@@ -816,6 +853,14 @@ export const getGroundingMetadataSchema = () =>
816
853
  web: z
817
854
  .object({ uri: z.string(), title: z.string().nullish() })
818
855
  .nullish(),
856
+ image: z
857
+ .object({
858
+ sourceUri: z.string(),
859
+ imageUri: z.string(),
860
+ title: z.string().nullish(),
861
+ domain: z.string().nullish(),
862
+ })
863
+ .nullish(),
819
864
  retrievedContext: z
820
865
  .object({
821
866
  uri: z.string().nullish(),
@@ -50,9 +50,10 @@ export function prepareTools({
50
50
  ] as const satisfies GoogleGenerativeAIModelId[]
51
51
  ).some(id => id === modelId);
52
52
  const isGemini2orNewer =
53
- modelId.includes('gemini-2') || modelId.includes('gemini-3') || isLatest;
54
- const supportsDynamicRetrieval =
55
- modelId.includes('gemini-1.5-flash') && !modelId.includes('-8b');
53
+ modelId.includes('gemini-2') ||
54
+ modelId.includes('gemini-3') ||
55
+ modelId.includes('nano-banana') ||
56
+ isLatest;
56
57
  const supportsFileSearch =
57
58
  modelId.includes('gemini-2.5') || modelId.includes('gemini-3');
58
59
 
@@ -79,24 +80,13 @@ export function prepareTools({
79
80
  switch (tool.id) {
80
81
  case 'google.google_search':
81
82
  if (isGemini2orNewer) {
82
- googleTools.push({ googleSearch: {} });
83
- } else if (supportsDynamicRetrieval) {
84
- // For non-Gemini-2 models that don't support dynamic retrieval, use basic googleSearchRetrieval
85
- googleTools.push({
86
- googleSearchRetrieval: {
87
- dynamicRetrievalConfig: {
88
- mode: tool.args.mode as
89
- | 'MODE_DYNAMIC'
90
- | 'MODE_UNSPECIFIED'
91
- | undefined,
92
- dynamicThreshold: tool.args.dynamicThreshold as
93
- | number
94
- | undefined,
95
- },
96
- },
97
- });
83
+ googleTools.push({ googleSearch: { ...tool.args } });
98
84
  } else {
99
- googleTools.push({ googleSearchRetrieval: {} });
85
+ toolWarnings.push({
86
+ type: 'unsupported',
87
+ feature: `provider-defined tool ${tool.id}`,
88
+ details: 'Google Search requires Gemini 2.0 or newer.',
89
+ });
100
90
  }
101
91
  break;
102
92
  case 'google.enterprise_web_search':
@@ -9,32 +9,35 @@ import { z } from 'zod/v4';
9
9
  // https://ai.google.dev/api/generate-content#GroundingSupport
10
10
  // https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-search
11
11
 
12
- export const googleSearch = createProviderToolFactory<
13
- {},
14
- {
15
- /**
16
- * The mode of the predictor to be used in dynamic retrieval. The following modes are supported:
17
- * - MODE_DYNAMIC: Run retrieval only when system decides it is necessary
18
- * - MODE_UNSPECIFIED: Always trigger retrieval
19
- * @default MODE_UNSPECIFIED
20
- */
21
- mode?: 'MODE_DYNAMIC' | 'MODE_UNSPECIFIED';
12
+ const googleSearchToolArgsBaseSchema = z
13
+ .object({
14
+ searchTypes: z
15
+ .object({
16
+ webSearch: z.object({}).optional(),
17
+ imageSearch: z.object({}).optional(),
18
+ })
19
+ .optional(),
20
+
21
+ timeRangeFilter: z
22
+ .object({
23
+ startTime: z.string(),
24
+ endTime: z.string(),
25
+ })
26
+ .optional(),
27
+ })
28
+ .passthrough();
29
+
30
+ export type GoogleSearchToolArgs = z.infer<
31
+ typeof googleSearchToolArgsBaseSchema
32
+ >;
22
33
 
23
- /**
24
- * The threshold to be used in dynamic retrieval (if not set, a system default value is used).
25
- */
26
- dynamicThreshold?: number;
27
- }
28
- >({
29
- id: 'google.google_search',
30
- inputSchema: lazySchema(() =>
31
- zodSchema(
32
- z.object({
33
- mode: z
34
- .enum(['MODE_DYNAMIC', 'MODE_UNSPECIFIED'])
35
- .default('MODE_UNSPECIFIED'),
36
- dynamicThreshold: z.number().default(1),
37
- }),
38
- ),
39
- ),
40
- });
34
+ const googleSearchToolArgsSchema = lazySchema(() =>
35
+ zodSchema(googleSearchToolArgsBaseSchema),
36
+ );
37
+
38
+ export const googleSearch = createProviderToolFactory<{}, GoogleSearchToolArgs>(
39
+ {
40
+ id: 'google.google_search',
41
+ inputSchema: googleSearchToolArgsSchema,
42
+ },
43
+ );