@ai-sdk/google 4.0.0-beta.7 → 4.0.0-beta.82
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 +614 -5
- package/README.md +6 -4
- package/dist/index.d.ts +301 -50
- package/dist/index.js +5410 -639
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +100 -26
- package/dist/internal/index.js +1653 -451
- package/dist/internal/index.js.map +1 -1
- package/docs/{15-google-generative-ai.mdx → 15-google.mdx} +784 -69
- package/package.json +16 -17
- package/src/{convert-google-generative-ai-usage.ts → convert-google-usage.ts} +13 -5
- package/src/convert-json-schema-to-openapi-schema.ts +1 -1
- package/src/convert-to-google-messages.ts +647 -0
- package/src/{google-generative-ai-embedding-options.ts → google-embedding-model-options.ts} +9 -2
- package/src/{google-generative-ai-embedding-model.ts → google-embedding-model.ts} +31 -18
- package/src/google-error.ts +1 -1
- package/src/google-files.ts +225 -0
- package/src/google-image-model-options.ts +35 -0
- package/src/{google-generative-ai-image-model.ts → google-image-model.ts} +116 -65
- package/src/{google-generative-ai-image-settings.ts → google-image-settings.ts} +2 -2
- package/src/google-json-accumulator.ts +371 -0
- package/src/{google-generative-ai-options.ts → google-language-model-options.ts} +50 -5
- package/src/{google-generative-ai-language-model.ts → google-language-model.ts} +701 -219
- package/src/google-prepare-tools.ts +72 -12
- package/src/google-prompt.ts +86 -0
- package/src/google-provider.ts +157 -53
- package/src/google-speech-api.ts +36 -0
- package/src/google-speech-model-options.ts +48 -0
- package/src/google-speech-model.ts +311 -0
- package/src/google-video-model-options.ts +43 -0
- package/src/{google-generative-ai-video-model.ts → google-video-model.ts} +25 -60
- package/src/{google-generative-ai-video-settings.ts → google-video-settings.ts} +2 -1
- package/src/index.ts +40 -9
- package/src/interactions/build-google-interactions-stream-transform.ts +818 -0
- package/src/interactions/cancel-google-interaction.ts +60 -0
- package/src/interactions/convert-google-interactions-usage.ts +47 -0
- package/src/interactions/convert-to-google-interactions-input.ts +557 -0
- package/src/interactions/extract-google-interactions-sources.ts +252 -0
- package/src/interactions/google-interactions-agent.ts +15 -0
- package/src/interactions/google-interactions-api.ts +530 -0
- package/src/interactions/google-interactions-language-model-options.ts +262 -0
- package/src/interactions/google-interactions-language-model.ts +776 -0
- package/src/interactions/google-interactions-prompt.ts +582 -0
- package/src/interactions/google-interactions-provider-metadata.ts +23 -0
- package/src/interactions/map-google-interactions-finish-reason.ts +31 -0
- package/src/interactions/parse-google-interactions-outputs.ts +252 -0
- package/src/interactions/poll-google-interactions.ts +129 -0
- package/src/interactions/prepare-google-interactions-tools.ts +245 -0
- package/src/interactions/stream-google-interactions.ts +242 -0
- package/src/interactions/synthesize-google-interactions-agent-stream.ts +185 -0
- package/src/internal/index.ts +3 -2
- package/src/{map-google-generative-ai-finish-reason.ts → map-google-finish-reason.ts} +3 -3
- package/src/realtime/google-realtime-event-mapper.ts +383 -0
- package/src/realtime/google-realtime-model-options.ts +3 -0
- package/src/realtime/google-realtime-model.ts +160 -0
- package/src/realtime/index.ts +2 -0
- package/src/tool/code-execution.ts +2 -2
- package/src/tool/enterprise-web-search.ts +9 -3
- package/src/tool/file-search.ts +5 -7
- package/src/tool/google-maps.ts +3 -2
- package/src/tool/google-search.ts +11 -12
- package/src/tool/url-context.ts +4 -2
- package/src/tool/vertex-rag-store.ts +9 -6
- package/dist/index.d.mts +0 -376
- package/dist/index.mjs +0 -2517
- package/dist/index.mjs.map +0 -1
- package/dist/internal/index.d.mts +0 -284
- package/dist/internal/index.mjs +0 -1706
- package/dist/internal/index.mjs.map +0 -1
- package/src/convert-to-google-generative-ai-messages.ts +0 -239
- package/src/google-generative-ai-prompt.ts +0 -38
|
@@ -1,72 +1,95 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4,
|
|
3
|
+
LanguageModelV4CallOptions,
|
|
4
|
+
LanguageModelV4Content,
|
|
5
|
+
LanguageModelV4FinishReason,
|
|
6
|
+
LanguageModelV4GenerateResult,
|
|
7
|
+
LanguageModelV4Source,
|
|
8
|
+
LanguageModelV4StreamPart,
|
|
9
|
+
LanguageModelV4StreamResult,
|
|
10
|
+
JSONObject,
|
|
11
|
+
SharedV4ProviderMetadata,
|
|
12
|
+
SharedV4Warning,
|
|
12
13
|
} from '@ai-sdk/provider';
|
|
13
14
|
import {
|
|
14
15
|
combineHeaders,
|
|
15
16
|
createEventSourceResponseHandler,
|
|
16
17
|
createJsonResponseHandler,
|
|
17
|
-
FetchFunction,
|
|
18
18
|
generateId,
|
|
19
|
-
|
|
19
|
+
isCustomReasoning,
|
|
20
20
|
lazySchema,
|
|
21
|
+
mapReasoningToProviderBudget,
|
|
22
|
+
mapReasoningToProviderEffort,
|
|
21
23
|
parseProviderOptions,
|
|
22
|
-
ParseResult,
|
|
23
24
|
postJsonToApi,
|
|
24
|
-
Resolvable,
|
|
25
25
|
resolve,
|
|
26
|
+
serializeModelOptions,
|
|
27
|
+
WORKFLOW_SERIALIZE,
|
|
28
|
+
WORKFLOW_DESERIALIZE,
|
|
26
29
|
zodSchema,
|
|
30
|
+
type FetchFunction,
|
|
31
|
+
type InferSchema,
|
|
32
|
+
type ParseResult,
|
|
33
|
+
type Resolvable,
|
|
27
34
|
} from '@ai-sdk/provider-utils';
|
|
28
35
|
import { z } from 'zod/v4';
|
|
29
36
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from './convert-google-
|
|
37
|
+
convertGoogleUsage,
|
|
38
|
+
type GoogleUsageMetadata,
|
|
39
|
+
} from './convert-google-usage';
|
|
33
40
|
import { convertJSONSchemaToOpenAPISchema } from './convert-json-schema-to-openapi-schema';
|
|
34
|
-
import {
|
|
41
|
+
import { convertToGoogleMessages } from './convert-to-google-messages';
|
|
35
42
|
import { getModelPath } from './get-model-path';
|
|
36
43
|
import { googleFailedResponseHandler } from './google-error';
|
|
37
44
|
import {
|
|
38
|
-
GoogleGenerativeAIModelId,
|
|
39
45
|
googleLanguageModelOptions,
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
type GoogleLanguageModelOptions,
|
|
47
|
+
type GoogleModelId,
|
|
48
|
+
} from './google-language-model-options';
|
|
49
|
+
import type { GoogleProviderMetadata } from './google-prompt';
|
|
42
50
|
import { prepareTools } from './google-prepare-tools';
|
|
43
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
GoogleJSONAccumulator,
|
|
53
|
+
type PartialArg,
|
|
54
|
+
} from './google-json-accumulator';
|
|
55
|
+
import { mapGoogleFinishReason } from './map-google-finish-reason';
|
|
44
56
|
|
|
45
|
-
type
|
|
57
|
+
type GoogleConfig = {
|
|
46
58
|
provider: string;
|
|
47
59
|
baseURL: string;
|
|
48
|
-
headers
|
|
60
|
+
headers?: Resolvable<Record<string, string | undefined>>;
|
|
49
61
|
fetch?: FetchFunction;
|
|
50
62
|
generateId: () => string;
|
|
51
63
|
|
|
52
64
|
/**
|
|
53
65
|
* The supported URLs for the model.
|
|
54
66
|
*/
|
|
55
|
-
supportedUrls?: () =>
|
|
67
|
+
supportedUrls?: () => LanguageModelV4['supportedUrls'];
|
|
56
68
|
};
|
|
57
69
|
|
|
58
|
-
export class
|
|
59
|
-
readonly specificationVersion = '
|
|
70
|
+
export class GoogleLanguageModel implements LanguageModelV4 {
|
|
71
|
+
readonly specificationVersion = 'v4';
|
|
60
72
|
|
|
61
|
-
readonly modelId:
|
|
73
|
+
readonly modelId: GoogleModelId;
|
|
62
74
|
|
|
63
|
-
private readonly config:
|
|
75
|
+
private readonly config: GoogleConfig;
|
|
64
76
|
private readonly generateId: () => string;
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
static [WORKFLOW_SERIALIZE](model: GoogleLanguageModel) {
|
|
79
|
+
return serializeModelOptions({
|
|
80
|
+
modelId: model.modelId,
|
|
81
|
+
config: model.config,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
86
|
+
modelId: string;
|
|
87
|
+
config: GoogleConfig;
|
|
88
|
+
}) {
|
|
89
|
+
return new GoogleLanguageModel(options.modelId, options.config);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
constructor(modelId: GoogleModelId, config: GoogleConfig) {
|
|
70
93
|
this.modelId = modelId;
|
|
71
94
|
this.config = config;
|
|
72
95
|
this.generateId = config.generateId ?? generateId;
|
|
@@ -80,33 +103,49 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
80
103
|
return this.config.supportedUrls?.() ?? {};
|
|
81
104
|
}
|
|
82
105
|
|
|
83
|
-
private async getArgs(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const warnings: SharedV3Warning[] = [];
|
|
99
|
-
|
|
100
|
-
const providerOptionsName = this.config.provider.includes('vertex')
|
|
101
|
-
? 'vertex'
|
|
102
|
-
: 'google';
|
|
103
|
-
let googleOptions = await parseProviderOptions({
|
|
104
|
-
provider: providerOptionsName,
|
|
106
|
+
private async getArgs(
|
|
107
|
+
{
|
|
108
|
+
prompt,
|
|
109
|
+
maxOutputTokens,
|
|
110
|
+
temperature,
|
|
111
|
+
topP,
|
|
112
|
+
topK,
|
|
113
|
+
frequencyPenalty,
|
|
114
|
+
presencePenalty,
|
|
115
|
+
stopSequences,
|
|
116
|
+
responseFormat,
|
|
117
|
+
seed,
|
|
118
|
+
tools,
|
|
119
|
+
toolChoice,
|
|
120
|
+
reasoning,
|
|
105
121
|
providerOptions,
|
|
106
|
-
|
|
107
|
-
}
|
|
122
|
+
}: LanguageModelV4CallOptions,
|
|
123
|
+
{ isStreaming = false }: { isStreaming?: boolean } = {},
|
|
124
|
+
) {
|
|
125
|
+
const warnings: SharedV4Warning[] = [];
|
|
126
|
+
|
|
127
|
+
// Names to look up in providerOptions and to write into providerMetadata.
|
|
128
|
+
// For the Vertex provider we read both the new `googleVertex` key and the
|
|
129
|
+
// legacy `vertex` key (new takes precedence) and write under both for
|
|
130
|
+
// backward compatibility. For other Google providers we use just `google`.
|
|
131
|
+
const providerOptionsNames: readonly string[] =
|
|
132
|
+
this.config.provider.includes('vertex')
|
|
133
|
+
? (['googleVertex', 'vertex'] as const)
|
|
134
|
+
: (['google'] as const);
|
|
135
|
+
|
|
136
|
+
let googleOptions: GoogleLanguageModelOptions | undefined;
|
|
137
|
+
for (const name of providerOptionsNames) {
|
|
138
|
+
googleOptions = await parseProviderOptions({
|
|
139
|
+
provider: name,
|
|
140
|
+
providerOptions,
|
|
141
|
+
schema: googleLanguageModelOptions,
|
|
142
|
+
});
|
|
143
|
+
if (googleOptions != null) break;
|
|
144
|
+
}
|
|
108
145
|
|
|
109
|
-
|
|
146
|
+
// Cross-namespace fallback: a Vertex provider may receive options under
|
|
147
|
+
// the `google` key (e.g. via the AI Gateway).
|
|
148
|
+
if (googleOptions == null && !providerOptionsNames.includes('google')) {
|
|
110
149
|
googleOptions = await parseProviderOptions({
|
|
111
150
|
provider: 'google',
|
|
112
151
|
providerOptions,
|
|
@@ -115,12 +154,14 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
115
154
|
}
|
|
116
155
|
|
|
117
156
|
// Add warning if Vertex rag tools are used with a non-Vertex Google provider
|
|
157
|
+
const isVertexProvider = this.config.provider.startsWith('google.vertex.');
|
|
158
|
+
|
|
118
159
|
if (
|
|
119
160
|
tools?.some(
|
|
120
161
|
tool =>
|
|
121
162
|
tool.type === 'provider' && tool.id === 'google.vertex_rag_store',
|
|
122
163
|
) &&
|
|
123
|
-
!
|
|
164
|
+
!isVertexProvider
|
|
124
165
|
) {
|
|
125
166
|
warnings.push({
|
|
126
167
|
type: 'other',
|
|
@@ -131,12 +172,65 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
131
172
|
});
|
|
132
173
|
}
|
|
133
174
|
|
|
134
|
-
|
|
175
|
+
if (googleOptions?.streamFunctionCallArguments && !isVertexProvider) {
|
|
176
|
+
warnings.push({
|
|
177
|
+
type: 'other',
|
|
178
|
+
message:
|
|
179
|
+
"'streamFunctionCallArguments' is only supported on the Vertex AI API " +
|
|
180
|
+
'and will be ignored with the current Google provider ' +
|
|
181
|
+
`(${this.config.provider}). See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#streaming-fc`,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
135
184
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
185
|
+
if (googleOptions?.serviceTier && isVertexProvider) {
|
|
186
|
+
warnings.push({
|
|
187
|
+
type: 'other',
|
|
188
|
+
message:
|
|
189
|
+
"'serviceTier' is a Gemini API option and is not supported on Vertex AI. " +
|
|
190
|
+
"Use 'sharedRequestType' (and optionally 'requestType') instead. See " +
|
|
191
|
+
'https://docs.cloud.google.com/vertex-ai/generative-ai/docs/priority-paygo',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (
|
|
195
|
+
(googleOptions?.sharedRequestType || googleOptions?.requestType) &&
|
|
196
|
+
!isVertexProvider
|
|
197
|
+
) {
|
|
198
|
+
warnings.push({
|
|
199
|
+
type: 'other',
|
|
200
|
+
message:
|
|
201
|
+
"'sharedRequestType' and 'requestType' are Vertex AI options and " +
|
|
202
|
+
`are ignored with the current Google provider (${this.config.provider}).`,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const vertexPaygoHeaders: Record<string, string> | undefined =
|
|
207
|
+
isVertexProvider &&
|
|
208
|
+
(googleOptions?.sharedRequestType || googleOptions?.requestType)
|
|
209
|
+
? {
|
|
210
|
+
...(googleOptions.sharedRequestType && {
|
|
211
|
+
'X-Vertex-AI-LLM-Shared-Request-Type':
|
|
212
|
+
googleOptions.sharedRequestType,
|
|
213
|
+
}),
|
|
214
|
+
...(googleOptions.requestType && {
|
|
215
|
+
'X-Vertex-AI-LLM-Request-Type': googleOptions.requestType,
|
|
216
|
+
}),
|
|
217
|
+
}
|
|
218
|
+
: undefined;
|
|
219
|
+
const bodyServiceTier = isVertexProvider
|
|
220
|
+
? undefined
|
|
221
|
+
: googleOptions?.serviceTier;
|
|
222
|
+
|
|
223
|
+
const isGemmaModel = this.modelId.toLowerCase().startsWith('gemma-');
|
|
224
|
+
const isGemini3Model = /^gemini-3[.-]/.test(this.modelId);
|
|
225
|
+
const supportsFunctionResponseParts = isGemini3Model;
|
|
226
|
+
|
|
227
|
+
const { contents, systemInstruction } = convertToGoogleMessages(prompt, {
|
|
228
|
+
isGemmaModel,
|
|
229
|
+
isGemini3Model,
|
|
230
|
+
onWarning: warning => warnings.push(warning),
|
|
231
|
+
providerOptionsNames,
|
|
232
|
+
supportsFunctionResponseParts,
|
|
233
|
+
});
|
|
140
234
|
|
|
141
235
|
const {
|
|
142
236
|
tools: googleTools,
|
|
@@ -146,8 +240,42 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
146
240
|
tools,
|
|
147
241
|
toolChoice,
|
|
148
242
|
modelId: this.modelId,
|
|
243
|
+
isVertexProvider,
|
|
149
244
|
});
|
|
150
245
|
|
|
246
|
+
const resolvedThinking = resolveThinkingConfig({
|
|
247
|
+
reasoning,
|
|
248
|
+
modelId: this.modelId,
|
|
249
|
+
warnings,
|
|
250
|
+
});
|
|
251
|
+
const thinkingConfig =
|
|
252
|
+
googleOptions?.thinkingConfig || resolvedThinking
|
|
253
|
+
? { ...resolvedThinking, ...googleOptions?.thinkingConfig }
|
|
254
|
+
: undefined;
|
|
255
|
+
|
|
256
|
+
const streamFunctionCallArguments =
|
|
257
|
+
isStreaming && isVertexProvider
|
|
258
|
+
? (googleOptions?.streamFunctionCallArguments ?? false)
|
|
259
|
+
: undefined;
|
|
260
|
+
|
|
261
|
+
const toolConfig =
|
|
262
|
+
googleToolConfig ||
|
|
263
|
+
streamFunctionCallArguments ||
|
|
264
|
+
googleOptions?.retrievalConfig
|
|
265
|
+
? {
|
|
266
|
+
...googleToolConfig,
|
|
267
|
+
...(streamFunctionCallArguments && {
|
|
268
|
+
functionCallingConfig: {
|
|
269
|
+
...googleToolConfig?.functionCallingConfig,
|
|
270
|
+
streamFunctionCallArguments: true as const,
|
|
271
|
+
},
|
|
272
|
+
}),
|
|
273
|
+
...(googleOptions?.retrievalConfig && {
|
|
274
|
+
retrievalConfig: googleOptions.retrievalConfig,
|
|
275
|
+
}),
|
|
276
|
+
}
|
|
277
|
+
: undefined;
|
|
278
|
+
|
|
151
279
|
return {
|
|
152
280
|
args: {
|
|
153
281
|
generationConfig: {
|
|
@@ -179,7 +307,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
179
307
|
|
|
180
308
|
// provider options:
|
|
181
309
|
responseModalities: googleOptions?.responseModalities,
|
|
182
|
-
thinkingConfig
|
|
310
|
+
thinkingConfig,
|
|
183
311
|
...(googleOptions?.mediaResolution && {
|
|
184
312
|
mediaResolution: googleOptions.mediaResolution,
|
|
185
313
|
}),
|
|
@@ -191,28 +319,31 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
191
319
|
systemInstruction: isGemmaModel ? undefined : systemInstruction,
|
|
192
320
|
safetySettings: googleOptions?.safetySettings,
|
|
193
321
|
tools: googleTools,
|
|
194
|
-
toolConfig
|
|
195
|
-
? {
|
|
196
|
-
...googleToolConfig,
|
|
197
|
-
retrievalConfig: googleOptions.retrievalConfig,
|
|
198
|
-
}
|
|
199
|
-
: googleToolConfig,
|
|
322
|
+
toolConfig,
|
|
200
323
|
cachedContent: googleOptions?.cachedContent,
|
|
201
324
|
labels: googleOptions?.labels,
|
|
325
|
+
serviceTier: bodyServiceTier,
|
|
202
326
|
},
|
|
203
327
|
warnings: [...warnings, ...toolWarnings],
|
|
204
|
-
|
|
328
|
+
providerOptionsNames,
|
|
329
|
+
extraHeaders: vertexPaygoHeaders,
|
|
205
330
|
};
|
|
206
331
|
}
|
|
207
332
|
|
|
208
333
|
async doGenerate(
|
|
209
|
-
options:
|
|
210
|
-
): Promise<
|
|
211
|
-
const { args, warnings,
|
|
334
|
+
options: LanguageModelV4CallOptions,
|
|
335
|
+
): Promise<LanguageModelV4GenerateResult> {
|
|
336
|
+
const { args, warnings, providerOptionsNames, extraHeaders } =
|
|
337
|
+
await this.getArgs(options);
|
|
338
|
+
const wrapProviderMetadata = (payload: Record<string, unknown>) =>
|
|
339
|
+
Object.fromEntries(
|
|
340
|
+
providerOptionsNames.map(name => [name, payload]),
|
|
341
|
+
) as SharedV4ProviderMetadata;
|
|
212
342
|
|
|
213
343
|
const mergedHeaders = combineHeaders(
|
|
214
|
-
await resolve(this.config.headers),
|
|
344
|
+
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
215
345
|
options.headers,
|
|
346
|
+
extraHeaders,
|
|
216
347
|
);
|
|
217
348
|
|
|
218
349
|
const {
|
|
@@ -232,7 +363,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
232
363
|
});
|
|
233
364
|
|
|
234
365
|
const candidate = response.candidates[0];
|
|
235
|
-
const content: Array<
|
|
366
|
+
const content: Array<LanguageModelV4Content> = [];
|
|
236
367
|
|
|
237
368
|
// map ordered parts to content:
|
|
238
369
|
const parts = candidate.content?.parts ?? [];
|
|
@@ -241,6 +372,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
241
372
|
|
|
242
373
|
// Associates a code execution result with its preceding call.
|
|
243
374
|
let lastCodeExecutionToolCallId: string | undefined;
|
|
375
|
+
// Associates a server-side tool response with its preceding call (tool combination).
|
|
376
|
+
let lastServerToolCallId: string | undefined;
|
|
244
377
|
|
|
245
378
|
// Build content array from all parts
|
|
246
379
|
for (const part of parts) {
|
|
@@ -270,11 +403,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
270
403
|
lastCodeExecutionToolCallId = undefined;
|
|
271
404
|
} else if ('text' in part && part.text != null) {
|
|
272
405
|
const thoughtSignatureMetadata = part.thoughtSignature
|
|
273
|
-
? {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
},
|
|
277
|
-
}
|
|
406
|
+
? wrapProviderMetadata({
|
|
407
|
+
thoughtSignature: part.thoughtSignature,
|
|
408
|
+
})
|
|
278
409
|
: undefined;
|
|
279
410
|
|
|
280
411
|
if (part.text.length === 0) {
|
|
@@ -289,39 +420,74 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
289
420
|
providerMetadata: thoughtSignatureMetadata,
|
|
290
421
|
});
|
|
291
422
|
}
|
|
292
|
-
} else if ('functionCall' in part) {
|
|
423
|
+
} else if ('functionCall' in part && part.functionCall.name != null) {
|
|
293
424
|
content.push({
|
|
294
425
|
type: 'tool-call' as const,
|
|
295
|
-
toolCallId: this.config.generateId(),
|
|
426
|
+
toolCallId: part.functionCall.id ?? this.config.generateId(),
|
|
296
427
|
toolName: part.functionCall.name,
|
|
297
|
-
input: JSON.stringify(part.functionCall.args),
|
|
428
|
+
input: JSON.stringify(part.functionCall.args ?? {}),
|
|
298
429
|
providerMetadata: part.thoughtSignature
|
|
299
|
-
? {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
},
|
|
303
|
-
}
|
|
430
|
+
? wrapProviderMetadata({
|
|
431
|
+
thoughtSignature: part.thoughtSignature,
|
|
432
|
+
})
|
|
304
433
|
: undefined,
|
|
305
434
|
});
|
|
306
435
|
} else if ('inlineData' in part) {
|
|
307
436
|
const hasThought = part.thought === true;
|
|
308
437
|
const hasThoughtSignature = !!part.thoughtSignature;
|
|
309
438
|
content.push({
|
|
310
|
-
type: 'file'
|
|
311
|
-
data: part.inlineData.data,
|
|
439
|
+
type: hasThought ? 'reasoning-file' : 'file',
|
|
440
|
+
data: { type: 'data', data: part.inlineData.data },
|
|
312
441
|
mediaType: part.inlineData.mimeType,
|
|
313
|
-
providerMetadata:
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
...(hasThoughtSignature
|
|
319
|
-
? { thoughtSignature: part.thoughtSignature }
|
|
320
|
-
: {}),
|
|
321
|
-
},
|
|
322
|
-
}
|
|
323
|
-
: undefined,
|
|
442
|
+
providerMetadata: hasThoughtSignature
|
|
443
|
+
? wrapProviderMetadata({
|
|
444
|
+
thoughtSignature: part.thoughtSignature,
|
|
445
|
+
})
|
|
446
|
+
: undefined,
|
|
324
447
|
});
|
|
448
|
+
} else if ('toolCall' in part && part.toolCall) {
|
|
449
|
+
const toolCallId = part.toolCall.id ?? this.config.generateId();
|
|
450
|
+
lastServerToolCallId = toolCallId;
|
|
451
|
+
content.push({
|
|
452
|
+
type: 'tool-call',
|
|
453
|
+
toolCallId,
|
|
454
|
+
toolName: `server:${part.toolCall.toolType}`,
|
|
455
|
+
input: JSON.stringify(part.toolCall.args ?? {}),
|
|
456
|
+
providerExecuted: true,
|
|
457
|
+
dynamic: true,
|
|
458
|
+
providerMetadata: part.thoughtSignature
|
|
459
|
+
? wrapProviderMetadata({
|
|
460
|
+
thoughtSignature: part.thoughtSignature,
|
|
461
|
+
serverToolCallId: toolCallId,
|
|
462
|
+
serverToolType: part.toolCall.toolType,
|
|
463
|
+
})
|
|
464
|
+
: wrapProviderMetadata({
|
|
465
|
+
serverToolCallId: toolCallId,
|
|
466
|
+
serverToolType: part.toolCall.toolType,
|
|
467
|
+
}),
|
|
468
|
+
});
|
|
469
|
+
} else if ('toolResponse' in part && part.toolResponse) {
|
|
470
|
+
const responseToolCallId =
|
|
471
|
+
lastServerToolCallId ??
|
|
472
|
+
part.toolResponse.id ??
|
|
473
|
+
this.config.generateId();
|
|
474
|
+
content.push({
|
|
475
|
+
type: 'tool-result',
|
|
476
|
+
toolCallId: responseToolCallId,
|
|
477
|
+
toolName: `server:${part.toolResponse.toolType}`,
|
|
478
|
+
result: (part.toolResponse.response ?? {}) as JSONObject,
|
|
479
|
+
providerMetadata: part.thoughtSignature
|
|
480
|
+
? wrapProviderMetadata({
|
|
481
|
+
thoughtSignature: part.thoughtSignature,
|
|
482
|
+
serverToolCallId: responseToolCallId,
|
|
483
|
+
serverToolType: part.toolResponse.toolType,
|
|
484
|
+
})
|
|
485
|
+
: wrapProviderMetadata({
|
|
486
|
+
serverToolCallId: responseToolCallId,
|
|
487
|
+
serverToolType: part.toolResponse.toolType,
|
|
488
|
+
}),
|
|
489
|
+
});
|
|
490
|
+
lastServerToolCallId = undefined;
|
|
325
491
|
}
|
|
326
492
|
}
|
|
327
493
|
|
|
@@ -337,7 +503,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
337
503
|
return {
|
|
338
504
|
content,
|
|
339
505
|
finishReason: {
|
|
340
|
-
unified:
|
|
506
|
+
unified: mapGoogleFinishReason({
|
|
341
507
|
finishReason: candidate.finishReason,
|
|
342
508
|
// Only count client-executed tool calls for finish reason determination.
|
|
343
509
|
hasToolCalls: content.some(
|
|
@@ -346,17 +512,17 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
346
512
|
}),
|
|
347
513
|
raw: candidate.finishReason ?? undefined,
|
|
348
514
|
},
|
|
349
|
-
usage:
|
|
515
|
+
usage: convertGoogleUsage(usageMetadata),
|
|
350
516
|
warnings,
|
|
351
|
-
providerMetadata: {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
},
|
|
517
|
+
providerMetadata: wrapProviderMetadata({
|
|
518
|
+
promptFeedback: response.promptFeedback ?? null,
|
|
519
|
+
groundingMetadata: candidate.groundingMetadata ?? null,
|
|
520
|
+
urlContextMetadata: candidate.urlContextMetadata ?? null,
|
|
521
|
+
safetyRatings: candidate.safetyRatings ?? null,
|
|
522
|
+
usageMetadata: usageMetadata ?? null,
|
|
523
|
+
finishMessage: candidate.finishMessage ?? null,
|
|
524
|
+
serviceTier: usageMetadata?.serviceTier ?? null,
|
|
525
|
+
} satisfies GoogleProviderMetadata),
|
|
360
526
|
request: { body: args },
|
|
361
527
|
response: {
|
|
362
528
|
// TODO timestamp, model id, id
|
|
@@ -367,13 +533,19 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
367
533
|
}
|
|
368
534
|
|
|
369
535
|
async doStream(
|
|
370
|
-
options:
|
|
371
|
-
): Promise<
|
|
372
|
-
const { args, warnings,
|
|
536
|
+
options: LanguageModelV4CallOptions,
|
|
537
|
+
): Promise<LanguageModelV4StreamResult> {
|
|
538
|
+
const { args, warnings, providerOptionsNames, extraHeaders } =
|
|
539
|
+
await this.getArgs(options, { isStreaming: true });
|
|
540
|
+
const wrapProviderMetadata = (payload: Record<string, unknown>) =>
|
|
541
|
+
Object.fromEntries(
|
|
542
|
+
providerOptionsNames.map(name => [name, payload]),
|
|
543
|
+
) as SharedV4ProviderMetadata;
|
|
373
544
|
|
|
374
545
|
const headers = combineHeaders(
|
|
375
|
-
await resolve(this.config.headers),
|
|
546
|
+
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
376
547
|
options.headers,
|
|
548
|
+
extraHeaders,
|
|
377
549
|
);
|
|
378
550
|
|
|
379
551
|
const { responseHeaders, value: response } = await postJsonToApi({
|
|
@@ -388,12 +560,12 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
388
560
|
fetch: this.config.fetch,
|
|
389
561
|
});
|
|
390
562
|
|
|
391
|
-
let finishReason:
|
|
563
|
+
let finishReason: LanguageModelV4FinishReason = {
|
|
392
564
|
unified: 'other',
|
|
393
565
|
raw: undefined,
|
|
394
566
|
};
|
|
395
|
-
let usage:
|
|
396
|
-
let providerMetadata:
|
|
567
|
+
let usage: GoogleUsageMetadata | undefined = undefined;
|
|
568
|
+
let providerMetadata: SharedV4ProviderMetadata | undefined = undefined;
|
|
397
569
|
let lastGroundingMetadata: GroundingMetadataSchema | null = null;
|
|
398
570
|
let lastUrlContextMetadata: UrlContextMetadataSchema | null = null;
|
|
399
571
|
|
|
@@ -409,12 +581,57 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
409
581
|
const emittedSourceUrls = new Set<string>();
|
|
410
582
|
// Associates a code execution result with its preceding call.
|
|
411
583
|
let lastCodeExecutionToolCallId: string | undefined;
|
|
584
|
+
// Associates a server-side tool response with its preceding call (tool combination).
|
|
585
|
+
let lastServerToolCallId: string | undefined;
|
|
586
|
+
|
|
587
|
+
const activeStreamingToolCalls: Array<{
|
|
588
|
+
toolCallId: string;
|
|
589
|
+
toolName: string;
|
|
590
|
+
accumulator: GoogleJSONAccumulator;
|
|
591
|
+
providerMetadata?: SharedV4ProviderMetadata;
|
|
592
|
+
}> = [];
|
|
593
|
+
|
|
594
|
+
const finishActiveStreamingToolCall = (
|
|
595
|
+
controller: TransformStreamDefaultController<LanguageModelV4StreamPart>,
|
|
596
|
+
) => {
|
|
597
|
+
const active = activeStreamingToolCalls.pop();
|
|
598
|
+
if (active == null) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const { finalJSON, closingDelta } = active.accumulator.finalize();
|
|
603
|
+
|
|
604
|
+
if (closingDelta.length > 0) {
|
|
605
|
+
controller.enqueue({
|
|
606
|
+
type: 'tool-input-delta',
|
|
607
|
+
id: active.toolCallId,
|
|
608
|
+
delta: closingDelta,
|
|
609
|
+
providerMetadata: active.providerMetadata,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
controller.enqueue({
|
|
614
|
+
type: 'tool-input-end',
|
|
615
|
+
id: active.toolCallId,
|
|
616
|
+
providerMetadata: active.providerMetadata,
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
controller.enqueue({
|
|
620
|
+
type: 'tool-call',
|
|
621
|
+
toolCallId: active.toolCallId,
|
|
622
|
+
toolName: active.toolName,
|
|
623
|
+
input: finalJSON,
|
|
624
|
+
providerMetadata: active.providerMetadata,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
hasToolCalls = true;
|
|
628
|
+
};
|
|
412
629
|
|
|
413
630
|
return {
|
|
414
631
|
stream: response.pipeThrough(
|
|
415
632
|
new TransformStream<
|
|
416
633
|
ParseResult<ChunkSchema>,
|
|
417
|
-
|
|
634
|
+
LanguageModelV4StreamPart
|
|
418
635
|
>({
|
|
419
636
|
start(controller) {
|
|
420
637
|
controller.enqueue({ type: 'stream-start', warnings });
|
|
@@ -508,11 +725,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
508
725
|
}
|
|
509
726
|
} else if ('text' in part && part.text != null) {
|
|
510
727
|
const thoughtSignatureMetadata = part.thoughtSignature
|
|
511
|
-
? {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
},
|
|
515
|
-
}
|
|
728
|
+
? wrapProviderMetadata({
|
|
729
|
+
thoughtSignature: part.thoughtSignature,
|
|
730
|
+
})
|
|
516
731
|
: undefined;
|
|
517
732
|
|
|
518
733
|
if (part.text.length === 0) {
|
|
@@ -598,60 +813,222 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
598
813
|
|
|
599
814
|
const hasThought = part.thought === true;
|
|
600
815
|
const hasThoughtSignature = !!part.thoughtSignature;
|
|
601
|
-
const fileMeta =
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
...(hasThoughtSignature
|
|
607
|
-
? { thoughtSignature: part.thoughtSignature }
|
|
608
|
-
: {}),
|
|
609
|
-
},
|
|
610
|
-
}
|
|
611
|
-
: undefined;
|
|
816
|
+
const fileMeta = hasThoughtSignature
|
|
817
|
+
? wrapProviderMetadata({
|
|
818
|
+
thoughtSignature: part.thoughtSignature,
|
|
819
|
+
})
|
|
820
|
+
: undefined;
|
|
612
821
|
controller.enqueue({
|
|
613
|
-
type: 'file',
|
|
822
|
+
type: hasThought ? 'reasoning-file' : 'file',
|
|
614
823
|
mediaType: part.inlineData.mimeType,
|
|
615
|
-
data: part.inlineData.data,
|
|
824
|
+
data: { type: 'data', data: part.inlineData.data },
|
|
616
825
|
providerMetadata: fileMeta,
|
|
617
826
|
});
|
|
827
|
+
} else if ('toolCall' in part && part.toolCall) {
|
|
828
|
+
const toolCallId = part.toolCall.id ?? generateId();
|
|
829
|
+
lastServerToolCallId = toolCallId;
|
|
830
|
+
const serverMeta = wrapProviderMetadata({
|
|
831
|
+
...(part.thoughtSignature
|
|
832
|
+
? { thoughtSignature: part.thoughtSignature }
|
|
833
|
+
: {}),
|
|
834
|
+
serverToolCallId: toolCallId,
|
|
835
|
+
serverToolType: part.toolCall.toolType,
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
controller.enqueue({
|
|
839
|
+
type: 'tool-call',
|
|
840
|
+
toolCallId,
|
|
841
|
+
toolName: `server:${part.toolCall.toolType}`,
|
|
842
|
+
input: JSON.stringify(part.toolCall.args ?? {}),
|
|
843
|
+
providerExecuted: true,
|
|
844
|
+
dynamic: true,
|
|
845
|
+
providerMetadata: serverMeta,
|
|
846
|
+
});
|
|
847
|
+
} else if ('toolResponse' in part && part.toolResponse) {
|
|
848
|
+
const responseToolCallId =
|
|
849
|
+
lastServerToolCallId ??
|
|
850
|
+
part.toolResponse.id ??
|
|
851
|
+
generateId();
|
|
852
|
+
const serverMeta = wrapProviderMetadata({
|
|
853
|
+
...(part.thoughtSignature
|
|
854
|
+
? { thoughtSignature: part.thoughtSignature }
|
|
855
|
+
: {}),
|
|
856
|
+
serverToolCallId: responseToolCallId,
|
|
857
|
+
serverToolType: part.toolResponse.toolType,
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
controller.enqueue({
|
|
861
|
+
type: 'tool-result',
|
|
862
|
+
toolCallId: responseToolCallId,
|
|
863
|
+
toolName: `server:${part.toolResponse.toolType}`,
|
|
864
|
+
result: (part.toolResponse.response ?? {}) as JSONObject,
|
|
865
|
+
providerMetadata: serverMeta,
|
|
866
|
+
});
|
|
867
|
+
lastServerToolCallId = undefined;
|
|
618
868
|
}
|
|
619
869
|
}
|
|
620
870
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
871
|
+
// Handle streaming and complete function calls
|
|
872
|
+
for (const part of parts) {
|
|
873
|
+
if (!('functionCall' in part)) continue;
|
|
874
|
+
|
|
875
|
+
const providerMeta = part.thoughtSignature
|
|
876
|
+
? wrapProviderMetadata({
|
|
877
|
+
thoughtSignature: part.thoughtSignature,
|
|
878
|
+
})
|
|
879
|
+
: undefined;
|
|
880
|
+
|
|
881
|
+
const isStreamingChunk =
|
|
882
|
+
part.functionCall.partialArgs != null ||
|
|
883
|
+
(part.functionCall.name != null &&
|
|
884
|
+
part.functionCall.willContinue === true);
|
|
885
|
+
const isTerminalChunk =
|
|
886
|
+
part.functionCall.name == null &&
|
|
887
|
+
part.functionCall.args == null &&
|
|
888
|
+
part.functionCall.partialArgs == null &&
|
|
889
|
+
part.functionCall.willContinue == null;
|
|
890
|
+
const isCompleteCall =
|
|
891
|
+
part.functionCall.name != null &&
|
|
892
|
+
part.functionCall.args != null &&
|
|
893
|
+
part.functionCall.partialArgs == null;
|
|
894
|
+
// Single-chunk no-args call: `{ name: 'X' }` with no `args`,
|
|
895
|
+
// `partialArgs`, or `willContinue`. Carries `thoughtSignature`.
|
|
896
|
+
const isNoArgsCompleteCall =
|
|
897
|
+
part.functionCall.name != null &&
|
|
898
|
+
part.functionCall.args == null &&
|
|
899
|
+
part.functionCall.partialArgs == null &&
|
|
900
|
+
part.functionCall.willContinue !== true;
|
|
901
|
+
|
|
902
|
+
if (isStreamingChunk) {
|
|
903
|
+
if (part.functionCall.name != null) {
|
|
904
|
+
const toolCallId = part.functionCall.id ?? generateId();
|
|
905
|
+
const accumulator = new GoogleJSONAccumulator();
|
|
906
|
+
activeStreamingToolCalls.push({
|
|
907
|
+
toolCallId,
|
|
908
|
+
toolName: part.functionCall.name,
|
|
909
|
+
accumulator,
|
|
910
|
+
providerMetadata: providerMeta,
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
controller.enqueue({
|
|
914
|
+
type: 'tool-input-start',
|
|
915
|
+
id: toolCallId,
|
|
916
|
+
toolName: part.functionCall.name,
|
|
917
|
+
providerMetadata: providerMeta,
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
if (part.functionCall.partialArgs != null) {
|
|
921
|
+
const partialArgs = part.functionCall
|
|
922
|
+
.partialArgs as PartialArg[];
|
|
923
|
+
const { textDelta } =
|
|
924
|
+
accumulator.processPartialArgs(partialArgs);
|
|
925
|
+
if (textDelta.length > 0) {
|
|
926
|
+
controller.enqueue({
|
|
927
|
+
type: 'tool-input-delta',
|
|
928
|
+
id: toolCallId,
|
|
929
|
+
delta: textDelta,
|
|
930
|
+
providerMetadata: providerMeta,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
if (
|
|
934
|
+
part.functionCall.willContinue !== true &&
|
|
935
|
+
partialArgs.every(arg => arg.willContinue !== true)
|
|
936
|
+
) {
|
|
937
|
+
finishActiveStreamingToolCall(controller);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
} else if (
|
|
941
|
+
part.functionCall.partialArgs != null &&
|
|
942
|
+
activeStreamingToolCalls.length > 0
|
|
943
|
+
) {
|
|
944
|
+
const active =
|
|
945
|
+
activeStreamingToolCalls[
|
|
946
|
+
activeStreamingToolCalls.length - 1
|
|
947
|
+
];
|
|
948
|
+
const partialArgs = part.functionCall
|
|
949
|
+
.partialArgs as PartialArg[];
|
|
950
|
+
const { textDelta } =
|
|
951
|
+
active.accumulator.processPartialArgs(partialArgs);
|
|
952
|
+
if (textDelta.length > 0) {
|
|
953
|
+
controller.enqueue({
|
|
954
|
+
type: 'tool-input-delta',
|
|
955
|
+
id: active.toolCallId,
|
|
956
|
+
delta: textDelta,
|
|
957
|
+
providerMetadata: providerMeta,
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
if (
|
|
961
|
+
part.functionCall.willContinue !== true &&
|
|
962
|
+
partialArgs.every(arg => arg.willContinue !== true)
|
|
963
|
+
) {
|
|
964
|
+
finishActiveStreamingToolCall(controller);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
} else if (
|
|
968
|
+
isTerminalChunk &&
|
|
969
|
+
activeStreamingToolCalls.length > 0
|
|
970
|
+
) {
|
|
971
|
+
finishActiveStreamingToolCall(controller);
|
|
972
|
+
} else if (isCompleteCall) {
|
|
973
|
+
const toolCallId = part.functionCall.id ?? generateId();
|
|
974
|
+
const toolName = part.functionCall.name!;
|
|
975
|
+
const args =
|
|
976
|
+
typeof part.functionCall.args === 'string'
|
|
977
|
+
? part.functionCall.args
|
|
978
|
+
: JSON.stringify(part.functionCall.args ?? {});
|
|
626
979
|
|
|
627
|
-
if (toolCallDeltas != null) {
|
|
628
|
-
for (const toolCall of toolCallDeltas) {
|
|
629
980
|
controller.enqueue({
|
|
630
981
|
type: 'tool-input-start',
|
|
631
|
-
id:
|
|
632
|
-
toolName
|
|
633
|
-
providerMetadata:
|
|
982
|
+
id: toolCallId,
|
|
983
|
+
toolName,
|
|
984
|
+
providerMetadata: providerMeta,
|
|
634
985
|
});
|
|
635
986
|
|
|
636
987
|
controller.enqueue({
|
|
637
988
|
type: 'tool-input-delta',
|
|
638
|
-
id:
|
|
639
|
-
delta:
|
|
640
|
-
providerMetadata:
|
|
989
|
+
id: toolCallId,
|
|
990
|
+
delta: args,
|
|
991
|
+
providerMetadata: providerMeta,
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
controller.enqueue({
|
|
995
|
+
type: 'tool-input-end',
|
|
996
|
+
id: toolCallId,
|
|
997
|
+
providerMetadata: providerMeta,
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
controller.enqueue({
|
|
1001
|
+
type: 'tool-call',
|
|
1002
|
+
toolCallId,
|
|
1003
|
+
toolName,
|
|
1004
|
+
input: args,
|
|
1005
|
+
providerMetadata: providerMeta,
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
hasToolCalls = true;
|
|
1009
|
+
} else if (isNoArgsCompleteCall) {
|
|
1010
|
+
const toolCallId = part.functionCall.id ?? generateId();
|
|
1011
|
+
const toolName = part.functionCall.name!;
|
|
1012
|
+
|
|
1013
|
+
controller.enqueue({
|
|
1014
|
+
type: 'tool-input-start',
|
|
1015
|
+
id: toolCallId,
|
|
1016
|
+
toolName,
|
|
1017
|
+
providerMetadata: providerMeta,
|
|
641
1018
|
});
|
|
642
1019
|
|
|
643
1020
|
controller.enqueue({
|
|
644
1021
|
type: 'tool-input-end',
|
|
645
|
-
id:
|
|
646
|
-
providerMetadata:
|
|
1022
|
+
id: toolCallId,
|
|
1023
|
+
providerMetadata: providerMeta,
|
|
647
1024
|
});
|
|
648
1025
|
|
|
649
1026
|
controller.enqueue({
|
|
650
1027
|
type: 'tool-call',
|
|
651
|
-
toolCallId
|
|
652
|
-
toolName
|
|
653
|
-
input:
|
|
654
|
-
providerMetadata:
|
|
1028
|
+
toolCallId,
|
|
1029
|
+
toolName,
|
|
1030
|
+
input: '{}',
|
|
1031
|
+
providerMetadata: providerMeta,
|
|
655
1032
|
});
|
|
656
1033
|
|
|
657
1034
|
hasToolCalls = true;
|
|
@@ -661,29 +1038,22 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
661
1038
|
|
|
662
1039
|
if (candidate.finishReason != null) {
|
|
663
1040
|
finishReason = {
|
|
664
|
-
unified:
|
|
1041
|
+
unified: mapGoogleFinishReason({
|
|
665
1042
|
finishReason: candidate.finishReason,
|
|
666
1043
|
hasToolCalls,
|
|
667
1044
|
}),
|
|
668
1045
|
raw: candidate.finishReason,
|
|
669
1046
|
};
|
|
670
1047
|
|
|
671
|
-
providerMetadata = {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
(
|
|
681
|
-
providerMetadata[providerOptionsName] as Record<
|
|
682
|
-
string,
|
|
683
|
-
unknown
|
|
684
|
-
>
|
|
685
|
-
).usageMetadata = usageMetadata;
|
|
686
|
-
}
|
|
1048
|
+
providerMetadata = wrapProviderMetadata({
|
|
1049
|
+
promptFeedback: value.promptFeedback ?? null,
|
|
1050
|
+
groundingMetadata: lastGroundingMetadata,
|
|
1051
|
+
urlContextMetadata: lastUrlContextMetadata,
|
|
1052
|
+
safetyRatings: candidate.safetyRatings ?? null,
|
|
1053
|
+
usageMetadata: usageMetadata ?? null,
|
|
1054
|
+
finishMessage: candidate.finishMessage ?? null,
|
|
1055
|
+
serviceTier: usage?.serviceTier ?? null,
|
|
1056
|
+
} satisfies GoogleProviderMetadata);
|
|
687
1057
|
}
|
|
688
1058
|
},
|
|
689
1059
|
|
|
@@ -704,7 +1074,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
704
1074
|
controller.enqueue({
|
|
705
1075
|
type: 'finish',
|
|
706
1076
|
finishReason,
|
|
707
|
-
usage:
|
|
1077
|
+
usage: convertGoogleUsage(usage),
|
|
708
1078
|
providerMetadata,
|
|
709
1079
|
});
|
|
710
1080
|
},
|
|
@@ -716,39 +1086,107 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
716
1086
|
}
|
|
717
1087
|
}
|
|
718
1088
|
|
|
719
|
-
function
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1089
|
+
function isGemini3Model(modelId: string): boolean {
|
|
1090
|
+
return /gemini-3[\.\-]/i.test(modelId) || /gemini-3$/i.test(modelId);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function getMaxOutputTokensForGemini25Model(): number {
|
|
1094
|
+
return 65536;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
function getMaxThinkingTokensForGemini25Model(modelId: string): number {
|
|
1098
|
+
const id = modelId.toLowerCase();
|
|
1099
|
+
if (id.includes('2.5-pro') || id.includes('gemini-3-pro-image')) {
|
|
1100
|
+
return 32768;
|
|
1101
|
+
}
|
|
1102
|
+
return 24576;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
type GoogleThinkingConfig = NonNullable<
|
|
1106
|
+
InferSchema<typeof googleLanguageModelOptions>['thinkingConfig']
|
|
1107
|
+
>;
|
|
1108
|
+
|
|
1109
|
+
function resolveThinkingConfig({
|
|
1110
|
+
reasoning,
|
|
1111
|
+
modelId,
|
|
1112
|
+
warnings,
|
|
723
1113
|
}: {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
1114
|
+
reasoning: LanguageModelV4CallOptions['reasoning'];
|
|
1115
|
+
modelId: string;
|
|
1116
|
+
warnings: SharedV4Warning[];
|
|
1117
|
+
}): Omit<GoogleThinkingConfig, 'includeThoughts'> | undefined {
|
|
1118
|
+
if (!isCustomReasoning(reasoning)) {
|
|
1119
|
+
return undefined;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (isGemini3Model(modelId) && !modelId.includes('gemini-3-pro-image')) {
|
|
1123
|
+
return resolveGemini3ThinkingConfig({ reasoning, warnings });
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
return resolveGemini25ThinkingConfig({ reasoning, modelId, warnings });
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function resolveGemini3ThinkingConfig({
|
|
1130
|
+
reasoning,
|
|
1131
|
+
warnings,
|
|
1132
|
+
}: {
|
|
1133
|
+
reasoning: Exclude<
|
|
1134
|
+
LanguageModelV4CallOptions['reasoning'],
|
|
1135
|
+
'provider-default' | undefined
|
|
735
1136
|
>;
|
|
1137
|
+
warnings: SharedV4Warning[];
|
|
1138
|
+
}): Pick<GoogleThinkingConfig, 'thinkingLevel'> | undefined {
|
|
1139
|
+
if (reasoning === 'none') {
|
|
1140
|
+
// It's not possible to fully disable thinking with Gemini 3.
|
|
1141
|
+
return { thinkingLevel: 'minimal' };
|
|
1142
|
+
}
|
|
736
1143
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
:
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1144
|
+
const thinkingLevel = mapReasoningToProviderEffort({
|
|
1145
|
+
reasoning,
|
|
1146
|
+
effortMap: {
|
|
1147
|
+
minimal: 'minimal',
|
|
1148
|
+
low: 'low',
|
|
1149
|
+
medium: 'medium',
|
|
1150
|
+
high: 'high',
|
|
1151
|
+
xhigh: 'high',
|
|
1152
|
+
},
|
|
1153
|
+
warnings,
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
if (thinkingLevel == null) {
|
|
1157
|
+
return undefined;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return { thinkingLevel };
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function resolveGemini25ThinkingConfig({
|
|
1164
|
+
reasoning,
|
|
1165
|
+
modelId,
|
|
1166
|
+
warnings,
|
|
1167
|
+
}: {
|
|
1168
|
+
reasoning: Exclude<
|
|
1169
|
+
LanguageModelV4CallOptions['reasoning'],
|
|
1170
|
+
'provider-default' | undefined
|
|
1171
|
+
>;
|
|
1172
|
+
modelId: string;
|
|
1173
|
+
warnings: SharedV4Warning[];
|
|
1174
|
+
}): Pick<GoogleThinkingConfig, 'thinkingBudget'> | undefined {
|
|
1175
|
+
if (reasoning === 'none') {
|
|
1176
|
+
return { thinkingBudget: 0 };
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
const thinkingBudget = mapReasoningToProviderBudget({
|
|
1180
|
+
reasoning,
|
|
1181
|
+
maxOutputTokens: getMaxOutputTokensForGemini25Model(),
|
|
1182
|
+
maxReasoningBudget: getMaxThinkingTokensForGemini25Model(modelId),
|
|
1183
|
+
minReasoningBudget: 0,
|
|
1184
|
+
warnings,
|
|
1185
|
+
});
|
|
1186
|
+
if (thinkingBudget == null) {
|
|
1187
|
+
return undefined;
|
|
1188
|
+
}
|
|
1189
|
+
return { thinkingBudget };
|
|
752
1190
|
}
|
|
753
1191
|
|
|
754
1192
|
function extractSources({
|
|
@@ -757,12 +1195,12 @@ function extractSources({
|
|
|
757
1195
|
}: {
|
|
758
1196
|
groundingMetadata: GroundingMetadataSchema | undefined | null;
|
|
759
1197
|
generateId: () => string;
|
|
760
|
-
}): undefined |
|
|
1198
|
+
}): undefined | LanguageModelV4Source[] {
|
|
761
1199
|
if (!groundingMetadata?.groundingChunks) {
|
|
762
1200
|
return undefined;
|
|
763
1201
|
}
|
|
764
1202
|
|
|
765
|
-
const sources:
|
|
1203
|
+
const sources: LanguageModelV4Source[] = [];
|
|
766
1204
|
|
|
767
1205
|
for (const chunk of groundingMetadata.groundingChunks) {
|
|
768
1206
|
if (chunk.web != null) {
|
|
@@ -928,6 +1366,15 @@ export const getGroundingMetadataSchema = () =>
|
|
|
928
1366
|
.nullish(),
|
|
929
1367
|
});
|
|
930
1368
|
|
|
1369
|
+
const partialArgSchema = z.object({
|
|
1370
|
+
jsonPath: z.string(),
|
|
1371
|
+
stringValue: z.string().nullish(),
|
|
1372
|
+
numberValue: z.number().nullish(),
|
|
1373
|
+
boolValue: z.boolean().nullish(),
|
|
1374
|
+
nullValue: z.unknown().nullish(),
|
|
1375
|
+
willContinue: z.boolean().nullish(),
|
|
1376
|
+
});
|
|
1377
|
+
|
|
931
1378
|
const getContentSchema = () =>
|
|
932
1379
|
z.object({
|
|
933
1380
|
parts: z
|
|
@@ -936,8 +1383,11 @@ const getContentSchema = () =>
|
|
|
936
1383
|
// note: order matters since text can be fully empty
|
|
937
1384
|
z.object({
|
|
938
1385
|
functionCall: z.object({
|
|
939
|
-
|
|
940
|
-
|
|
1386
|
+
id: z.string().nullish(),
|
|
1387
|
+
name: z.string().nullish(),
|
|
1388
|
+
args: z.unknown().nullish(),
|
|
1389
|
+
partialArgs: z.array(partialArgSchema).nullish(),
|
|
1390
|
+
willContinue: z.boolean().nullish(),
|
|
941
1391
|
}),
|
|
942
1392
|
thoughtSignature: z.string().nullish(),
|
|
943
1393
|
}),
|
|
@@ -949,6 +1399,22 @@ const getContentSchema = () =>
|
|
|
949
1399
|
thought: z.boolean().nullish(),
|
|
950
1400
|
thoughtSignature: z.string().nullish(),
|
|
951
1401
|
}),
|
|
1402
|
+
z.object({
|
|
1403
|
+
toolCall: z.object({
|
|
1404
|
+
toolType: z.string(),
|
|
1405
|
+
args: z.unknown().nullish(),
|
|
1406
|
+
id: z.string(),
|
|
1407
|
+
}),
|
|
1408
|
+
thoughtSignature: z.string().nullish(),
|
|
1409
|
+
}),
|
|
1410
|
+
z.object({
|
|
1411
|
+
toolResponse: z.object({
|
|
1412
|
+
toolType: z.string(),
|
|
1413
|
+
response: z.unknown().nullish(),
|
|
1414
|
+
id: z.string(),
|
|
1415
|
+
}),
|
|
1416
|
+
thoughtSignature: z.string().nullish(),
|
|
1417
|
+
}),
|
|
952
1418
|
z.object({
|
|
953
1419
|
executableCode: z
|
|
954
1420
|
.object({
|
|
@@ -982,6 +1448,15 @@ const getSafetyRatingSchema = () =>
|
|
|
982
1448
|
blocked: z.boolean().nullish(),
|
|
983
1449
|
});
|
|
984
1450
|
|
|
1451
|
+
const tokenDetailsSchema = z
|
|
1452
|
+
.array(
|
|
1453
|
+
z.object({
|
|
1454
|
+
modality: z.string(),
|
|
1455
|
+
tokenCount: z.number(),
|
|
1456
|
+
}),
|
|
1457
|
+
)
|
|
1458
|
+
.nullish();
|
|
1459
|
+
|
|
985
1460
|
const usageSchema = z.object({
|
|
986
1461
|
cachedContentTokenCount: z.number().nullish(),
|
|
987
1462
|
thoughtsTokenCount: z.number().nullish(),
|
|
@@ -990,6 +1465,10 @@ const usageSchema = z.object({
|
|
|
990
1465
|
totalTokenCount: z.number().nullish(),
|
|
991
1466
|
// https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
|
|
992
1467
|
trafficType: z.string().nullish(),
|
|
1468
|
+
serviceTier: z.string().nullish(),
|
|
1469
|
+
// https://ai.google.dev/api/generate-content#Modality
|
|
1470
|
+
promptTokensDetails: tokenDetailsSchema,
|
|
1471
|
+
candidatesTokensDetails: tokenDetailsSchema,
|
|
993
1472
|
});
|
|
994
1473
|
|
|
995
1474
|
// https://ai.google.dev/api/generate-content#UrlRetrievalMetadata
|
|
@@ -1012,6 +1491,7 @@ const responseSchema = lazySchema(() =>
|
|
|
1012
1491
|
z.object({
|
|
1013
1492
|
content: getContentSchema().nullish().or(z.object({}).strict()),
|
|
1014
1493
|
finishReason: z.string().nullish(),
|
|
1494
|
+
finishMessage: z.string().nullish(),
|
|
1015
1495
|
safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
|
|
1016
1496
|
groundingMetadata: getGroundingMetadataSchema().nullish(),
|
|
1017
1497
|
urlContextMetadata: getUrlContextMetadataSchema().nullish(),
|
|
@@ -1028,17 +1508,10 @@ const responseSchema = lazySchema(() =>
|
|
|
1028
1508
|
),
|
|
1029
1509
|
);
|
|
1030
1510
|
|
|
1031
|
-
type ContentSchema = NonNullable<
|
|
1032
|
-
InferSchema<typeof responseSchema>['candidates'][number]['content']
|
|
1033
|
-
>;
|
|
1034
1511
|
export type GroundingMetadataSchema = NonNullable<
|
|
1035
1512
|
InferSchema<typeof responseSchema>['candidates'][number]['groundingMetadata']
|
|
1036
1513
|
>;
|
|
1037
1514
|
|
|
1038
|
-
type GroundingChunkSchema = NonNullable<
|
|
1039
|
-
GroundingMetadataSchema['groundingChunks']
|
|
1040
|
-
>[number];
|
|
1041
|
-
|
|
1042
1515
|
export type UrlContextMetadataSchema = NonNullable<
|
|
1043
1516
|
InferSchema<typeof responseSchema>['candidates'][number]['urlContextMetadata']
|
|
1044
1517
|
>;
|
|
@@ -1047,6 +1520,14 @@ export type SafetyRatingSchema = NonNullable<
|
|
|
1047
1520
|
InferSchema<typeof responseSchema>['candidates'][number]['safetyRatings']
|
|
1048
1521
|
>[number];
|
|
1049
1522
|
|
|
1523
|
+
export type PromptFeedbackSchema = NonNullable<
|
|
1524
|
+
InferSchema<typeof responseSchema>['promptFeedback']
|
|
1525
|
+
>;
|
|
1526
|
+
|
|
1527
|
+
export type UsageMetadataSchema = NonNullable<
|
|
1528
|
+
InferSchema<typeof responseSchema>['usageMetadata']
|
|
1529
|
+
>;
|
|
1530
|
+
|
|
1050
1531
|
// limited version of the schema, focussed on what is needed for the implementation
|
|
1051
1532
|
// this approach limits breakages when the API changes and increases efficiency
|
|
1052
1533
|
const chunkSchema = lazySchema(() =>
|
|
@@ -1057,6 +1538,7 @@ const chunkSchema = lazySchema(() =>
|
|
|
1057
1538
|
z.object({
|
|
1058
1539
|
content: getContentSchema().nullish(),
|
|
1059
1540
|
finishReason: z.string().nullish(),
|
|
1541
|
+
finishMessage: z.string().nullish(),
|
|
1060
1542
|
safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
|
|
1061
1543
|
groundingMetadata: getGroundingMetadataSchema().nullish(),
|
|
1062
1544
|
urlContextMetadata: getUrlContextMetadataSchema().nullish(),
|