@ai-sdk/google 4.0.107 → 4.0.109
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 +34 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +262 -113
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +9 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +249 -99
- package/dist/internal/index.js.map +1 -1
- package/docs/15-google.mdx +27 -3
- package/package.json +3 -3
- package/src/convert-to-google-messages.ts +16 -5
- package/src/google-batch.ts +3 -4
- package/src/google-embedding-model.ts +2 -1
- package/src/google-files.ts +7 -7
- package/src/google-json-accumulator.ts +26 -14
- package/src/google-language-model.ts +42 -90
- package/src/google-model-capabilities.ts +7 -40
- package/src/google-model-catalog.ts +319 -0
- package/src/google-prompt.ts +15 -0
- package/src/google-provider.ts +15 -11
- package/src/index.ts +1 -0
- package/src/interactions/cancel-google-interaction.ts +7 -2
- package/src/interactions/google-interactions-language-model.ts +8 -12
- package/src/interactions/poll-google-interactions.ts +4 -2
- package/src/interactions/stream-google-interactions.ts +4 -7
- package/src/realtime/google-realtime-model.ts +7 -3
- package/src/speech-translation/google-speech-translation-model.ts +7 -2
package/docs/15-google.mdx
CHANGED
|
@@ -97,7 +97,11 @@ You can use the following optional settings to customize the Google provider ins
|
|
|
97
97
|
Use a different URL prefix for API calls, e.g. to use proxy servers.
|
|
98
98
|
The default prefix is `https://generativelanguage.googleapis.com/v1beta`.
|
|
99
99
|
|
|
100
|
-
- **apiKey**
|
|
100
|
+
- **apiKey** _string | PromiseLike<string> | (() => string | PromiseLike<string>)_
|
|
101
|
+
|
|
102
|
+
Pass a function to resolve credentials before each request, including when keys
|
|
103
|
+
rotate. The function can return a string or a promise. Credential caching and
|
|
104
|
+
refresh are managed by the function.
|
|
101
105
|
|
|
102
106
|
API key that is being sent using the `x-goog-api-key` header.
|
|
103
107
|
It defaults to the `GOOGLE_GENERATIVE_AI_API_KEY` environment variable.
|
|
@@ -540,7 +544,14 @@ const result = await generateText({
|
|
|
540
544
|
});
|
|
541
545
|
```
|
|
542
546
|
|
|
543
|
-
You can also use YouTube URLs directly
|
|
547
|
+
You can also use YouTube URLs directly. Pass `videoMetadata` through
|
|
548
|
+
`providerOptions.google` on a video file part to clip a segment or control the
|
|
549
|
+
frame rate. This also works with inline video data and uploaded file references.
|
|
550
|
+
The metadata supports these optional fields:
|
|
551
|
+
|
|
552
|
+
- `startOffset` (string): Start time as a duration in seconds, e.g. `'60s'` or `'3.5s'`.
|
|
553
|
+
- `endOffset` (string): End time in the same duration format.
|
|
554
|
+
- `fps` (number): Frames per second in the range `(0, 24]`. The default is `1`.
|
|
544
555
|
|
|
545
556
|
```ts
|
|
546
557
|
import { google } from '@ai-sdk/google';
|
|
@@ -554,12 +565,21 @@ const result = await generateText({
|
|
|
554
565
|
content: [
|
|
555
566
|
{
|
|
556
567
|
type: 'text',
|
|
557
|
-
text: 'Summarize this video',
|
|
568
|
+
text: 'Summarize this part of the video',
|
|
558
569
|
},
|
|
559
570
|
{
|
|
560
571
|
type: 'file',
|
|
561
572
|
data: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
|
562
573
|
mediaType: 'video/mp4',
|
|
574
|
+
providerOptions: {
|
|
575
|
+
google: {
|
|
576
|
+
videoMetadata: {
|
|
577
|
+
startOffset: '60s',
|
|
578
|
+
endOffset: '600s',
|
|
579
|
+
fps: 1,
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
},
|
|
563
583
|
},
|
|
564
584
|
],
|
|
565
585
|
},
|
|
@@ -1249,6 +1269,8 @@ will still validate the response against the schema in `Output.object`.
|
|
|
1249
1269
|
|
|
1250
1270
|
### Model Capabilities
|
|
1251
1271
|
|
|
1272
|
+
{/* model-capabilities:google-language:start */}
|
|
1273
|
+
|
|
1252
1274
|
| Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Google Search | URL Context |
|
|
1253
1275
|
| ------------------------------------- | ----------- | ----------------- | ---------- | -------------- | ------------- | ----------- |
|
|
1254
1276
|
| `gemini-nano-banana-2.1` | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Cross /> |
|
|
@@ -1270,6 +1292,8 @@ will still validate the response against the schema in `Output.object`.
|
|
|
1270
1292
|
| `gemini-2.5-flash-lite-preview-06-17` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
1271
1293
|
| `gemini-2.0-flash` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
1272
1294
|
|
|
1295
|
+
{/* model-capabilities:google-language:end */}
|
|
1296
|
+
|
|
1273
1297
|
<Note>
|
|
1274
1298
|
The table above lists popular models. Please see the [Google Generative AI
|
|
1275
1299
|
docs](https://ai.google.dev/gemini-api/docs/models/) for a full list of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/google",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.109",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@ai-sdk/provider": "4.0.
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
38
|
+
"@ai-sdk/provider": "4.0.35",
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.72"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.4",
|
|
@@ -14,11 +14,12 @@ import {
|
|
|
14
14
|
resolveProviderReference,
|
|
15
15
|
secureJsonParse,
|
|
16
16
|
} from '@ai-sdk/provider-utils';
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
import {
|
|
18
|
+
googleVideoMetadataSchema,
|
|
19
|
+
type GoogleContent,
|
|
20
|
+
type GoogleContentPart,
|
|
21
|
+
type GoogleFunctionResponsePart,
|
|
22
|
+
type GooglePrompt,
|
|
22
23
|
} from './google-prompt';
|
|
23
24
|
import {
|
|
24
25
|
codeExecutionInputSchema,
|
|
@@ -330,6 +331,12 @@ export function convertToGoogleMessages(
|
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
case 'file': {
|
|
334
|
+
const rawVideoMetadata = readProviderOpts(part)?.videoMetadata;
|
|
335
|
+
const videoMetadata =
|
|
336
|
+
rawVideoMetadata == null
|
|
337
|
+
? undefined
|
|
338
|
+
: googleVideoMetadataSchema.parse(rawVideoMetadata);
|
|
339
|
+
|
|
333
340
|
switch (part.data.type) {
|
|
334
341
|
case 'url': {
|
|
335
342
|
parts.push({
|
|
@@ -341,6 +348,7 @@ export function convertToGoogleMessages(
|
|
|
341
348
|
? part.data.originalUrl
|
|
342
349
|
: part.data.url.toString(),
|
|
343
350
|
},
|
|
351
|
+
...(videoMetadata != null ? { videoMetadata } : {}),
|
|
344
352
|
});
|
|
345
353
|
break;
|
|
346
354
|
}
|
|
@@ -359,6 +367,7 @@ export function convertToGoogleMessages(
|
|
|
359
367
|
provider: 'google',
|
|
360
368
|
}),
|
|
361
369
|
},
|
|
370
|
+
...(videoMetadata != null ? { videoMetadata } : {}),
|
|
362
371
|
});
|
|
363
372
|
break;
|
|
364
373
|
}
|
|
@@ -372,6 +381,7 @@ export function convertToGoogleMessages(
|
|
|
372
381
|
new TextEncoder().encode(part.data.text),
|
|
373
382
|
),
|
|
374
383
|
},
|
|
384
|
+
...(videoMetadata != null ? { videoMetadata } : {}),
|
|
375
385
|
});
|
|
376
386
|
break;
|
|
377
387
|
}
|
|
@@ -381,6 +391,7 @@ export function convertToGoogleMessages(
|
|
|
381
391
|
mimeType: resolveFullMediaType({ part }),
|
|
382
392
|
data: convertToBase64(part.data.data),
|
|
383
393
|
},
|
|
394
|
+
...(videoMetadata != null ? { videoMetadata } : {}),
|
|
384
395
|
});
|
|
385
396
|
break;
|
|
386
397
|
}
|
package/src/google-batch.ts
CHANGED
|
@@ -292,7 +292,6 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
const headers = await this.getHeaders(options.headers);
|
|
296
295
|
const createUrl = `${this.batchConfig.baseURL}/${getModelPath(
|
|
297
296
|
modelId,
|
|
298
297
|
)}:batchGenerateContent`;
|
|
@@ -300,7 +299,7 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
|
|
|
300
299
|
if (fileParts == null) {
|
|
301
300
|
const { value: operation } = await postJsonToApi({
|
|
302
301
|
url: createUrl,
|
|
303
|
-
headers,
|
|
302
|
+
headers: await this.getHeaders(options.headers),
|
|
304
303
|
body: inlineBatchBody,
|
|
305
304
|
failedResponseHandler: googleFailedResponseHandler,
|
|
306
305
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -329,7 +328,7 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
|
|
|
329
328
|
|
|
330
329
|
const { value: uploadUrl } = await postJsonToApi({
|
|
331
330
|
url: `${this.getBaseOrigin()}/upload/v1beta/files`,
|
|
332
|
-
headers: combineHeaders(headers, {
|
|
331
|
+
headers: combineHeaders(await this.getHeaders(options.headers), {
|
|
333
332
|
'X-Goog-Upload-Protocol': 'resumable',
|
|
334
333
|
'X-Goog-Upload-Command': 'start',
|
|
335
334
|
'X-Goog-Upload-Header-Content-Length': String(inputFile.size),
|
|
@@ -370,7 +369,7 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
|
|
|
370
369
|
|
|
371
370
|
const { value: operation } = await postJsonToApi({
|
|
372
371
|
url: createUrl,
|
|
373
|
-
headers,
|
|
372
|
+
headers: await this.getHeaders(options.headers),
|
|
374
373
|
body: {
|
|
375
374
|
batch: {
|
|
376
375
|
displayName,
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type EmbeddingModelV4,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
5
5
|
import {
|
|
6
|
+
type Resolvable,
|
|
6
7
|
combineHeaders,
|
|
7
8
|
createJsonResponseHandler,
|
|
8
9
|
type EmbeddingModelProviderOptionsTransformer,
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
type GoogleEmbeddingConfig = {
|
|
27
28
|
provider: string;
|
|
28
29
|
baseURL: string;
|
|
29
|
-
headers?:
|
|
30
|
+
headers?: Resolvable<Record<string, string | undefined>>;
|
|
30
31
|
fetch?: FetchFunction;
|
|
31
32
|
};
|
|
32
33
|
|
package/src/google-files.ts
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
type SharedV4Warning,
|
|
7
7
|
} from '@ai-sdk/provider';
|
|
8
8
|
import {
|
|
9
|
+
resolve,
|
|
10
|
+
type Resolvable,
|
|
9
11
|
combineHeaders,
|
|
10
12
|
convertInlineFileDataToUint8Array,
|
|
11
13
|
createJsonResponseHandler,
|
|
@@ -31,7 +33,7 @@ export type GoogleFilesUploadOptions = {
|
|
|
31
33
|
interface GoogleFilesConfig {
|
|
32
34
|
provider: string;
|
|
33
35
|
baseURL: string;
|
|
34
|
-
headers:
|
|
36
|
+
headers: Resolvable<Record<string, string | undefined>>;
|
|
35
37
|
fetch?: FetchFunction;
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -53,10 +55,8 @@ export class GoogleFiles implements FilesV4 {
|
|
|
53
55
|
schema: googleFilesUploadOptionsSchema,
|
|
54
56
|
})) as GoogleFilesUploadOptions | undefined;
|
|
55
57
|
|
|
56
|
-
const
|
|
57
|
-
this.config.headers
|
|
58
|
-
options.headers,
|
|
59
|
-
);
|
|
58
|
+
const getHeaders = async () =>
|
|
59
|
+
combineHeaders(await resolve(this.config.headers), options.headers);
|
|
60
60
|
const fetchFn = this.config.fetch ?? globalThis.fetch;
|
|
61
61
|
|
|
62
62
|
const warnings: Array<SharedV4Warning> = [];
|
|
@@ -74,7 +74,7 @@ export class GoogleFiles implements FilesV4 {
|
|
|
74
74
|
const initResponse = await fetchFn(`${baseOrigin}/upload/v1beta/files`, {
|
|
75
75
|
method: 'POST',
|
|
76
76
|
headers: {
|
|
77
|
-
...
|
|
77
|
+
...(await getHeaders()),
|
|
78
78
|
'X-Goog-Upload-Protocol': 'resumable',
|
|
79
79
|
'X-Goog-Upload-Command': 'start',
|
|
80
80
|
'X-Goog-Upload-Header-Content-Length': String(fileBytes.length),
|
|
@@ -152,7 +152,7 @@ export class GoogleFiles implements FilesV4 {
|
|
|
152
152
|
const { value: fileStatus } = await getFromApi({
|
|
153
153
|
url: `${this.config.baseURL}/${filePath}`,
|
|
154
154
|
validateUrl: false,
|
|
155
|
-
headers:
|
|
155
|
+
headers: await getHeaders(),
|
|
156
156
|
successfulResponseHandler: createJsonResponseHandler(
|
|
157
157
|
googleFileResponseSchema,
|
|
158
158
|
),
|
|
@@ -74,6 +74,9 @@ export class GoogleJSONAccumulator {
|
|
|
74
74
|
(existingValue as string) + arg.stringValue,
|
|
75
75
|
);
|
|
76
76
|
delta += escaped;
|
|
77
|
+
if (arg.willContinue !== true) {
|
|
78
|
+
delta += this.closeOpenString();
|
|
79
|
+
}
|
|
77
80
|
continue;
|
|
78
81
|
}
|
|
79
82
|
|
|
@@ -81,8 +84,15 @@ export class GoogleJSONAccumulator {
|
|
|
81
84
|
if (resolved == null) {
|
|
82
85
|
// A value-less entry for a path without a value is an empty container
|
|
83
86
|
// (an empty container has no leaves to stream). Value-less entries for
|
|
84
|
-
// existing paths are string or array terminators
|
|
85
|
-
|
|
87
|
+
// existing paths are string or array terminators. A string terminator
|
|
88
|
+
// closes the open string, an array terminator is ignored.
|
|
89
|
+
if (existingValue !== undefined) {
|
|
90
|
+
if (typeof existingValue === 'string' && arg.willContinue !== true) {
|
|
91
|
+
delta += this.closeOpenString();
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (arg.willContinue === true) continue;
|
|
86
96
|
|
|
87
97
|
setNestedValue(
|
|
88
98
|
this.accumulatedArgs,
|
|
@@ -115,6 +125,18 @@ export class GoogleJSONAccumulator {
|
|
|
115
125
|
return { finalJSON: finalArgs, closingDelta };
|
|
116
126
|
}
|
|
117
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Input: stringOpen=true
|
|
130
|
+
* Output: '"' (and stringOpen=false), or '' when no string is open
|
|
131
|
+
*/
|
|
132
|
+
private closeOpenString(): string {
|
|
133
|
+
if (!this.stringOpen) {
|
|
134
|
+
return '';
|
|
135
|
+
}
|
|
136
|
+
this.stringOpen = false;
|
|
137
|
+
return '"';
|
|
138
|
+
}
|
|
139
|
+
|
|
118
140
|
/**
|
|
119
141
|
* Input: pathStack=[] (first call) or pathStack=[root,...] (subsequent calls)
|
|
120
142
|
* Output: '{' (first call) or '' (subsequent calls)
|
|
@@ -139,12 +161,7 @@ export class GoogleJSONAccumulator {
|
|
|
139
161
|
arg: PartialArg,
|
|
140
162
|
valueJson: string,
|
|
141
163
|
): string {
|
|
142
|
-
let fragment =
|
|
143
|
-
|
|
144
|
-
if (this.stringOpen) {
|
|
145
|
-
fragment += '"';
|
|
146
|
-
this.stringOpen = false;
|
|
147
|
-
}
|
|
164
|
+
let fragment = this.closeOpenString();
|
|
148
165
|
|
|
149
166
|
fragment += this.ensureRoot();
|
|
150
167
|
|
|
@@ -172,12 +189,7 @@ export class GoogleJSONAccumulator {
|
|
|
172
189
|
targetSegments: PathSegment[],
|
|
173
190
|
isArray: boolean,
|
|
174
191
|
): string {
|
|
175
|
-
let fragment =
|
|
176
|
-
|
|
177
|
-
if (this.stringOpen) {
|
|
178
|
-
fragment += '"';
|
|
179
|
-
this.stringOpen = false;
|
|
180
|
-
}
|
|
192
|
+
let fragment = this.closeOpenString();
|
|
181
193
|
|
|
182
194
|
fragment += this.ensureRoot();
|
|
183
195
|
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
type GoogleLanguageModelOptions,
|
|
59
59
|
type GoogleModelId,
|
|
60
60
|
} from './google-language-model-options';
|
|
61
|
-
import {
|
|
61
|
+
import { getGoogleLanguageModelEntry } from './google-model-catalog';
|
|
62
62
|
import type { GoogleProviderMetadata } from './google-prompt';
|
|
63
63
|
import { prepareTools } from './google-prepare-tools';
|
|
64
64
|
import {
|
|
@@ -74,8 +74,6 @@ const configurableSafetySettingCategories = [
|
|
|
74
74
|
'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
75
75
|
] as const;
|
|
76
76
|
|
|
77
|
-
const gemini25ModelPattern = /(^|[/.])gemini-2\.5(?:[.-]|$)/i;
|
|
78
|
-
|
|
79
77
|
const googleCloudStorageFunctionResponseUrls = {
|
|
80
78
|
'image/png': [/^gs:\/\/.*$/],
|
|
81
79
|
'image/jpeg': [/^gs:\/\/.*$/],
|
|
@@ -296,9 +294,12 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
296
294
|
}
|
|
297
295
|
}
|
|
298
296
|
|
|
299
|
-
const
|
|
297
|
+
const entry = getGoogleLanguageModelEntry(modelId);
|
|
298
|
+
const isGemmaModel =
|
|
299
|
+
entry.requestConfig.language.systemInstruction === 'user';
|
|
300
300
|
const isGemini25DeveloperApiModel =
|
|
301
|
-
!isVertexProvider &&
|
|
301
|
+
!isVertexProvider &&
|
|
302
|
+
entry.capabilities.language.sampling.penalties !== true;
|
|
302
303
|
|
|
303
304
|
if (isGemini25DeveloperApiModel && frequencyPenalty != null) {
|
|
304
305
|
warnings.push({
|
|
@@ -313,7 +314,8 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
313
314
|
});
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
const
|
|
317
|
+
const usesGemini3Features =
|
|
318
|
+
entry.capabilities.language.tools.functionResponseParts;
|
|
317
319
|
const supportedFunctionResponseUrls =
|
|
318
320
|
usesGemini3Features &&
|
|
319
321
|
config.downloadToolResultFiles?.supportsGoogleCloudStorageUrls
|
|
@@ -466,6 +468,8 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
466
468
|
{ isStreaming = false }: { isStreaming?: boolean } = {},
|
|
467
469
|
) {
|
|
468
470
|
const { responseFormat, tools, toolChoice } = options;
|
|
471
|
+
const entry = getGoogleLanguageModelEntry(this.modelId);
|
|
472
|
+
const { structuredOutput } = entry.capabilities.language;
|
|
469
473
|
let jsonResponseTool: LanguageModelV4FunctionTool | undefined;
|
|
470
474
|
// Gemini models before Gemini 3 reject JSON response formatting together
|
|
471
475
|
// with function tools. Gemini 3 supports the combination only with automatic
|
|
@@ -474,8 +478,8 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
474
478
|
responseFormat?.type === 'json' &&
|
|
475
479
|
responseFormat.schema != null &&
|
|
476
480
|
tools?.some(tool => tool.type === 'function') &&
|
|
477
|
-
|
|
478
|
-
(!
|
|
481
|
+
entry.requestConfig.language.jsonResponseTool &&
|
|
482
|
+
(!structuredOutput.withFunctionTools ||
|
|
479
483
|
toolChoice?.type === 'required' ||
|
|
480
484
|
toolChoice?.type === 'tool')
|
|
481
485
|
) {
|
|
@@ -754,18 +758,16 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
754
758
|
jsonResponseToolName,
|
|
755
759
|
} = await this.getArgs(options);
|
|
756
760
|
|
|
757
|
-
const
|
|
758
|
-
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
759
|
-
options.headers,
|
|
760
|
-
extraHeaders,
|
|
761
|
-
);
|
|
762
|
-
|
|
763
|
-
const generateContent = (body: typeof args) =>
|
|
761
|
+
const generateContent = async (body: typeof args) =>
|
|
764
762
|
postJsonToApi({
|
|
765
763
|
url: `${this.config.baseURL}/${getModelPath(
|
|
766
764
|
this.modelId,
|
|
767
765
|
)}:generateContent`,
|
|
768
|
-
headers:
|
|
766
|
+
headers: combineHeaders(
|
|
767
|
+
await resolve(this.config.headers),
|
|
768
|
+
options.headers,
|
|
769
|
+
extraHeaders,
|
|
770
|
+
),
|
|
769
771
|
body,
|
|
770
772
|
failedResponseHandler: googleFailedResponseHandler,
|
|
771
773
|
successfulResponseHandler: createJsonResponseHandler(responseSchema),
|
|
@@ -875,18 +877,16 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
875
877
|
providerOptionsNames.map(name => [name, payload]),
|
|
876
878
|
) as SharedV4ProviderMetadata;
|
|
877
879
|
|
|
878
|
-
const
|
|
879
|
-
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
880
|
-
options.headers,
|
|
881
|
-
extraHeaders,
|
|
882
|
-
);
|
|
883
|
-
|
|
884
|
-
const streamGenerateContent = (body: typeof args) =>
|
|
880
|
+
const streamGenerateContent = async (body: typeof args) =>
|
|
885
881
|
postJsonToApi({
|
|
886
882
|
url: `${this.config.baseURL}/${getModelPath(
|
|
887
883
|
this.modelId,
|
|
888
884
|
)}:streamGenerateContent?alt=sse`,
|
|
889
|
-
headers
|
|
885
|
+
headers: combineHeaders(
|
|
886
|
+
await resolve(this.config.headers),
|
|
887
|
+
options.headers,
|
|
888
|
+
extraHeaders,
|
|
889
|
+
),
|
|
890
890
|
body,
|
|
891
891
|
failedResponseHandler: googleFailedResponseHandler,
|
|
892
892
|
successfulResponseHandler:
|
|
@@ -1571,18 +1571,6 @@ export class GoogleLanguageModel implements LanguageModelV4 {
|
|
|
1571
1571
|
}
|
|
1572
1572
|
}
|
|
1573
1573
|
|
|
1574
|
-
function getMaxOutputTokensForGemini25Model(): number {
|
|
1575
|
-
return 65536;
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
function getMaxThinkingTokensForGemini25Model(modelId: string): number {
|
|
1579
|
-
const id = modelId.toLowerCase();
|
|
1580
|
-
if (id.includes('2.5-pro') || id.includes('gemini-3-pro-image')) {
|
|
1581
|
-
return 32768;
|
|
1582
|
-
}
|
|
1583
|
-
return 24576;
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
1574
|
type GoogleThinkingConfig = NonNullable<
|
|
1587
1575
|
InferSchema<typeof googleLanguageModelOptions>['thinkingConfig']
|
|
1588
1576
|
>;
|
|
@@ -1601,8 +1589,8 @@ function resolveThinkingConfig({
|
|
|
1601
1589
|
}
|
|
1602
1590
|
|
|
1603
1591
|
if (
|
|
1604
|
-
|
|
1605
|
-
|
|
1592
|
+
getGoogleLanguageModelEntry(modelId).requestConfig.language
|
|
1593
|
+
.thinkingFormat === 'level'
|
|
1606
1594
|
) {
|
|
1607
1595
|
return resolveGemini3ThinkingConfig({ reasoning, modelId, warnings });
|
|
1608
1596
|
}
|
|
@@ -1622,22 +1610,21 @@ function resolveGemini3ThinkingConfig({
|
|
|
1622
1610
|
modelId: string;
|
|
1623
1611
|
warnings: SharedV4Warning[];
|
|
1624
1612
|
}): Pick<GoogleThinkingConfig, 'thinkingLevel'> | undefined {
|
|
1625
|
-
const minimumThinkingLevel =
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
// It's not possible to fully disable thinking with Gemini 3.
|
|
1629
|
-
return { thinkingLevel: minimumThinkingLevel };
|
|
1630
|
-
}
|
|
1613
|
+
const minimumThinkingLevel =
|
|
1614
|
+
getGoogleLanguageModelEntry(modelId).capabilities.language.reasoning
|
|
1615
|
+
.efforts?.[0];
|
|
1631
1616
|
|
|
1617
|
+
// It's not possible to fully disable thinking with Gemini 3, so reasoning
|
|
1618
|
+
// 'none' maps to the lowest supported thinking level.
|
|
1632
1619
|
const thinkingLevel = mapReasoningToProviderEffort({
|
|
1633
1620
|
reasoning,
|
|
1634
1621
|
effortMap: {
|
|
1635
|
-
minimal
|
|
1622
|
+
...(minimumThinkingLevel === 'minimal' && {
|
|
1623
|
+
minimal: 'minimal' as const,
|
|
1624
|
+
}),
|
|
1636
1625
|
low: 'low',
|
|
1637
1626
|
medium: 'medium',
|
|
1638
1627
|
high: 'high',
|
|
1639
|
-
xhigh: 'high',
|
|
1640
|
-
max: 'high',
|
|
1641
1628
|
},
|
|
1642
1629
|
warnings,
|
|
1643
1630
|
});
|
|
@@ -1649,41 +1636,6 @@ function resolveGemini3ThinkingConfig({
|
|
|
1649
1636
|
return { thinkingLevel };
|
|
1650
1637
|
}
|
|
1651
1638
|
|
|
1652
|
-
function getMinimumThinkingLevelForGemini3Model(
|
|
1653
|
-
modelId: string,
|
|
1654
|
-
): 'minimal' | 'low' {
|
|
1655
|
-
const modelName = /(?:^|[/.])(gemini-[^/]*)$/i
|
|
1656
|
-
.exec(modelId)?.[1]
|
|
1657
|
-
.toLowerCase();
|
|
1658
|
-
|
|
1659
|
-
if (modelName === 'gemini-flash-latest') {
|
|
1660
|
-
return 'low';
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
const proVersionMatch = /^gemini-(\d+)(?:\.\d+)?-pro(?:$|-)/.exec(
|
|
1664
|
-
modelName ?? '',
|
|
1665
|
-
);
|
|
1666
|
-
|
|
1667
|
-
if (Number(proVersionMatch?.[1]) >= 3) {
|
|
1668
|
-
return 'low';
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
const versionMatch = /^gemini-(\d+)\.(\d+)-flash(?:$|-(?!lite(?:-|$)))/.exec(
|
|
1672
|
-
modelName ?? '',
|
|
1673
|
-
);
|
|
1674
|
-
|
|
1675
|
-
if (versionMatch == null) {
|
|
1676
|
-
return 'minimal';
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
const majorVersion = Number(versionMatch[1]);
|
|
1680
|
-
const minorVersion = Number(versionMatch[2]);
|
|
1681
|
-
|
|
1682
|
-
return majorVersion > 3 || (majorVersion === 3 && minorVersion >= 7)
|
|
1683
|
-
? 'low'
|
|
1684
|
-
: 'minimal';
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
1639
|
function resolveGemini25ThinkingConfig({
|
|
1688
1640
|
reasoning,
|
|
1689
1641
|
modelId,
|
|
@@ -1695,21 +1647,21 @@ function resolveGemini25ThinkingConfig({
|
|
|
1695
1647
|
>;
|
|
1696
1648
|
modelId: string;
|
|
1697
1649
|
warnings: SharedV4Warning[];
|
|
1698
|
-
}): Pick<GoogleThinkingConfig, 'thinkingBudget'>
|
|
1699
|
-
|
|
1650
|
+
}): Pick<GoogleThinkingConfig, 'thinkingBudget'> {
|
|
1651
|
+
const entry = getGoogleLanguageModelEntry(modelId);
|
|
1652
|
+
const minThinkingBudget = entry.capabilities.language.reasoning.minBudget;
|
|
1653
|
+
|
|
1654
|
+
if (reasoning === 'none' && minThinkingBudget === 0) {
|
|
1700
1655
|
return { thinkingBudget: 0 };
|
|
1701
1656
|
}
|
|
1702
1657
|
|
|
1703
1658
|
const thinkingBudget = mapReasoningToProviderBudget({
|
|
1704
1659
|
reasoning,
|
|
1705
|
-
maxOutputTokens:
|
|
1706
|
-
maxReasoningBudget:
|
|
1707
|
-
minReasoningBudget:
|
|
1660
|
+
maxOutputTokens: entry.requestConfig.language.reasoningMaxOutputTokens,
|
|
1661
|
+
maxReasoningBudget: entry.capabilities.language.reasoning.maxBudget,
|
|
1662
|
+
minReasoningBudget: minThinkingBudget,
|
|
1708
1663
|
warnings,
|
|
1709
1664
|
});
|
|
1710
|
-
if (thinkingBudget == null) {
|
|
1711
|
-
return undefined;
|
|
1712
|
-
}
|
|
1713
1665
|
return { thinkingBudget };
|
|
1714
1666
|
}
|
|
1715
1667
|
|
|
@@ -1,44 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
supportsGemini2Tools: boolean;
|
|
3
|
-
supportsFileSearch: boolean;
|
|
4
|
-
usesGemini3Features: boolean;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const gemini1ModelPattern = /(^|[/.])gemini-1(?:[.-]|$)/i;
|
|
8
|
-
const gemini2ModelPattern = /(^|[/.])gemini-2(?:[.-]|$)/i;
|
|
9
|
-
const gemini25ModelPattern = /(^|[/.])gemini-2\.5(?:[.-]|$)/i;
|
|
10
|
-
const geminiModelPattern = /(^|[/.])gemini-/i;
|
|
11
|
-
|
|
12
|
-
function isKnownPreGemini2Model(modelId: string): boolean {
|
|
13
|
-
return (
|
|
14
|
-
gemini1ModelPattern.test(modelId) ||
|
|
15
|
-
/(^|[/.])gemini-pro(?:-vision)?$/i.test(modelId) ||
|
|
16
|
-
/(^|[/.])gemini-robotics-er-1\.5(?:[.-]|$)/i.test(modelId)
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Classifies Gemini capabilities by excluding known older generations.
|
|
22
|
-
*
|
|
23
|
-
* Google model IDs are open-ended, so unrecognized Gemini IDs and aliases
|
|
24
|
-
* intentionally inherit the newest supported behavior. Add exceptions here
|
|
25
|
-
* only when a model is known to require a legacy request shape.
|
|
26
|
-
*/
|
|
27
|
-
export function getGoogleModelCapabilities(
|
|
28
|
-
modelId: string,
|
|
29
|
-
): GoogleModelCapabilities {
|
|
30
|
-
const isGeminiModel = geminiModelPattern.test(modelId);
|
|
31
|
-
const isGemini2Model = gemini2ModelPattern.test(modelId);
|
|
32
|
-
const isKnownPreGemini2 = isKnownPreGemini2Model(modelId);
|
|
33
|
-
const isKnownOlderModel = isKnownPreGemini2 || isGemini2Model;
|
|
34
|
-
const usesGemini3Features = isGeminiModel && !isKnownOlderModel;
|
|
1
|
+
import { getGoogleLanguageModelEntry } from './google-model-catalog';
|
|
35
2
|
|
|
3
|
+
/** Compatibility projection of the resolved catalog for existing request code. */
|
|
4
|
+
export function getGoogleModelCapabilities(modelId: string) {
|
|
5
|
+
const { tools } = getGoogleLanguageModelEntry(modelId).capabilities.language;
|
|
36
6
|
return {
|
|
37
|
-
supportsGemini2Tools:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
supportsFileSearch:
|
|
41
|
-
gemini25ModelPattern.test(modelId) || usesGemini3Features,
|
|
42
|
-
usesGemini3Features,
|
|
7
|
+
supportsGemini2Tools: tools.gemini2,
|
|
8
|
+
supportsFileSearch: tools.fileSearch,
|
|
9
|
+
usesGemini3Features: tools.mixed,
|
|
43
10
|
};
|
|
44
11
|
}
|