Skip to content

Commit 217a997

Browse files
Backport: fix(google): use VALIDATED function calling mode when any tool has strict:true (#13098)
This is an automated backport of #12967 to the release-v6.0 branch. FYI @sleitor Co-authored-by: Dmitrii <sleitor@list.ru> Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
1 parent 5849553 commit 217a997

File tree

3 files changed

+119
-5
lines changed

3 files changed

+119
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ai-sdk/google': patch
3+
---
4+
5+
fix(google): use VALIDATED function calling mode when any tool has strict:true

packages/google/src/google-prepare-tools.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,3 +560,98 @@ it('should add warnings for google maps on unsupported models', () => {
560560
]
561561
`);
562562
});
563+
564+
it('should use VALIDATED mode when any function tool has strict: true', () => {
565+
const result = prepareTools({
566+
tools: [
567+
{
568+
type: 'function',
569+
name: 'createMeeting',
570+
description: 'Create a meeting',
571+
inputSchema: {
572+
type: 'object',
573+
properties: { title: { type: 'string' } },
574+
required: ['title'],
575+
additionalProperties: false,
576+
},
577+
strict: true,
578+
},
579+
],
580+
modelId: 'gemini-3-flash-preview',
581+
});
582+
expect(result.toolConfig).toEqual({
583+
functionCallingConfig: { mode: 'VALIDATED' },
584+
});
585+
expect(result.toolWarnings).toEqual([]);
586+
});
587+
588+
it('should use VALIDATED mode with toolChoice auto when strict: true', () => {
589+
const result = prepareTools({
590+
tools: [
591+
{
592+
type: 'function',
593+
name: 'getWeather',
594+
description: 'Get weather',
595+
inputSchema: {
596+
type: 'object',
597+
properties: { city: { type: 'string' } },
598+
required: ['city'],
599+
additionalProperties: false,
600+
},
601+
strict: true,
602+
},
603+
],
604+
toolChoice: { type: 'auto' },
605+
modelId: 'gemini-3-flash-preview',
606+
});
607+
expect(result.toolConfig).toEqual({
608+
functionCallingConfig: { mode: 'VALIDATED' },
609+
});
610+
});
611+
612+
it('should use VALIDATED mode with toolChoice required when strict: true', () => {
613+
const result = prepareTools({
614+
tools: [
615+
{
616+
type: 'function',
617+
name: 'getWeather',
618+
description: 'Get weather',
619+
inputSchema: {
620+
type: 'object',
621+
properties: { city: { type: 'string' } },
622+
required: ['city'],
623+
additionalProperties: false,
624+
},
625+
strict: true,
626+
},
627+
],
628+
toolChoice: { type: 'required' },
629+
modelId: 'gemini-3-flash-preview',
630+
});
631+
expect(result.toolConfig).toEqual({
632+
functionCallingConfig: { mode: 'VALIDATED' },
633+
});
634+
});
635+
636+
it('should use AUTO mode when no tools have strict: true', () => {
637+
const result = prepareTools({
638+
tools: [
639+
{
640+
type: 'function',
641+
name: 'getWeather',
642+
description: 'Get weather',
643+
inputSchema: {
644+
type: 'object',
645+
properties: { city: { type: 'string' } },
646+
required: ['city'],
647+
additionalProperties: false,
648+
},
649+
},
650+
],
651+
toolChoice: { type: 'auto' },
652+
modelId: 'gemini-3-flash-preview',
653+
});
654+
expect(result.toolConfig).toEqual({
655+
functionCallingConfig: { mode: 'AUTO' },
656+
});
657+
});

packages/google/src/google-prepare-tools.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function prepareTools({
3131
| undefined
3232
| {
3333
functionCallingConfig: {
34-
mode: 'AUTO' | 'NONE' | 'ANY';
34+
mode: 'AUTO' | 'NONE' | 'ANY' | 'VALIDATED';
3535
allowedFunctionNames?: string[];
3636
};
3737
};
@@ -189,6 +189,7 @@ export function prepareTools({
189189
}
190190

191191
const functionDeclarations = [];
192+
let hasStrictTools = false;
192193
for (const tool of tools) {
193194
switch (tool.type) {
194195
case 'function':
@@ -197,6 +198,9 @@ export function prepareTools({
197198
description: tool.description ?? '',
198199
parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema),
199200
});
201+
if (tool.strict === true) {
202+
hasStrictTools = true;
203+
}
200204
break;
201205
default:
202206
toolWarnings.push({ type: 'unsupported-tool', tool });
@@ -207,7 +211,9 @@ export function prepareTools({
207211
if (toolChoice == null) {
208212
return {
209213
tools: [{ functionDeclarations }],
210-
toolConfig: undefined,
214+
toolConfig: hasStrictTools
215+
? { functionCallingConfig: { mode: 'VALIDATED' } }
216+
: undefined,
211217
toolWarnings,
212218
};
213219
}
@@ -218,7 +224,11 @@ export function prepareTools({
218224
case 'auto':
219225
return {
220226
tools: [{ functionDeclarations }],
221-
toolConfig: { functionCallingConfig: { mode: 'AUTO' } },
227+
toolConfig: {
228+
functionCallingConfig: {
229+
mode: hasStrictTools ? 'VALIDATED' : 'AUTO',
230+
},
231+
},
222232
toolWarnings,
223233
};
224234
case 'none':
@@ -230,15 +240,19 @@ export function prepareTools({
230240
case 'required':
231241
return {
232242
tools: [{ functionDeclarations }],
233-
toolConfig: { functionCallingConfig: { mode: 'ANY' } },
243+
toolConfig: {
244+
functionCallingConfig: {
245+
mode: hasStrictTools ? 'VALIDATED' : 'ANY',
246+
},
247+
},
234248
toolWarnings,
235249
};
236250
case 'tool':
237251
return {
238252
tools: [{ functionDeclarations }],
239253
toolConfig: {
240254
functionCallingConfig: {
241-
mode: 'ANY',
255+
mode: hasStrictTools ? 'VALIDATED' : 'ANY',
242256
allowedFunctionNames: [toolChoice.toolName],
243257
},
244258
},

0 commit comments

Comments
 (0)