@ai-sdk/google 3.0.9 → 3.0.11
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 +14 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/__snapshots__/google-generative-ai-embedding-model.test.ts.snap +33 -0
- package/src/convert-google-generative-ai-usage.ts +51 -0
- package/src/convert-json-schema-to-openapi-schema.test.ts +684 -0
- package/src/convert-json-schema-to-openapi-schema.ts +158 -0
- package/src/convert-to-google-generative-ai-messages.test.ts +495 -0
- package/src/convert-to-google-generative-ai-messages.ts +232 -0
- package/src/get-model-path.test.ts +16 -0
- package/src/get-model-path.ts +3 -0
- package/src/google-error.ts +26 -0
- package/src/google-generative-ai-embedding-model.test.ts +204 -0
- package/src/google-generative-ai-embedding-model.ts +159 -0
- package/src/google-generative-ai-embedding-options.ts +52 -0
- package/src/google-generative-ai-image-model.test.ts +411 -0
- package/src/google-generative-ai-image-model.ts +184 -0
- package/src/google-generative-ai-image-settings.ts +12 -0
- package/src/google-generative-ai-language-model.test.ts +4616 -0
- package/src/google-generative-ai-language-model.ts +1009 -0
- package/src/google-generative-ai-options.ts +193 -0
- package/src/google-generative-ai-prompt.ts +38 -0
- package/src/google-prepare-tools.test.ts +474 -0
- package/src/google-prepare-tools.ts +264 -0
- package/src/google-provider.test.ts +307 -0
- package/src/google-provider.ts +201 -0
- package/src/google-supported-file-url.test.ts +57 -0
- package/src/google-supported-file-url.ts +20 -0
- package/src/google-tools.ts +71 -0
- package/src/index.ts +11 -0
- package/src/internal/index.ts +3 -0
- package/src/map-google-generative-ai-finish-reason.ts +29 -0
- package/src/tool/code-execution.ts +35 -0
- package/src/tool/enterprise-web-search.ts +18 -0
- package/src/tool/file-search.ts +51 -0
- package/src/tool/google-maps.ts +14 -0
- package/src/tool/google-search.ts +40 -0
- package/src/tool/url-context.ts +16 -0
- package/src/tool/vertex-rag-store.ts +31 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
|
|
4
|
+
export type GoogleGenerativeAIModelId =
|
|
5
|
+
// Stable models
|
|
6
|
+
// https://ai.google.dev/gemini-api/docs/models/gemini
|
|
7
|
+
| 'gemini-1.5-flash'
|
|
8
|
+
| 'gemini-1.5-flash-latest'
|
|
9
|
+
| 'gemini-1.5-flash-001'
|
|
10
|
+
| 'gemini-1.5-flash-002'
|
|
11
|
+
| 'gemini-1.5-flash-8b'
|
|
12
|
+
| 'gemini-1.5-flash-8b-latest'
|
|
13
|
+
| 'gemini-1.5-flash-8b-001'
|
|
14
|
+
| 'gemini-1.5-pro'
|
|
15
|
+
| 'gemini-1.5-pro-latest'
|
|
16
|
+
| 'gemini-1.5-pro-001'
|
|
17
|
+
| 'gemini-1.5-pro-002'
|
|
18
|
+
| 'gemini-2.0-flash'
|
|
19
|
+
| 'gemini-2.0-flash-001'
|
|
20
|
+
| 'gemini-2.0-flash-live-001'
|
|
21
|
+
| 'gemini-2.0-flash-lite'
|
|
22
|
+
| 'gemini-2.0-pro-exp-02-05'
|
|
23
|
+
| 'gemini-2.0-flash-thinking-exp-01-21'
|
|
24
|
+
| 'gemini-2.0-flash-exp'
|
|
25
|
+
| 'gemini-2.5-pro'
|
|
26
|
+
| 'gemini-2.5-flash'
|
|
27
|
+
| 'gemini-2.5-flash-image-preview'
|
|
28
|
+
| 'gemini-2.5-flash-lite'
|
|
29
|
+
| 'gemini-2.5-flash-lite-preview-09-2025'
|
|
30
|
+
| 'gemini-2.5-flash-preview-04-17'
|
|
31
|
+
| 'gemini-2.5-flash-preview-09-2025'
|
|
32
|
+
| 'gemini-3-pro-preview'
|
|
33
|
+
| 'gemini-3-pro-image-preview'
|
|
34
|
+
| 'gemini-3-flash-preview'
|
|
35
|
+
// latest version
|
|
36
|
+
// https://ai.google.dev/gemini-api/docs/models#latest
|
|
37
|
+
| 'gemini-pro-latest'
|
|
38
|
+
| 'gemini-flash-latest'
|
|
39
|
+
| 'gemini-flash-lite-latest'
|
|
40
|
+
// Experimental models
|
|
41
|
+
// https://ai.google.dev/gemini-api/docs/models/experimental-models
|
|
42
|
+
| 'gemini-2.5-pro-exp-03-25'
|
|
43
|
+
| 'gemini-exp-1206'
|
|
44
|
+
| 'gemma-3-12b-it'
|
|
45
|
+
| 'gemma-3-27b-it'
|
|
46
|
+
| (string & {});
|
|
47
|
+
|
|
48
|
+
export const googleGenerativeAIProviderOptions = lazySchema(() =>
|
|
49
|
+
zodSchema(
|
|
50
|
+
z.object({
|
|
51
|
+
responseModalities: z.array(z.enum(['TEXT', 'IMAGE'])).optional(),
|
|
52
|
+
|
|
53
|
+
thinkingConfig: z
|
|
54
|
+
.object({
|
|
55
|
+
thinkingBudget: z.number().optional(),
|
|
56
|
+
includeThoughts: z.boolean().optional(),
|
|
57
|
+
// https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high#thinking_level
|
|
58
|
+
thinkingLevel: z
|
|
59
|
+
.enum(['minimal', 'low', 'medium', 'high'])
|
|
60
|
+
.optional(),
|
|
61
|
+
})
|
|
62
|
+
.optional(),
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Optional.
|
|
66
|
+
* The name of the cached content used as context to serve the prediction.
|
|
67
|
+
* Format: cachedContents/{cachedContent}
|
|
68
|
+
*/
|
|
69
|
+
cachedContent: z.string().optional(),
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Optional. Enable structured output. Default is true.
|
|
73
|
+
*
|
|
74
|
+
* This is useful when the JSON Schema contains elements that are
|
|
75
|
+
* not supported by the OpenAPI schema version that
|
|
76
|
+
* Google Generative AI uses. You can use this to disable
|
|
77
|
+
* structured outputs if you need to.
|
|
78
|
+
*/
|
|
79
|
+
structuredOutputs: z.boolean().optional(),
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Optional. A list of unique safety settings for blocking unsafe content.
|
|
83
|
+
*/
|
|
84
|
+
safetySettings: z
|
|
85
|
+
.array(
|
|
86
|
+
z.object({
|
|
87
|
+
category: z.enum([
|
|
88
|
+
'HARM_CATEGORY_UNSPECIFIED',
|
|
89
|
+
'HARM_CATEGORY_HATE_SPEECH',
|
|
90
|
+
'HARM_CATEGORY_DANGEROUS_CONTENT',
|
|
91
|
+
'HARM_CATEGORY_HARASSMENT',
|
|
92
|
+
'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
93
|
+
'HARM_CATEGORY_CIVIC_INTEGRITY',
|
|
94
|
+
]),
|
|
95
|
+
threshold: z.enum([
|
|
96
|
+
'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
97
|
+
'BLOCK_LOW_AND_ABOVE',
|
|
98
|
+
'BLOCK_MEDIUM_AND_ABOVE',
|
|
99
|
+
'BLOCK_ONLY_HIGH',
|
|
100
|
+
'BLOCK_NONE',
|
|
101
|
+
'OFF',
|
|
102
|
+
]),
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
.optional(),
|
|
106
|
+
|
|
107
|
+
threshold: z
|
|
108
|
+
.enum([
|
|
109
|
+
'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
110
|
+
'BLOCK_LOW_AND_ABOVE',
|
|
111
|
+
'BLOCK_MEDIUM_AND_ABOVE',
|
|
112
|
+
'BLOCK_ONLY_HIGH',
|
|
113
|
+
'BLOCK_NONE',
|
|
114
|
+
'OFF',
|
|
115
|
+
])
|
|
116
|
+
.optional(),
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Optional. Enables timestamp understanding for audio-only files.
|
|
120
|
+
*
|
|
121
|
+
* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/audio-understanding
|
|
122
|
+
*/
|
|
123
|
+
audioTimestamp: z.boolean().optional(),
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Optional. Defines labels used in billing reports. Available on Vertex AI only.
|
|
127
|
+
*
|
|
128
|
+
* https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/add-labels-to-api-calls
|
|
129
|
+
*/
|
|
130
|
+
labels: z.record(z.string(), z.string()).optional(),
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Optional. If specified, the media resolution specified will be used.
|
|
134
|
+
*
|
|
135
|
+
* https://ai.google.dev/api/generate-content#MediaResolution
|
|
136
|
+
*/
|
|
137
|
+
mediaResolution: z
|
|
138
|
+
.enum([
|
|
139
|
+
'MEDIA_RESOLUTION_UNSPECIFIED',
|
|
140
|
+
'MEDIA_RESOLUTION_LOW',
|
|
141
|
+
'MEDIA_RESOLUTION_MEDIUM',
|
|
142
|
+
'MEDIA_RESOLUTION_HIGH',
|
|
143
|
+
])
|
|
144
|
+
.optional(),
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Optional. Configures the image generation aspect ratio for Gemini models.
|
|
148
|
+
*
|
|
149
|
+
* https://ai.google.dev/gemini-api/docs/image-generation#aspect_ratios
|
|
150
|
+
*/
|
|
151
|
+
imageConfig: z
|
|
152
|
+
.object({
|
|
153
|
+
aspectRatio: z
|
|
154
|
+
.enum([
|
|
155
|
+
'1:1',
|
|
156
|
+
'2:3',
|
|
157
|
+
'3:2',
|
|
158
|
+
'3:4',
|
|
159
|
+
'4:3',
|
|
160
|
+
'4:5',
|
|
161
|
+
'5:4',
|
|
162
|
+
'9:16',
|
|
163
|
+
'16:9',
|
|
164
|
+
'21:9',
|
|
165
|
+
])
|
|
166
|
+
.optional(),
|
|
167
|
+
imageSize: z.enum(['1K', '2K', '4K']).optional(),
|
|
168
|
+
})
|
|
169
|
+
.optional(),
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Optional. Configuration for grounding retrieval.
|
|
173
|
+
* Used to provide location context for Google Maps and Google Search grounding.
|
|
174
|
+
*
|
|
175
|
+
* https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
|
|
176
|
+
*/
|
|
177
|
+
retrievalConfig: z
|
|
178
|
+
.object({
|
|
179
|
+
latLng: z
|
|
180
|
+
.object({
|
|
181
|
+
latitude: z.number(),
|
|
182
|
+
longitude: z.number(),
|
|
183
|
+
})
|
|
184
|
+
.optional(),
|
|
185
|
+
})
|
|
186
|
+
.optional(),
|
|
187
|
+
}),
|
|
188
|
+
),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
export type GoogleGenerativeAIProviderOptions = InferSchema<
|
|
192
|
+
typeof googleGenerativeAIProviderOptions
|
|
193
|
+
>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GroundingMetadataSchema,
|
|
3
|
+
UrlContextMetadataSchema,
|
|
4
|
+
} from './google-generative-ai-language-model';
|
|
5
|
+
import { type SafetyRatingSchema } from './google-generative-ai-language-model';
|
|
6
|
+
|
|
7
|
+
export type GoogleGenerativeAIPrompt = {
|
|
8
|
+
systemInstruction?: GoogleGenerativeAISystemInstruction;
|
|
9
|
+
contents: Array<GoogleGenerativeAIContent>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type GoogleGenerativeAISystemInstruction = {
|
|
13
|
+
parts: Array<{ text: string }>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type GoogleGenerativeAIContent = {
|
|
17
|
+
role: 'user' | 'model';
|
|
18
|
+
parts: Array<GoogleGenerativeAIContentPart>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type GoogleGenerativeAIContentPart =
|
|
22
|
+
| { text: string; thought?: boolean; thoughtSignature?: string }
|
|
23
|
+
| { inlineData: { mimeType: string; data: string } }
|
|
24
|
+
| { functionCall: { name: string; args: unknown }; thoughtSignature?: string }
|
|
25
|
+
| { functionResponse: { name: string; response: unknown } }
|
|
26
|
+
| { fileData: { mimeType: string; fileUri: string } };
|
|
27
|
+
|
|
28
|
+
export type GoogleGenerativeAIGroundingMetadata = GroundingMetadataSchema;
|
|
29
|
+
|
|
30
|
+
export type GoogleGenerativeAIUrlContextMetadata = UrlContextMetadataSchema;
|
|
31
|
+
|
|
32
|
+
export type GoogleGenerativeAISafetyRating = SafetyRatingSchema;
|
|
33
|
+
|
|
34
|
+
export interface GoogleGenerativeAIProviderMetadata {
|
|
35
|
+
groundingMetadata: GoogleGenerativeAIGroundingMetadata | null;
|
|
36
|
+
urlContextMetadata: GoogleGenerativeAIUrlContextMetadata | null;
|
|
37
|
+
safetyRatings: GoogleGenerativeAISafetyRating[] | null;
|
|
38
|
+
}
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { LanguageModelV3ProviderTool } from '@ai-sdk/provider';
|
|
2
|
+
import { expect, it } from 'vitest';
|
|
3
|
+
import { prepareTools } from './google-prepare-tools';
|
|
4
|
+
|
|
5
|
+
it('should return undefined tools and tool_choice when tools are null', () => {
|
|
6
|
+
const result = prepareTools({
|
|
7
|
+
tools: undefined,
|
|
8
|
+
modelId: 'gemini-2.5-flash',
|
|
9
|
+
});
|
|
10
|
+
expect(result).toEqual({
|
|
11
|
+
tools: undefined,
|
|
12
|
+
tool_choice: undefined,
|
|
13
|
+
toolWarnings: [],
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return undefined tools and tool_choice when tools are empty', () => {
|
|
18
|
+
const result = prepareTools({ tools: [], modelId: 'gemini-2.5-flash' });
|
|
19
|
+
expect(result).toEqual({
|
|
20
|
+
tools: undefined,
|
|
21
|
+
tool_choice: undefined,
|
|
22
|
+
toolWarnings: [],
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should correctly prepare function tools', () => {
|
|
27
|
+
const result = prepareTools({
|
|
28
|
+
tools: [
|
|
29
|
+
{
|
|
30
|
+
type: 'function',
|
|
31
|
+
name: 'testFunction',
|
|
32
|
+
description: 'A test function',
|
|
33
|
+
inputSchema: { type: 'object', properties: {} },
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
modelId: 'gemini-2.5-flash',
|
|
37
|
+
});
|
|
38
|
+
expect(result.tools).toEqual([
|
|
39
|
+
{
|
|
40
|
+
functionDeclarations: [
|
|
41
|
+
{
|
|
42
|
+
name: 'testFunction',
|
|
43
|
+
description: 'A test function',
|
|
44
|
+
parameters: undefined,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
expect(result.toolConfig).toBeUndefined();
|
|
50
|
+
expect(result.toolWarnings).toEqual([]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should correctly prepare provider-defined tools as array', () => {
|
|
54
|
+
const result = prepareTools({
|
|
55
|
+
tools: [
|
|
56
|
+
{
|
|
57
|
+
type: 'provider',
|
|
58
|
+
id: 'google.google_search',
|
|
59
|
+
name: 'google_search',
|
|
60
|
+
args: {},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'provider',
|
|
64
|
+
id: 'google.url_context',
|
|
65
|
+
name: 'url_context',
|
|
66
|
+
args: {},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'provider',
|
|
70
|
+
id: 'google.file_search',
|
|
71
|
+
name: 'file_search',
|
|
72
|
+
args: { fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'] },
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
modelId: 'gemini-2.5-flash',
|
|
76
|
+
});
|
|
77
|
+
expect(result.tools).toEqual([
|
|
78
|
+
{ googleSearch: {} },
|
|
79
|
+
{ urlContext: {} },
|
|
80
|
+
{
|
|
81
|
+
fileSearch: {
|
|
82
|
+
fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
expect(result.toolConfig).toBeUndefined();
|
|
87
|
+
expect(result.toolWarnings).toEqual([]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should correctly prepare single provider-defined tool', () => {
|
|
91
|
+
const result = prepareTools({
|
|
92
|
+
tools: [
|
|
93
|
+
{
|
|
94
|
+
type: 'provider',
|
|
95
|
+
id: 'google.google_search',
|
|
96
|
+
name: 'google_search',
|
|
97
|
+
args: {},
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
modelId: 'gemini-2.5-flash',
|
|
101
|
+
});
|
|
102
|
+
expect(result.tools).toEqual([{ googleSearch: {} }]);
|
|
103
|
+
expect(result.toolConfig).toBeUndefined();
|
|
104
|
+
expect(result.toolWarnings).toEqual([]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should add warnings for unsupported tools', () => {
|
|
108
|
+
const result = prepareTools({
|
|
109
|
+
tools: [
|
|
110
|
+
{
|
|
111
|
+
type: 'provider',
|
|
112
|
+
id: 'unsupported.tool',
|
|
113
|
+
name: 'unsupported_tool',
|
|
114
|
+
args: {},
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
modelId: 'gemini-2.5-flash',
|
|
118
|
+
});
|
|
119
|
+
expect(result.tools).toBeUndefined();
|
|
120
|
+
expect(result.toolConfig).toBeUndefined();
|
|
121
|
+
expect(result.toolWarnings).toMatchInlineSnapshot(`
|
|
122
|
+
[
|
|
123
|
+
{
|
|
124
|
+
"feature": "provider-defined tool unsupported.tool",
|
|
125
|
+
"type": "unsupported",
|
|
126
|
+
},
|
|
127
|
+
]
|
|
128
|
+
`);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should add warnings for file search on unsupported models', () => {
|
|
132
|
+
const tool: LanguageModelV3ProviderTool = {
|
|
133
|
+
type: 'provider' as const,
|
|
134
|
+
id: 'google.file_search',
|
|
135
|
+
name: 'file_search',
|
|
136
|
+
args: { fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'] },
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const result = prepareTools({
|
|
140
|
+
tools: [tool],
|
|
141
|
+
modelId: 'gemini-1.5-flash-8b',
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(result.tools).toBeUndefined();
|
|
145
|
+
expect(result.toolWarnings).toMatchInlineSnapshot(`
|
|
146
|
+
[
|
|
147
|
+
{
|
|
148
|
+
"details": "The file search tool is only supported with Gemini 2.5 models and Gemini 3 models.",
|
|
149
|
+
"feature": "provider-defined tool google.file_search",
|
|
150
|
+
"type": "unsupported",
|
|
151
|
+
},
|
|
152
|
+
]
|
|
153
|
+
`);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should correctly prepare file search tool for gemini-2.5 models', () => {
|
|
157
|
+
const result = prepareTools({
|
|
158
|
+
tools: [
|
|
159
|
+
{
|
|
160
|
+
type: 'provider',
|
|
161
|
+
id: 'google.file_search',
|
|
162
|
+
name: 'file_search',
|
|
163
|
+
args: {
|
|
164
|
+
fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
|
|
165
|
+
metadataFilter: 'author=Robert Graves',
|
|
166
|
+
topK: 5,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
modelId: 'gemini-2.5-pro',
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(result.tools).toEqual([
|
|
174
|
+
{
|
|
175
|
+
fileSearch: {
|
|
176
|
+
fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
|
|
177
|
+
metadataFilter: 'author=Robert Graves',
|
|
178
|
+
topK: 5,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
]);
|
|
182
|
+
expect(result.toolWarnings).toEqual([]);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should correctly prepare file search tool for gemini-3 models', () => {
|
|
186
|
+
const result = prepareTools({
|
|
187
|
+
tools: [
|
|
188
|
+
{
|
|
189
|
+
type: 'provider',
|
|
190
|
+
id: 'google.file_search',
|
|
191
|
+
name: 'file_search',
|
|
192
|
+
args: {
|
|
193
|
+
fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
|
|
194
|
+
metadataFilter: 'author=Robert Graves',
|
|
195
|
+
topK: 5,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
modelId: 'gemini-3-pro-preview',
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(result.tools).toEqual([
|
|
203
|
+
{
|
|
204
|
+
fileSearch: {
|
|
205
|
+
fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
|
|
206
|
+
metadataFilter: 'author=Robert Graves',
|
|
207
|
+
topK: 5,
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
]);
|
|
211
|
+
expect(result.toolWarnings).toEqual([]);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should handle tool choice "auto"', () => {
|
|
215
|
+
const result = prepareTools({
|
|
216
|
+
tools: [
|
|
217
|
+
{
|
|
218
|
+
type: 'function',
|
|
219
|
+
name: 'testFunction',
|
|
220
|
+
description: 'Test',
|
|
221
|
+
inputSchema: {},
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
toolChoice: { type: 'auto' },
|
|
225
|
+
modelId: 'gemini-2.5-flash',
|
|
226
|
+
});
|
|
227
|
+
expect(result.toolConfig).toEqual({
|
|
228
|
+
functionCallingConfig: { mode: 'AUTO' },
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should handle tool choice "required"', () => {
|
|
233
|
+
const result = prepareTools({
|
|
234
|
+
tools: [
|
|
235
|
+
{
|
|
236
|
+
type: 'function',
|
|
237
|
+
name: 'testFunction',
|
|
238
|
+
description: 'Test',
|
|
239
|
+
inputSchema: {},
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
toolChoice: { type: 'required' },
|
|
243
|
+
modelId: 'gemini-2.5-flash',
|
|
244
|
+
});
|
|
245
|
+
expect(result.toolConfig).toEqual({
|
|
246
|
+
functionCallingConfig: { mode: 'ANY' },
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should handle tool choice "none"', () => {
|
|
251
|
+
const result = prepareTools({
|
|
252
|
+
tools: [
|
|
253
|
+
{
|
|
254
|
+
type: 'function',
|
|
255
|
+
name: 'testFunction',
|
|
256
|
+
description: 'Test',
|
|
257
|
+
inputSchema: {},
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
toolChoice: { type: 'none' },
|
|
261
|
+
modelId: 'gemini-2.5-flash',
|
|
262
|
+
});
|
|
263
|
+
expect(result.tools).toEqual([
|
|
264
|
+
{
|
|
265
|
+
functionDeclarations: [
|
|
266
|
+
{
|
|
267
|
+
name: 'testFunction',
|
|
268
|
+
description: 'Test',
|
|
269
|
+
parameters: {},
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
},
|
|
273
|
+
]);
|
|
274
|
+
expect(result.toolConfig).toEqual({
|
|
275
|
+
functionCallingConfig: { mode: 'NONE' },
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should handle tool choice "tool"', () => {
|
|
280
|
+
const result = prepareTools({
|
|
281
|
+
tools: [
|
|
282
|
+
{
|
|
283
|
+
type: 'function',
|
|
284
|
+
name: 'testFunction',
|
|
285
|
+
description: 'Test',
|
|
286
|
+
inputSchema: {},
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
toolChoice: { type: 'tool', toolName: 'testFunction' },
|
|
290
|
+
modelId: 'gemini-2.5-flash',
|
|
291
|
+
});
|
|
292
|
+
expect(result.toolConfig).toEqual({
|
|
293
|
+
functionCallingConfig: {
|
|
294
|
+
mode: 'ANY',
|
|
295
|
+
allowedFunctionNames: ['testFunction'],
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('should warn when mixing function and provider-defined tools', () => {
|
|
301
|
+
const result = prepareTools({
|
|
302
|
+
tools: [
|
|
303
|
+
{
|
|
304
|
+
type: 'function',
|
|
305
|
+
name: 'testFunction',
|
|
306
|
+
description: 'A test function',
|
|
307
|
+
inputSchema: { type: 'object', properties: {} },
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
type: 'provider',
|
|
311
|
+
id: 'google.google_search',
|
|
312
|
+
name: 'google_search',
|
|
313
|
+
args: {},
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
modelId: 'gemini-2.5-flash',
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
expect(result.tools).toEqual([{ googleSearch: {} }]);
|
|
320
|
+
|
|
321
|
+
expect(result.toolWarnings).toMatchInlineSnapshot(`
|
|
322
|
+
[
|
|
323
|
+
{
|
|
324
|
+
"feature": "combination of function and provider-defined tools",
|
|
325
|
+
"type": "unsupported",
|
|
326
|
+
},
|
|
327
|
+
]
|
|
328
|
+
`);
|
|
329
|
+
|
|
330
|
+
expect(result.toolConfig).toBeUndefined();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should handle tool choice with mixed tools (provider-defined tools only)', () => {
|
|
334
|
+
const result = prepareTools({
|
|
335
|
+
tools: [
|
|
336
|
+
{
|
|
337
|
+
type: 'function',
|
|
338
|
+
name: 'testFunction',
|
|
339
|
+
description: 'A test function',
|
|
340
|
+
inputSchema: { type: 'object', properties: {} },
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
type: 'provider',
|
|
344
|
+
id: 'google.google_search',
|
|
345
|
+
name: 'google_search',
|
|
346
|
+
args: {},
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
toolChoice: { type: 'auto' },
|
|
350
|
+
modelId: 'gemini-2.5-flash',
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
expect(result.tools).toEqual([{ googleSearch: {} }]);
|
|
354
|
+
|
|
355
|
+
expect(result.toolConfig).toEqual(undefined);
|
|
356
|
+
|
|
357
|
+
expect(result.toolWarnings).toMatchInlineSnapshot(`
|
|
358
|
+
[
|
|
359
|
+
{
|
|
360
|
+
"feature": "combination of function and provider-defined tools",
|
|
361
|
+
"type": "unsupported",
|
|
362
|
+
},
|
|
363
|
+
]
|
|
364
|
+
`);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should handle latest modelId for provider-defined tools correctly', () => {
|
|
368
|
+
const result = prepareTools({
|
|
369
|
+
tools: [
|
|
370
|
+
{
|
|
371
|
+
type: 'provider',
|
|
372
|
+
id: 'google.google_search',
|
|
373
|
+
name: 'google_search',
|
|
374
|
+
args: {},
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
modelId: 'gemini-flash-latest',
|
|
378
|
+
});
|
|
379
|
+
expect(result.tools).toEqual([{ googleSearch: {} }]);
|
|
380
|
+
expect(result.toolConfig).toBeUndefined();
|
|
381
|
+
expect(result.toolWarnings).toEqual([]);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should handle gemini-3 modelId for provider-defined tools correctly', () => {
|
|
385
|
+
const result = prepareTools({
|
|
386
|
+
tools: [
|
|
387
|
+
{
|
|
388
|
+
type: 'provider',
|
|
389
|
+
id: 'google.google_search',
|
|
390
|
+
name: 'google_search',
|
|
391
|
+
args: {},
|
|
392
|
+
},
|
|
393
|
+
],
|
|
394
|
+
modelId: 'gemini-3-pro-preview',
|
|
395
|
+
});
|
|
396
|
+
expect(result.tools).toEqual([{ googleSearch: {} }]);
|
|
397
|
+
expect(result.toolConfig).toBeUndefined();
|
|
398
|
+
expect(result.toolWarnings).toEqual([]);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('should handle code execution tool', () => {
|
|
402
|
+
const result = prepareTools({
|
|
403
|
+
tools: [
|
|
404
|
+
{
|
|
405
|
+
type: 'provider',
|
|
406
|
+
id: 'google.code_execution',
|
|
407
|
+
name: 'code_execution',
|
|
408
|
+
args: {},
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
modelId: 'gemini-2.5-flash',
|
|
412
|
+
});
|
|
413
|
+
expect(result.tools).toEqual([{ codeExecution: {} }]);
|
|
414
|
+
expect(result.toolConfig).toBeUndefined();
|
|
415
|
+
expect(result.toolWarnings).toEqual([]);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('should handle url context tool alone', () => {
|
|
419
|
+
const result = prepareTools({
|
|
420
|
+
tools: [
|
|
421
|
+
{
|
|
422
|
+
type: 'provider',
|
|
423
|
+
id: 'google.url_context',
|
|
424
|
+
name: 'url_context',
|
|
425
|
+
args: {},
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
modelId: 'gemini-2.5-flash',
|
|
429
|
+
});
|
|
430
|
+
expect(result.tools).toEqual([{ urlContext: {} }]);
|
|
431
|
+
expect(result.toolConfig).toBeUndefined();
|
|
432
|
+
expect(result.toolWarnings).toEqual([]);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should handle google maps tool', () => {
|
|
436
|
+
const result = prepareTools({
|
|
437
|
+
tools: [
|
|
438
|
+
{
|
|
439
|
+
type: 'provider',
|
|
440
|
+
id: 'google.google_maps',
|
|
441
|
+
name: 'google_maps',
|
|
442
|
+
args: {},
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
modelId: 'gemini-2.5-flash',
|
|
446
|
+
});
|
|
447
|
+
expect(result.tools).toEqual([{ googleMaps: {} }]);
|
|
448
|
+
expect(result.toolConfig).toBeUndefined();
|
|
449
|
+
expect(result.toolWarnings).toEqual([]);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('should add warnings for google maps on unsupported models', () => {
|
|
453
|
+
const result = prepareTools({
|
|
454
|
+
tools: [
|
|
455
|
+
{
|
|
456
|
+
type: 'provider',
|
|
457
|
+
id: 'google.google_maps',
|
|
458
|
+
name: 'google_maps',
|
|
459
|
+
args: {},
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
modelId: 'gemini-1.5-flash',
|
|
463
|
+
});
|
|
464
|
+
expect(result.tools).toBeUndefined();
|
|
465
|
+
expect(result.toolWarnings).toMatchInlineSnapshot(`
|
|
466
|
+
[
|
|
467
|
+
{
|
|
468
|
+
"details": "The Google Maps grounding tool is not supported with Gemini models other than Gemini 2 or newer.",
|
|
469
|
+
"feature": "provider-defined tool google.google_maps",
|
|
470
|
+
"type": "unsupported",
|
|
471
|
+
},
|
|
472
|
+
]
|
|
473
|
+
`);
|
|
474
|
+
});
|