@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 +16 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +96 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +96 -20
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +53 -11
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +53 -11
- package/dist/internal/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/convert-to-google-generative-ai-messages.ts +104 -12
- package/src/google-generative-ai-image-model.ts +55 -4
- package/src/google-generative-ai-language-model.ts +4 -1
- package/src/tool/google-search.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
UnsupportedFunctionalityError,
|
|
3
3
|
type LanguageModelV3Prompt,
|
|
4
|
+
type SharedV3Warning,
|
|
4
5
|
} from '@ai-sdk/provider';
|
|
5
6
|
import { convertToBase64 } from '@ai-sdk/provider-utils';
|
|
6
7
|
import type {
|
|
@@ -10,6 +11,48 @@ import type {
|
|
|
10
11
|
GoogleGenerativeAIPrompt,
|
|
11
12
|
} from './google-generative-ai-prompt';
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Sentinel value Google documents for replaying functionCall parts whose
|
|
16
|
+
* original thoughtSignature is not available to the client.
|
|
17
|
+
*
|
|
18
|
+
* Gemini 3 models reject `functionCall` parts that lack a `thoughtSignature`
|
|
19
|
+
* with HTTP 400 "Function call is missing a thought_signature in functionCall
|
|
20
|
+
* parts." Sending this sentinel string in place of the missing signature
|
|
21
|
+
* makes Gemini skip the validator and continue the turn.
|
|
22
|
+
*
|
|
23
|
+
* See https://ai.google.dev/gemini-api/docs/thought-signatures.
|
|
24
|
+
*/
|
|
25
|
+
export const SKIP_THOUGHT_SIGNATURE_VALIDATOR =
|
|
26
|
+
'skip_thought_signature_validator';
|
|
27
|
+
|
|
28
|
+
type GoogleProviderOptions = {
|
|
29
|
+
thought?: unknown;
|
|
30
|
+
thoughtSignature?: unknown;
|
|
31
|
+
serverToolCallId?: unknown;
|
|
32
|
+
serverToolType?: unknown;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function getGoogleProviderOptions(
|
|
36
|
+
providerOptions: Record<string, GoogleProviderOptions> | undefined,
|
|
37
|
+
providerOptionsName: string,
|
|
38
|
+
): GoogleProviderOptions | undefined {
|
|
39
|
+
const namespaces = [
|
|
40
|
+
providerOptionsName,
|
|
41
|
+
'google',
|
|
42
|
+
'googleVertex',
|
|
43
|
+
'vertex',
|
|
44
|
+
].filter((namespace, index, allNamespaces) => {
|
|
45
|
+
return allNamespaces.indexOf(namespace) === index;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
for (const namespace of namespaces) {
|
|
49
|
+
const options = providerOptions?.[namespace];
|
|
50
|
+
if (options != null) {
|
|
51
|
+
return options;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
13
56
|
const dataUrlRegex = /^data:([^;,]+);base64,(.+)$/s;
|
|
14
57
|
|
|
15
58
|
function parseBase64DataUrl(
|
|
@@ -168,17 +211,41 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
168
211
|
prompt: LanguageModelV3Prompt,
|
|
169
212
|
options?: {
|
|
170
213
|
isGemmaModel?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Whether the target model is in the Gemini 3 family. Gemini 3 enforces a
|
|
216
|
+
* `thoughtSignature` on every replayed `functionCall` part; when one is
|
|
217
|
+
* missing we inject the documented `skip_thought_signature_validator`
|
|
218
|
+
* sentinel and emit a warning via `onWarning` so the developer can find
|
|
219
|
+
* and fix the upstream serialization that lost the signature.
|
|
220
|
+
*/
|
|
221
|
+
isGemini3Model?: boolean;
|
|
171
222
|
providerOptionsName?: string;
|
|
172
223
|
supportsFunctionResponseParts?: boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Called once for the request when a Gemini 3 `functionCall` part is
|
|
226
|
+
* about to be sent without a `thoughtSignature` and the sentinel is
|
|
227
|
+
* injected.
|
|
228
|
+
*/
|
|
229
|
+
onWarning?: (warning: SharedV3Warning) => void;
|
|
173
230
|
},
|
|
174
231
|
): GoogleGenerativeAIPrompt {
|
|
175
232
|
const systemInstructionParts: Array<{ text: string }> = [];
|
|
176
233
|
const contents: Array<GoogleGenerativeAIContent> = [];
|
|
177
234
|
let systemMessagesAllowed = true;
|
|
178
235
|
const isGemmaModel = options?.isGemmaModel ?? false;
|
|
236
|
+
const isGemini3Model = options?.isGemini3Model ?? false;
|
|
179
237
|
const providerOptionsName = options?.providerOptionsName ?? 'google';
|
|
180
238
|
const supportsFunctionResponseParts =
|
|
181
239
|
options?.supportsFunctionResponseParts ?? true;
|
|
240
|
+
const onWarning = options?.onWarning;
|
|
241
|
+
|
|
242
|
+
let sentinelInjected = false;
|
|
243
|
+
const missingSignatureToolNames: string[] = [];
|
|
244
|
+
const injectSkipSignature = (toolName: string) => {
|
|
245
|
+
missingSignatureToolNames.push(toolName);
|
|
246
|
+
sentinelInjected = true;
|
|
247
|
+
return SKIP_THOUGHT_SIGNATURE_VALIDATOR;
|
|
248
|
+
};
|
|
182
249
|
|
|
183
250
|
for (const { role, content } of prompt) {
|
|
184
251
|
switch (role) {
|
|
@@ -243,11 +310,10 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
243
310
|
role: 'model',
|
|
244
311
|
parts: content
|
|
245
312
|
.map(part => {
|
|
246
|
-
const providerOpts =
|
|
247
|
-
part.providerOptions
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
: part.providerOptions?.vertex);
|
|
313
|
+
const providerOpts = getGoogleProviderOptions(
|
|
314
|
+
part.providerOptions,
|
|
315
|
+
providerOptionsName,
|
|
316
|
+
);
|
|
251
317
|
const thoughtSignature =
|
|
252
318
|
providerOpts?.thoughtSignature != null
|
|
253
319
|
? String(providerOpts.thoughtSignature)
|
|
@@ -303,6 +369,16 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
303
369
|
? String(providerOpts.serverToolType)
|
|
304
370
|
: undefined;
|
|
305
371
|
|
|
372
|
+
// For Gemini 3, every replayed functionCall part must carry a
|
|
373
|
+
// thoughtSignature or the API returns HTTP 400. If the upstream
|
|
374
|
+
// serialization layer dropped the signature, inject the
|
|
375
|
+
// documented sentinel so the request still succeeds.
|
|
376
|
+
const effectiveThoughtSignature =
|
|
377
|
+
thoughtSignature ??
|
|
378
|
+
(isGemini3Model
|
|
379
|
+
? injectSkipSignature(part.toolName)
|
|
380
|
+
: undefined);
|
|
381
|
+
|
|
306
382
|
if (serverToolCallId && serverToolType) {
|
|
307
383
|
return {
|
|
308
384
|
toolCall: {
|
|
@@ -313,7 +389,7 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
313
389
|
: part.input,
|
|
314
390
|
id: serverToolCallId,
|
|
315
391
|
},
|
|
316
|
-
thoughtSignature,
|
|
392
|
+
thoughtSignature: effectiveThoughtSignature,
|
|
317
393
|
};
|
|
318
394
|
}
|
|
319
395
|
|
|
@@ -325,7 +401,7 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
325
401
|
name: part.toolName,
|
|
326
402
|
args: part.input,
|
|
327
403
|
},
|
|
328
|
-
thoughtSignature,
|
|
404
|
+
thoughtSignature: effectiveThoughtSignature,
|
|
329
405
|
};
|
|
330
406
|
}
|
|
331
407
|
|
|
@@ -371,11 +447,10 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
371
447
|
continue;
|
|
372
448
|
}
|
|
373
449
|
|
|
374
|
-
const partProviderOpts =
|
|
375
|
-
part.providerOptions
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
: part.providerOptions?.vertex);
|
|
450
|
+
const partProviderOpts = getGoogleProviderOptions(
|
|
451
|
+
part.providerOptions,
|
|
452
|
+
providerOptionsName,
|
|
453
|
+
);
|
|
379
454
|
const serverToolCallId =
|
|
380
455
|
partProviderOpts?.serverToolCallId != null
|
|
381
456
|
? String(partProviderOpts.serverToolCallId)
|
|
@@ -465,6 +540,23 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
465
540
|
contents[0].parts.unshift({ text: systemText + '\n\n' });
|
|
466
541
|
}
|
|
467
542
|
|
|
543
|
+
if (sentinelInjected && onWarning != null) {
|
|
544
|
+
const uniqueToolNames = Array.from(new Set(missingSignatureToolNames));
|
|
545
|
+
onWarning({
|
|
546
|
+
type: 'other',
|
|
547
|
+
message:
|
|
548
|
+
`Replayed ${missingSignatureToolNames.length} \`functionCall\` part(s) ` +
|
|
549
|
+
`for a Gemini 3 model without a \`thoughtSignature\` ` +
|
|
550
|
+
`(tools: ${uniqueToolNames.map(name => `\`${name}\``).join(', ')}). ` +
|
|
551
|
+
`Injected the documented \`skip_thought_signature_validator\` sentinel ` +
|
|
552
|
+
`to keep the request from failing with HTTP 400. ` +
|
|
553
|
+
`The likely cause is application code that drops ` +
|
|
554
|
+
'`providerOptions.google.thoughtSignature` when persisting or ' +
|
|
555
|
+
'serializing assistant tool-call messages. ' +
|
|
556
|
+
'See https://ai.google.dev/gemini-api/docs/thought-signatures.',
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
468
560
|
return {
|
|
469
561
|
systemInstruction:
|
|
470
562
|
systemInstructionParts.length > 0 && !isGemmaModel
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
resolve,
|
|
15
15
|
zodSchema,
|
|
16
16
|
type FetchFunction,
|
|
17
|
-
type Resolvable,
|
|
18
17
|
type InferSchema,
|
|
18
|
+
type Resolvable,
|
|
19
19
|
} from '@ai-sdk/provider-utils';
|
|
20
20
|
import { z } from 'zod/v4';
|
|
21
21
|
import { googleFailedResponseHandler } from './google-error';
|
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
} from './google-generative-ai-image-settings';
|
|
26
26
|
import { GoogleGenerativeAILanguageModel } from './google-generative-ai-language-model';
|
|
27
27
|
import type { GoogleLanguageModelOptions } from './google-generative-ai-options';
|
|
28
|
+
import { googleSearchToolArgsBaseSchema } from './tool/google-search';
|
|
28
29
|
|
|
29
30
|
interface GoogleGenerativeAIImageModelConfig {
|
|
30
31
|
provider: string;
|
|
@@ -139,7 +140,17 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
if (googleOptions) {
|
|
142
|
-
|
|
143
|
+
const { googleSearch: imagenGoogleSearch, ...imagenOptions } =
|
|
144
|
+
googleOptions;
|
|
145
|
+
if (imagenGoogleSearch != null) {
|
|
146
|
+
warnings.push({
|
|
147
|
+
type: 'unsupported',
|
|
148
|
+
feature: 'googleSearch',
|
|
149
|
+
details:
|
|
150
|
+
'Google Search grounding is only supported on Gemini image models.',
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
Object.assign(parameters, imagenOptions);
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
const body = {
|
|
@@ -257,6 +268,18 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
|
|
|
257
268
|
{ role: 'user', content: userContent },
|
|
258
269
|
];
|
|
259
270
|
|
|
271
|
+
// Parse image-model-specific provider options so we can map them onto
|
|
272
|
+
// the underlying language-model call. `googleSearch` is the dedicated
|
|
273
|
+
// escape hatch for grounding (generateImage has no `tools` parameter).
|
|
274
|
+
const googleImageOptions = await parseProviderOptions({
|
|
275
|
+
provider: 'google',
|
|
276
|
+
providerOptions,
|
|
277
|
+
schema: googleImageModelOptionsSchema,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const { googleSearch: _strippedGoogleSearch, ...passthroughGoogleOptions } =
|
|
281
|
+
providerOptions?.google ?? {};
|
|
282
|
+
|
|
260
283
|
// Instantiate language model
|
|
261
284
|
const languageModel = new GoogleGenerativeAILanguageModel(this.modelId, {
|
|
262
285
|
provider: this.config.provider,
|
|
@@ -280,12 +303,23 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
|
|
|
280
303
|
>['aspectRatio'],
|
|
281
304
|
}
|
|
282
305
|
: undefined,
|
|
283
|
-
...(
|
|
306
|
+
...(passthroughGoogleOptions as Omit<
|
|
284
307
|
GoogleLanguageModelOptions,
|
|
285
308
|
'responseModalities' | 'imageConfig'
|
|
286
|
-
>)
|
|
309
|
+
>),
|
|
287
310
|
} satisfies GoogleLanguageModelOptions,
|
|
288
311
|
},
|
|
312
|
+
tools:
|
|
313
|
+
googleImageOptions?.googleSearch != null
|
|
314
|
+
? [
|
|
315
|
+
{
|
|
316
|
+
type: 'provider',
|
|
317
|
+
id: 'google.google_search',
|
|
318
|
+
name: 'google_search',
|
|
319
|
+
args: googleImageOptions.googleSearch,
|
|
320
|
+
},
|
|
321
|
+
]
|
|
322
|
+
: undefined,
|
|
289
323
|
headers,
|
|
290
324
|
abortSignal,
|
|
291
325
|
});
|
|
@@ -300,11 +334,17 @@ export class GoogleGenerativeAIImageModel implements ImageModelV3 {
|
|
|
300
334
|
}
|
|
301
335
|
}
|
|
302
336
|
|
|
337
|
+
const languageModelGoogleMetadata =
|
|
338
|
+
(result.providerMetadata?.google as
|
|
339
|
+
| Record<string, unknown>
|
|
340
|
+
| undefined) ?? {};
|
|
341
|
+
|
|
303
342
|
return {
|
|
304
343
|
images,
|
|
305
344
|
warnings,
|
|
306
345
|
providerMetadata: {
|
|
307
346
|
google: {
|
|
347
|
+
...languageModelGoogleMetadata,
|
|
308
348
|
images: images.map(() => ({})),
|
|
309
349
|
},
|
|
310
350
|
},
|
|
@@ -350,6 +390,17 @@ const googleImageModelOptionsSchema = lazySchema(() =>
|
|
|
350
390
|
.enum(['dont_allow', 'allow_adult', 'allow_all'])
|
|
351
391
|
.nullish(),
|
|
352
392
|
aspectRatio: z.enum(['1:1', '3:4', '4:3', '9:16', '16:9']).nullish(),
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Enable Google Search grounding for Gemini image models. The value is
|
|
396
|
+
* forwarded as the args of the `google.tools.googleSearch` provider
|
|
397
|
+
* tool on the underlying language-model call. Pass `{}` for defaults.
|
|
398
|
+
*
|
|
399
|
+
* `generateImage` does not accept a `tools` parameter, so this is the
|
|
400
|
+
* dedicated escape hatch for grounding image generation the same way
|
|
401
|
+
* `generateText` does.
|
|
402
|
+
*/
|
|
403
|
+
googleSearch: googleSearchToolArgsBaseSchema.optional(),
|
|
353
404
|
}),
|
|
354
405
|
),
|
|
355
406
|
);
|
|
@@ -193,14 +193,17 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
193
193
|
: googleOptions?.serviceTier;
|
|
194
194
|
|
|
195
195
|
const isGemmaModel = this.modelId.toLowerCase().startsWith('gemma-');
|
|
196
|
-
const
|
|
196
|
+
const isGemini3Model = /^gemini-3[.-]/.test(this.modelId);
|
|
197
|
+
const supportsFunctionResponseParts = isGemini3Model;
|
|
197
198
|
|
|
198
199
|
const { contents, systemInstruction } = convertToGoogleGenerativeAIMessages(
|
|
199
200
|
prompt,
|
|
200
201
|
{
|
|
201
202
|
isGemmaModel,
|
|
203
|
+
isGemini3Model,
|
|
202
204
|
providerOptionsName,
|
|
203
205
|
supportsFunctionResponseParts,
|
|
206
|
+
onWarning: warning => warnings.push(warning),
|
|
204
207
|
},
|
|
205
208
|
);
|
|
206
209
|
|
|
@@ -9,7 +9,7 @@ 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
|
-
const googleSearchToolArgsBaseSchema = z
|
|
12
|
+
export const googleSearchToolArgsBaseSchema = z
|
|
13
13
|
.object({
|
|
14
14
|
searchTypes: z
|
|
15
15
|
.object({
|