@ai-sdk/google 4.0.0-beta.8 → 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 +608 -5
- package/README.md +6 -4
- package/dist/index.d.ts +297 -54
- package/dist/index.js +5409 -640
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +97 -26
- package/dist/internal/index.js +1653 -453
- 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} +691 -217
- 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 -384
- package/dist/index.mjs +0 -2519
- package/dist/index.mjs.map +0 -1
- package/dist/internal/index.d.mts +0 -287
- package/dist/internal/index.mjs +0 -1708
- 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 -47
|
@@ -1,75 +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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} from './google-generative-ai-prompt';
|
|
46
|
+
type GoogleLanguageModelOptions,
|
|
47
|
+
type GoogleModelId,
|
|
48
|
+
} from './google-language-model-options';
|
|
49
|
+
import type { GoogleProviderMetadata } from './google-prompt';
|
|
45
50
|
import { prepareTools } from './google-prepare-tools';
|
|
46
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
GoogleJSONAccumulator,
|
|
53
|
+
type PartialArg,
|
|
54
|
+
} from './google-json-accumulator';
|
|
55
|
+
import { mapGoogleFinishReason } from './map-google-finish-reason';
|
|
47
56
|
|
|
48
|
-
type
|
|
57
|
+
type GoogleConfig = {
|
|
49
58
|
provider: string;
|
|
50
59
|
baseURL: string;
|
|
51
|
-
headers
|
|
60
|
+
headers?: Resolvable<Record<string, string | undefined>>;
|
|
52
61
|
fetch?: FetchFunction;
|
|
53
62
|
generateId: () => string;
|
|
54
63
|
|
|
55
64
|
/**
|
|
56
65
|
* The supported URLs for the model.
|
|
57
66
|
*/
|
|
58
|
-
supportedUrls?: () =>
|
|
67
|
+
supportedUrls?: () => LanguageModelV4['supportedUrls'];
|
|
59
68
|
};
|
|
60
69
|
|
|
61
|
-
export class
|
|
62
|
-
readonly specificationVersion = '
|
|
70
|
+
export class GoogleLanguageModel implements LanguageModelV4 {
|
|
71
|
+
readonly specificationVersion = 'v4';
|
|
63
72
|
|
|
64
|
-
readonly modelId:
|
|
73
|
+
readonly modelId: GoogleModelId;
|
|
65
74
|
|
|
66
|
-
private readonly config:
|
|
75
|
+
private readonly config: GoogleConfig;
|
|
67
76
|
private readonly generateId: () => string;
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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) {
|
|
73
93
|
this.modelId = modelId;
|
|
74
94
|
this.config = config;
|
|
75
95
|
this.generateId = config.generateId ?? generateId;
|
|
@@ -83,33 +103,49 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
83
103
|
return this.config.supportedUrls?.() ?? {};
|
|
84
104
|
}
|
|
85
105
|
|
|
86
|
-
private async getArgs(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const warnings: SharedV3Warning[] = [];
|
|
102
|
-
|
|
103
|
-
const providerOptionsName = this.config.provider.includes('vertex')
|
|
104
|
-
? 'vertex'
|
|
105
|
-
: 'google';
|
|
106
|
-
let googleOptions = await parseProviderOptions({
|
|
107
|
-
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,
|
|
108
121
|
providerOptions,
|
|
109
|
-
|
|
110
|
-
}
|
|
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
|
+
}
|
|
111
145
|
|
|
112
|
-
|
|
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')) {
|
|
113
149
|
googleOptions = await parseProviderOptions({
|
|
114
150
|
provider: 'google',
|
|
115
151
|
providerOptions,
|
|
@@ -118,12 +154,14 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
118
154
|
}
|
|
119
155
|
|
|
120
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
|
+
|
|
121
159
|
if (
|
|
122
160
|
tools?.some(
|
|
123
161
|
tool =>
|
|
124
162
|
tool.type === 'provider' && tool.id === 'google.vertex_rag_store',
|
|
125
163
|
) &&
|
|
126
|
-
!
|
|
164
|
+
!isVertexProvider
|
|
127
165
|
) {
|
|
128
166
|
warnings.push({
|
|
129
167
|
type: 'other',
|
|
@@ -134,12 +172,65 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
134
172
|
});
|
|
135
173
|
}
|
|
136
174
|
|
|
137
|
-
|
|
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
|
+
}
|
|
138
184
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
+
});
|
|
143
234
|
|
|
144
235
|
const {
|
|
145
236
|
tools: googleTools,
|
|
@@ -149,8 +240,42 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
149
240
|
tools,
|
|
150
241
|
toolChoice,
|
|
151
242
|
modelId: this.modelId,
|
|
243
|
+
isVertexProvider,
|
|
152
244
|
});
|
|
153
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
|
+
|
|
154
279
|
return {
|
|
155
280
|
args: {
|
|
156
281
|
generationConfig: {
|
|
@@ -182,7 +307,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
182
307
|
|
|
183
308
|
// provider options:
|
|
184
309
|
responseModalities: googleOptions?.responseModalities,
|
|
185
|
-
thinkingConfig
|
|
310
|
+
thinkingConfig,
|
|
186
311
|
...(googleOptions?.mediaResolution && {
|
|
187
312
|
mediaResolution: googleOptions.mediaResolution,
|
|
188
313
|
}),
|
|
@@ -194,28 +319,31 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
194
319
|
systemInstruction: isGemmaModel ? undefined : systemInstruction,
|
|
195
320
|
safetySettings: googleOptions?.safetySettings,
|
|
196
321
|
tools: googleTools,
|
|
197
|
-
toolConfig
|
|
198
|
-
? {
|
|
199
|
-
...googleToolConfig,
|
|
200
|
-
retrievalConfig: googleOptions.retrievalConfig,
|
|
201
|
-
}
|
|
202
|
-
: googleToolConfig,
|
|
322
|
+
toolConfig,
|
|
203
323
|
cachedContent: googleOptions?.cachedContent,
|
|
204
324
|
labels: googleOptions?.labels,
|
|
325
|
+
serviceTier: bodyServiceTier,
|
|
205
326
|
},
|
|
206
327
|
warnings: [...warnings, ...toolWarnings],
|
|
207
|
-
|
|
328
|
+
providerOptionsNames,
|
|
329
|
+
extraHeaders: vertexPaygoHeaders,
|
|
208
330
|
};
|
|
209
331
|
}
|
|
210
332
|
|
|
211
333
|
async doGenerate(
|
|
212
|
-
options:
|
|
213
|
-
): Promise<
|
|
214
|
-
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;
|
|
215
342
|
|
|
216
343
|
const mergedHeaders = combineHeaders(
|
|
217
|
-
await resolve(this.config.headers),
|
|
344
|
+
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
218
345
|
options.headers,
|
|
346
|
+
extraHeaders,
|
|
219
347
|
);
|
|
220
348
|
|
|
221
349
|
const {
|
|
@@ -235,7 +363,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
235
363
|
});
|
|
236
364
|
|
|
237
365
|
const candidate = response.candidates[0];
|
|
238
|
-
const content: Array<
|
|
366
|
+
const content: Array<LanguageModelV4Content> = [];
|
|
239
367
|
|
|
240
368
|
// map ordered parts to content:
|
|
241
369
|
const parts = candidate.content?.parts ?? [];
|
|
@@ -244,6 +372,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
244
372
|
|
|
245
373
|
// Associates a code execution result with its preceding call.
|
|
246
374
|
let lastCodeExecutionToolCallId: string | undefined;
|
|
375
|
+
// Associates a server-side tool response with its preceding call (tool combination).
|
|
376
|
+
let lastServerToolCallId: string | undefined;
|
|
247
377
|
|
|
248
378
|
// Build content array from all parts
|
|
249
379
|
for (const part of parts) {
|
|
@@ -273,11 +403,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
273
403
|
lastCodeExecutionToolCallId = undefined;
|
|
274
404
|
} else if ('text' in part && part.text != null) {
|
|
275
405
|
const thoughtSignatureMetadata = part.thoughtSignature
|
|
276
|
-
? {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
},
|
|
280
|
-
}
|
|
406
|
+
? wrapProviderMetadata({
|
|
407
|
+
thoughtSignature: part.thoughtSignature,
|
|
408
|
+
})
|
|
281
409
|
: undefined;
|
|
282
410
|
|
|
283
411
|
if (part.text.length === 0) {
|
|
@@ -292,39 +420,74 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
292
420
|
providerMetadata: thoughtSignatureMetadata,
|
|
293
421
|
});
|
|
294
422
|
}
|
|
295
|
-
} else if ('functionCall' in part) {
|
|
423
|
+
} else if ('functionCall' in part && part.functionCall.name != null) {
|
|
296
424
|
content.push({
|
|
297
425
|
type: 'tool-call' as const,
|
|
298
|
-
toolCallId: this.config.generateId(),
|
|
426
|
+
toolCallId: part.functionCall.id ?? this.config.generateId(),
|
|
299
427
|
toolName: part.functionCall.name,
|
|
300
|
-
input: JSON.stringify(part.functionCall.args),
|
|
428
|
+
input: JSON.stringify(part.functionCall.args ?? {}),
|
|
301
429
|
providerMetadata: part.thoughtSignature
|
|
302
|
-
? {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
},
|
|
306
|
-
}
|
|
430
|
+
? wrapProviderMetadata({
|
|
431
|
+
thoughtSignature: part.thoughtSignature,
|
|
432
|
+
})
|
|
307
433
|
: undefined,
|
|
308
434
|
});
|
|
309
435
|
} else if ('inlineData' in part) {
|
|
310
436
|
const hasThought = part.thought === true;
|
|
311
437
|
const hasThoughtSignature = !!part.thoughtSignature;
|
|
312
438
|
content.push({
|
|
313
|
-
type: 'file'
|
|
314
|
-
data: part.inlineData.data,
|
|
439
|
+
type: hasThought ? 'reasoning-file' : 'file',
|
|
440
|
+
data: { type: 'data', data: part.inlineData.data },
|
|
315
441
|
mediaType: part.inlineData.mimeType,
|
|
316
|
-
providerMetadata:
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
...(hasThoughtSignature
|
|
322
|
-
? { thoughtSignature: part.thoughtSignature }
|
|
323
|
-
: {}),
|
|
324
|
-
},
|
|
325
|
-
}
|
|
326
|
-
: undefined,
|
|
442
|
+
providerMetadata: hasThoughtSignature
|
|
443
|
+
? wrapProviderMetadata({
|
|
444
|
+
thoughtSignature: part.thoughtSignature,
|
|
445
|
+
})
|
|
446
|
+
: undefined,
|
|
327
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;
|
|
328
491
|
}
|
|
329
492
|
}
|
|
330
493
|
|
|
@@ -340,7 +503,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
340
503
|
return {
|
|
341
504
|
content,
|
|
342
505
|
finishReason: {
|
|
343
|
-
unified:
|
|
506
|
+
unified: mapGoogleFinishReason({
|
|
344
507
|
finishReason: candidate.finishReason,
|
|
345
508
|
// Only count client-executed tool calls for finish reason determination.
|
|
346
509
|
hasToolCalls: content.some(
|
|
@@ -349,18 +512,17 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
349
512
|
}),
|
|
350
513
|
raw: candidate.finishReason ?? undefined,
|
|
351
514
|
},
|
|
352
|
-
usage:
|
|
515
|
+
usage: convertGoogleUsage(usageMetadata),
|
|
353
516
|
warnings,
|
|
354
|
-
providerMetadata: {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
},
|
|
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),
|
|
364
526
|
request: { body: args },
|
|
365
527
|
response: {
|
|
366
528
|
// TODO timestamp, model id, id
|
|
@@ -371,13 +533,19 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
371
533
|
}
|
|
372
534
|
|
|
373
535
|
async doStream(
|
|
374
|
-
options:
|
|
375
|
-
): Promise<
|
|
376
|
-
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;
|
|
377
544
|
|
|
378
545
|
const headers = combineHeaders(
|
|
379
|
-
await resolve(this.config.headers),
|
|
546
|
+
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
380
547
|
options.headers,
|
|
548
|
+
extraHeaders,
|
|
381
549
|
);
|
|
382
550
|
|
|
383
551
|
const { responseHeaders, value: response } = await postJsonToApi({
|
|
@@ -392,12 +560,12 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
392
560
|
fetch: this.config.fetch,
|
|
393
561
|
});
|
|
394
562
|
|
|
395
|
-
let finishReason:
|
|
563
|
+
let finishReason: LanguageModelV4FinishReason = {
|
|
396
564
|
unified: 'other',
|
|
397
565
|
raw: undefined,
|
|
398
566
|
};
|
|
399
|
-
let usage:
|
|
400
|
-
let providerMetadata:
|
|
567
|
+
let usage: GoogleUsageMetadata | undefined = undefined;
|
|
568
|
+
let providerMetadata: SharedV4ProviderMetadata | undefined = undefined;
|
|
401
569
|
let lastGroundingMetadata: GroundingMetadataSchema | null = null;
|
|
402
570
|
let lastUrlContextMetadata: UrlContextMetadataSchema | null = null;
|
|
403
571
|
|
|
@@ -413,12 +581,57 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
413
581
|
const emittedSourceUrls = new Set<string>();
|
|
414
582
|
// Associates a code execution result with its preceding call.
|
|
415
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
|
+
};
|
|
416
629
|
|
|
417
630
|
return {
|
|
418
631
|
stream: response.pipeThrough(
|
|
419
632
|
new TransformStream<
|
|
420
633
|
ParseResult<ChunkSchema>,
|
|
421
|
-
|
|
634
|
+
LanguageModelV4StreamPart
|
|
422
635
|
>({
|
|
423
636
|
start(controller) {
|
|
424
637
|
controller.enqueue({ type: 'stream-start', warnings });
|
|
@@ -512,11 +725,9 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
512
725
|
}
|
|
513
726
|
} else if ('text' in part && part.text != null) {
|
|
514
727
|
const thoughtSignatureMetadata = part.thoughtSignature
|
|
515
|
-
? {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
},
|
|
519
|
-
}
|
|
728
|
+
? wrapProviderMetadata({
|
|
729
|
+
thoughtSignature: part.thoughtSignature,
|
|
730
|
+
})
|
|
520
731
|
: undefined;
|
|
521
732
|
|
|
522
733
|
if (part.text.length === 0) {
|
|
@@ -602,60 +813,222 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
602
813
|
|
|
603
814
|
const hasThought = part.thought === true;
|
|
604
815
|
const hasThoughtSignature = !!part.thoughtSignature;
|
|
605
|
-
const fileMeta =
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
...(hasThoughtSignature
|
|
611
|
-
? { thoughtSignature: part.thoughtSignature }
|
|
612
|
-
: {}),
|
|
613
|
-
},
|
|
614
|
-
}
|
|
615
|
-
: undefined;
|
|
816
|
+
const fileMeta = hasThoughtSignature
|
|
817
|
+
? wrapProviderMetadata({
|
|
818
|
+
thoughtSignature: part.thoughtSignature,
|
|
819
|
+
})
|
|
820
|
+
: undefined;
|
|
616
821
|
controller.enqueue({
|
|
617
|
-
type: 'file',
|
|
822
|
+
type: hasThought ? 'reasoning-file' : 'file',
|
|
618
823
|
mediaType: part.inlineData.mimeType,
|
|
619
|
-
data: part.inlineData.data,
|
|
824
|
+
data: { type: 'data', data: part.inlineData.data },
|
|
620
825
|
providerMetadata: fileMeta,
|
|
621
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;
|
|
622
868
|
}
|
|
623
869
|
}
|
|
624
870
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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 ?? {});
|
|
630
979
|
|
|
631
|
-
if (toolCallDeltas != null) {
|
|
632
|
-
for (const toolCall of toolCallDeltas) {
|
|
633
980
|
controller.enqueue({
|
|
634
981
|
type: 'tool-input-start',
|
|
635
|
-
id:
|
|
636
|
-
toolName
|
|
637
|
-
providerMetadata:
|
|
982
|
+
id: toolCallId,
|
|
983
|
+
toolName,
|
|
984
|
+
providerMetadata: providerMeta,
|
|
638
985
|
});
|
|
639
986
|
|
|
640
987
|
controller.enqueue({
|
|
641
988
|
type: 'tool-input-delta',
|
|
642
|
-
id:
|
|
643
|
-
delta:
|
|
644
|
-
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,
|
|
645
1018
|
});
|
|
646
1019
|
|
|
647
1020
|
controller.enqueue({
|
|
648
1021
|
type: 'tool-input-end',
|
|
649
|
-
id:
|
|
650
|
-
providerMetadata:
|
|
1022
|
+
id: toolCallId,
|
|
1023
|
+
providerMetadata: providerMeta,
|
|
651
1024
|
});
|
|
652
1025
|
|
|
653
1026
|
controller.enqueue({
|
|
654
1027
|
type: 'tool-call',
|
|
655
|
-
toolCallId
|
|
656
|
-
toolName
|
|
657
|
-
input:
|
|
658
|
-
providerMetadata:
|
|
1028
|
+
toolCallId,
|
|
1029
|
+
toolName,
|
|
1030
|
+
input: '{}',
|
|
1031
|
+
providerMetadata: providerMeta,
|
|
659
1032
|
});
|
|
660
1033
|
|
|
661
1034
|
hasToolCalls = true;
|
|
@@ -665,23 +1038,22 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
665
1038
|
|
|
666
1039
|
if (candidate.finishReason != null) {
|
|
667
1040
|
finishReason = {
|
|
668
|
-
unified:
|
|
1041
|
+
unified: mapGoogleFinishReason({
|
|
669
1042
|
finishReason: candidate.finishReason,
|
|
670
1043
|
hasToolCalls,
|
|
671
1044
|
}),
|
|
672
1045
|
raw: candidate.finishReason,
|
|
673
1046
|
};
|
|
674
1047
|
|
|
675
|
-
providerMetadata = {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
};
|
|
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);
|
|
685
1057
|
}
|
|
686
1058
|
},
|
|
687
1059
|
|
|
@@ -702,7 +1074,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
702
1074
|
controller.enqueue({
|
|
703
1075
|
type: 'finish',
|
|
704
1076
|
finishReason,
|
|
705
|
-
usage:
|
|
1077
|
+
usage: convertGoogleUsage(usage),
|
|
706
1078
|
providerMetadata,
|
|
707
1079
|
});
|
|
708
1080
|
},
|
|
@@ -714,39 +1086,107 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
714
1086
|
}
|
|
715
1087
|
}
|
|
716
1088
|
|
|
717
|
-
function
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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,
|
|
721
1113
|
}: {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}) {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
|
733
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
|
+
}
|
|
734
1143
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
:
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
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 };
|
|
750
1190
|
}
|
|
751
1191
|
|
|
752
1192
|
function extractSources({
|
|
@@ -755,12 +1195,12 @@ function extractSources({
|
|
|
755
1195
|
}: {
|
|
756
1196
|
groundingMetadata: GroundingMetadataSchema | undefined | null;
|
|
757
1197
|
generateId: () => string;
|
|
758
|
-
}): undefined |
|
|
1198
|
+
}): undefined | LanguageModelV4Source[] {
|
|
759
1199
|
if (!groundingMetadata?.groundingChunks) {
|
|
760
1200
|
return undefined;
|
|
761
1201
|
}
|
|
762
1202
|
|
|
763
|
-
const sources:
|
|
1203
|
+
const sources: LanguageModelV4Source[] = [];
|
|
764
1204
|
|
|
765
1205
|
for (const chunk of groundingMetadata.groundingChunks) {
|
|
766
1206
|
if (chunk.web != null) {
|
|
@@ -926,6 +1366,15 @@ export const getGroundingMetadataSchema = () =>
|
|
|
926
1366
|
.nullish(),
|
|
927
1367
|
});
|
|
928
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
|
+
|
|
929
1378
|
const getContentSchema = () =>
|
|
930
1379
|
z.object({
|
|
931
1380
|
parts: z
|
|
@@ -934,8 +1383,11 @@ const getContentSchema = () =>
|
|
|
934
1383
|
// note: order matters since text can be fully empty
|
|
935
1384
|
z.object({
|
|
936
1385
|
functionCall: z.object({
|
|
937
|
-
|
|
938
|
-
|
|
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(),
|
|
939
1391
|
}),
|
|
940
1392
|
thoughtSignature: z.string().nullish(),
|
|
941
1393
|
}),
|
|
@@ -947,6 +1399,22 @@ const getContentSchema = () =>
|
|
|
947
1399
|
thought: z.boolean().nullish(),
|
|
948
1400
|
thoughtSignature: z.string().nullish(),
|
|
949
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
|
+
}),
|
|
950
1418
|
z.object({
|
|
951
1419
|
executableCode: z
|
|
952
1420
|
.object({
|
|
@@ -980,6 +1448,15 @@ const getSafetyRatingSchema = () =>
|
|
|
980
1448
|
blocked: z.boolean().nullish(),
|
|
981
1449
|
});
|
|
982
1450
|
|
|
1451
|
+
const tokenDetailsSchema = z
|
|
1452
|
+
.array(
|
|
1453
|
+
z.object({
|
|
1454
|
+
modality: z.string(),
|
|
1455
|
+
tokenCount: z.number(),
|
|
1456
|
+
}),
|
|
1457
|
+
)
|
|
1458
|
+
.nullish();
|
|
1459
|
+
|
|
983
1460
|
const usageSchema = z.object({
|
|
984
1461
|
cachedContentTokenCount: z.number().nullish(),
|
|
985
1462
|
thoughtsTokenCount: z.number().nullish(),
|
|
@@ -988,6 +1465,10 @@ const usageSchema = z.object({
|
|
|
988
1465
|
totalTokenCount: z.number().nullish(),
|
|
989
1466
|
// https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
|
|
990
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,
|
|
991
1472
|
});
|
|
992
1473
|
|
|
993
1474
|
// https://ai.google.dev/api/generate-content#UrlRetrievalMetadata
|
|
@@ -1027,17 +1508,10 @@ const responseSchema = lazySchema(() =>
|
|
|
1027
1508
|
),
|
|
1028
1509
|
);
|
|
1029
1510
|
|
|
1030
|
-
type ContentSchema = NonNullable<
|
|
1031
|
-
InferSchema<typeof responseSchema>['candidates'][number]['content']
|
|
1032
|
-
>;
|
|
1033
1511
|
export type GroundingMetadataSchema = NonNullable<
|
|
1034
1512
|
InferSchema<typeof responseSchema>['candidates'][number]['groundingMetadata']
|
|
1035
1513
|
>;
|
|
1036
1514
|
|
|
1037
|
-
type GroundingChunkSchema = NonNullable<
|
|
1038
|
-
GroundingMetadataSchema['groundingChunks']
|
|
1039
|
-
>[number];
|
|
1040
|
-
|
|
1041
1515
|
export type UrlContextMetadataSchema = NonNullable<
|
|
1042
1516
|
InferSchema<typeof responseSchema>['candidates'][number]['urlContextMetadata']
|
|
1043
1517
|
>;
|