@ai-sdk/google 4.0.0-beta.8 → 4.0.0-canary.50
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 +351 -4
- package/README.md +6 -4
- package/dist/index.d.ts +97 -54
- package/dist/index.js +1644 -580
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +66 -26
- package/dist/internal/index.js +1258 -450
- package/dist/internal/index.js.map +1 -1
- package/docs/{15-google-generative-ai.mdx → 15-google.mdx} +46 -40
- package/package.json +13 -14
- package/src/{convert-google-generative-ai-usage.ts → convert-google-usage.ts} +12 -5
- package/src/convert-json-schema-to-openapi-schema.ts +1 -1
- package/src/convert-to-google-messages.ts +577 -0
- package/src/{google-generative-ai-embedding-options.ts → google-embedding-model-options.ts} +2 -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 +23 -0
- package/src/{google-generative-ai-image-model.ts → google-image-model.ts} +74 -62
- package/src/{google-generative-ai-image-settings.ts → google-image-settings.ts} +2 -2
- package/src/google-json-accumulator.ts +336 -0
- package/src/{google-generative-ai-options.ts → google-language-model-options.ts} +32 -5
- package/src/{google-generative-ai-language-model.ts → google-language-model.ts} +609 -214
- package/src/google-prepare-tools.ts +72 -12
- package/src/google-prompt.ts +82 -0
- package/src/google-provider.ts +63 -54
- package/src/google-video-model-options.ts +43 -0
- package/src/{google-generative-ai-video-model.ts → google-video-model.ts} +17 -56
- package/src/{google-generative-ai-video-settings.ts → google-video-settings.ts} +2 -1
- package/src/index.ts +28 -9
- package/src/internal/index.ts +2 -2
- package/src/{map-google-generative-ai-finish-reason.ts → map-google-finish-reason.ts} +3 -3
- 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 +10 -11
- 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
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UnsupportedFunctionalityError,
|
|
3
|
+
type LanguageModelV4Prompt,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import {
|
|
6
|
+
convertToBase64,
|
|
7
|
+
isFullMediaType,
|
|
8
|
+
resolveFullMediaType,
|
|
9
|
+
resolveProviderReference,
|
|
10
|
+
} from '@ai-sdk/provider-utils';
|
|
11
|
+
import type {
|
|
12
|
+
GoogleContent,
|
|
13
|
+
GoogleContentPart,
|
|
14
|
+
GoogleFunctionResponsePart,
|
|
15
|
+
GooglePrompt,
|
|
16
|
+
} from './google-prompt';
|
|
17
|
+
|
|
18
|
+
const dataUrlRegex = /^data:([^;,]+);base64,(.+)$/s;
|
|
19
|
+
|
|
20
|
+
function parseBase64DataUrl(
|
|
21
|
+
value: string,
|
|
22
|
+
): { mediaType: string; data: string } | undefined {
|
|
23
|
+
const match = dataUrlRegex.exec(value);
|
|
24
|
+
if (match == null) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
mediaType: match[1],
|
|
30
|
+
data: match[2],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function convertUrlToolResultPart(
|
|
35
|
+
url: string,
|
|
36
|
+
): GoogleFunctionResponsePart | undefined {
|
|
37
|
+
// Per https://ai.google.dev/api/caching#FunctionResponsePart, only inline data is supported.
|
|
38
|
+
// https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/function-calling#functionresponsepart suggests that this
|
|
39
|
+
// may be different for Vertex, but this needs to be confirmed and further tested for both APIs.
|
|
40
|
+
const parsedDataUrl = parseBase64DataUrl(url);
|
|
41
|
+
if (parsedDataUrl == null) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
inlineData: {
|
|
47
|
+
mimeType: parsedDataUrl.mediaType,
|
|
48
|
+
data: parsedDataUrl.data,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* Appends tool result content parts to the message using the functionResponse
|
|
55
|
+
* format with support for multimodal parts (e.g. inline images/files alongside
|
|
56
|
+
* text). This format is supported by Gemini 3+ models.
|
|
57
|
+
*/
|
|
58
|
+
function appendToolResultParts(
|
|
59
|
+
parts: GoogleContentPart[],
|
|
60
|
+
toolName: string,
|
|
61
|
+
outputValue: Array<{
|
|
62
|
+
type: string;
|
|
63
|
+
[key: string]: unknown;
|
|
64
|
+
}>,
|
|
65
|
+
): void {
|
|
66
|
+
const functionResponseParts: GoogleFunctionResponsePart[] = [];
|
|
67
|
+
const responseTextParts: string[] = [];
|
|
68
|
+
|
|
69
|
+
for (const contentPart of outputValue) {
|
|
70
|
+
switch (contentPart.type) {
|
|
71
|
+
case 'text': {
|
|
72
|
+
responseTextParts.push(contentPart.text as string);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case 'file-data': {
|
|
76
|
+
functionResponseParts.push({
|
|
77
|
+
inlineData: {
|
|
78
|
+
mimeType: contentPart.mediaType as string,
|
|
79
|
+
data: contentPart.data as string,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case 'file-url': {
|
|
85
|
+
const functionResponsePart = convertUrlToolResultPart(
|
|
86
|
+
contentPart.url as string,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (functionResponsePart != null) {
|
|
90
|
+
functionResponseParts.push(functionResponsePart);
|
|
91
|
+
} else {
|
|
92
|
+
responseTextParts.push(JSON.stringify(contentPart));
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
default: {
|
|
97
|
+
responseTextParts.push(JSON.stringify(contentPart));
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
parts.push({
|
|
104
|
+
functionResponse: {
|
|
105
|
+
name: toolName,
|
|
106
|
+
response: {
|
|
107
|
+
name: toolName,
|
|
108
|
+
content:
|
|
109
|
+
responseTextParts.length > 0
|
|
110
|
+
? responseTextParts.join('\n')
|
|
111
|
+
: 'Tool executed successfully.',
|
|
112
|
+
},
|
|
113
|
+
...(functionResponseParts.length > 0
|
|
114
|
+
? { parts: functionResponseParts }
|
|
115
|
+
: {}),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/*
|
|
121
|
+
* Appends tool result content parts using a legacy format for pre-Gemini 3
|
|
122
|
+
* models that do not support multimodal parts within functionResponse. Instead,
|
|
123
|
+
* non-text content like images is sent as separate top-level inlineData parts.
|
|
124
|
+
*/
|
|
125
|
+
function appendLegacyToolResultParts(
|
|
126
|
+
parts: GoogleContentPart[],
|
|
127
|
+
toolName: string,
|
|
128
|
+
outputValue: Array<{
|
|
129
|
+
type: string;
|
|
130
|
+
[key: string]: unknown;
|
|
131
|
+
}>,
|
|
132
|
+
): void {
|
|
133
|
+
for (const contentPart of outputValue) {
|
|
134
|
+
switch (contentPart.type) {
|
|
135
|
+
case 'text':
|
|
136
|
+
parts.push({
|
|
137
|
+
functionResponse: {
|
|
138
|
+
name: toolName,
|
|
139
|
+
response: {
|
|
140
|
+
name: toolName,
|
|
141
|
+
content: contentPart.text,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
case 'file-data':
|
|
147
|
+
if ((contentPart.mediaType as string).startsWith('image/')) {
|
|
148
|
+
parts.push(
|
|
149
|
+
{
|
|
150
|
+
inlineData: {
|
|
151
|
+
mimeType: contentPart.mediaType as string,
|
|
152
|
+
data: contentPart.data as string,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
text: 'Tool executed successfully and returned this image as a response',
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
parts.push({ text: JSON.stringify(contentPart) });
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
parts.push({ text: JSON.stringify(contentPart) });
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function convertToGoogleMessages(
|
|
171
|
+
prompt: LanguageModelV4Prompt,
|
|
172
|
+
options?: {
|
|
173
|
+
isGemmaModel?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Names to look up under `providerOptions` when reading per-part metadata
|
|
176
|
+
* (e.g. thought signatures). Tried in order; first match wins. For the
|
|
177
|
+
* Vertex provider this is `['googleVertex', 'vertex']` (new key first,
|
|
178
|
+
* legacy key as fallback) and for the Google provider it is `['google']`.
|
|
179
|
+
*/
|
|
180
|
+
providerOptionsNames?: readonly string[];
|
|
181
|
+
supportsFunctionResponseParts?: boolean;
|
|
182
|
+
},
|
|
183
|
+
): GooglePrompt {
|
|
184
|
+
const systemInstructionParts: Array<{ text: string }> = [];
|
|
185
|
+
const contents: Array<GoogleContent> = [];
|
|
186
|
+
let systemMessagesAllowed = true;
|
|
187
|
+
const isGemmaModel = options?.isGemmaModel ?? false;
|
|
188
|
+
const providerOptionsNames = options?.providerOptionsNames ?? ['google'];
|
|
189
|
+
const isVertexLike = !providerOptionsNames.includes('google');
|
|
190
|
+
const supportsFunctionResponseParts =
|
|
191
|
+
options?.supportsFunctionResponseParts ?? true;
|
|
192
|
+
|
|
193
|
+
const readProviderOpts = (part: {
|
|
194
|
+
providerOptions?: Record<string, unknown> | undefined;
|
|
195
|
+
}): Record<string, unknown> | undefined => {
|
|
196
|
+
for (const name of providerOptionsNames) {
|
|
197
|
+
const v = part.providerOptions?.[name];
|
|
198
|
+
if (v != null) return v as Record<string, unknown>;
|
|
199
|
+
}
|
|
200
|
+
// Cross-namespace fallback (gateway interop): Vertex providers may receive
|
|
201
|
+
// metadata under `google`, and the Google provider may receive metadata
|
|
202
|
+
// under `googleVertex`/`vertex`.
|
|
203
|
+
if (isVertexLike) {
|
|
204
|
+
return part.providerOptions?.google as
|
|
205
|
+
| Record<string, unknown>
|
|
206
|
+
| undefined;
|
|
207
|
+
}
|
|
208
|
+
return (part.providerOptions?.googleVertex ??
|
|
209
|
+
part.providerOptions?.vertex) as Record<string, unknown> | undefined;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
for (const { role, content } of prompt) {
|
|
213
|
+
switch (role) {
|
|
214
|
+
case 'system': {
|
|
215
|
+
if (!systemMessagesAllowed) {
|
|
216
|
+
throw new UnsupportedFunctionalityError({
|
|
217
|
+
functionality:
|
|
218
|
+
'system messages are only supported at the beginning of the conversation',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
systemInstructionParts.push({ text: content });
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
case 'user': {
|
|
227
|
+
systemMessagesAllowed = false;
|
|
228
|
+
|
|
229
|
+
const parts: GoogleContentPart[] = [];
|
|
230
|
+
|
|
231
|
+
for (const part of content) {
|
|
232
|
+
switch (part.type) {
|
|
233
|
+
case 'text': {
|
|
234
|
+
parts.push({ text: part.text });
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
case 'file': {
|
|
239
|
+
switch (part.data.type) {
|
|
240
|
+
case 'url': {
|
|
241
|
+
parts.push({
|
|
242
|
+
fileData: {
|
|
243
|
+
mimeType: resolveFullMediaType({ part }),
|
|
244
|
+
fileUri: part.data.url.toString(),
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case 'reference': {
|
|
250
|
+
if (isVertexLike) {
|
|
251
|
+
throw new UnsupportedFunctionalityError({
|
|
252
|
+
functionality: 'file parts with provider references',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
parts.push({
|
|
257
|
+
fileData: {
|
|
258
|
+
mimeType: resolveFullMediaType({ part }),
|
|
259
|
+
fileUri: resolveProviderReference({
|
|
260
|
+
reference: part.data.reference,
|
|
261
|
+
provider: 'google',
|
|
262
|
+
}),
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case 'text': {
|
|
268
|
+
parts.push({
|
|
269
|
+
inlineData: {
|
|
270
|
+
mimeType: isFullMediaType(part.mediaType)
|
|
271
|
+
? part.mediaType
|
|
272
|
+
: 'text/plain',
|
|
273
|
+
data: convertToBase64(
|
|
274
|
+
new TextEncoder().encode(part.data.text),
|
|
275
|
+
),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case 'data': {
|
|
281
|
+
parts.push({
|
|
282
|
+
inlineData: {
|
|
283
|
+
mimeType: resolveFullMediaType({ part }),
|
|
284
|
+
data: convertToBase64(part.data.data),
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
contents.push({ role: 'user', parts });
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
case 'assistant': {
|
|
301
|
+
systemMessagesAllowed = false;
|
|
302
|
+
|
|
303
|
+
contents.push({
|
|
304
|
+
role: 'model',
|
|
305
|
+
parts: content
|
|
306
|
+
.map(part => {
|
|
307
|
+
const providerOpts = readProviderOpts(part);
|
|
308
|
+
const thoughtSignature =
|
|
309
|
+
providerOpts?.thoughtSignature != null
|
|
310
|
+
? String(providerOpts.thoughtSignature)
|
|
311
|
+
: undefined;
|
|
312
|
+
|
|
313
|
+
switch (part.type) {
|
|
314
|
+
case 'text': {
|
|
315
|
+
return part.text.length === 0
|
|
316
|
+
? undefined
|
|
317
|
+
: {
|
|
318
|
+
text: part.text,
|
|
319
|
+
thoughtSignature,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
case 'reasoning': {
|
|
324
|
+
return part.text.length === 0
|
|
325
|
+
? undefined
|
|
326
|
+
: {
|
|
327
|
+
text: part.text,
|
|
328
|
+
thought: true,
|
|
329
|
+
thoughtSignature,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
case 'reasoning-file': {
|
|
334
|
+
switch (part.data.type) {
|
|
335
|
+
case 'url': {
|
|
336
|
+
throw new UnsupportedFunctionalityError({
|
|
337
|
+
functionality:
|
|
338
|
+
'File data URLs in assistant messages are not supported',
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
case 'data': {
|
|
342
|
+
return {
|
|
343
|
+
inlineData: {
|
|
344
|
+
mimeType: part.mediaType,
|
|
345
|
+
data: convertToBase64(part.data.data),
|
|
346
|
+
},
|
|
347
|
+
thought: true,
|
|
348
|
+
thoughtSignature,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
case 'file': {
|
|
356
|
+
switch (part.data.type) {
|
|
357
|
+
case 'url': {
|
|
358
|
+
throw new UnsupportedFunctionalityError({
|
|
359
|
+
functionality:
|
|
360
|
+
'File data URLs in assistant messages are not supported',
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
case 'reference': {
|
|
364
|
+
if (isVertexLike) {
|
|
365
|
+
throw new UnsupportedFunctionalityError({
|
|
366
|
+
functionality: 'file parts with provider references',
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
fileData: {
|
|
372
|
+
mimeType: part.mediaType,
|
|
373
|
+
fileUri: resolveProviderReference({
|
|
374
|
+
reference: part.data.reference,
|
|
375
|
+
provider: 'google',
|
|
376
|
+
}),
|
|
377
|
+
},
|
|
378
|
+
...(providerOpts?.thought === true
|
|
379
|
+
? { thought: true }
|
|
380
|
+
: {}),
|
|
381
|
+
thoughtSignature,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
case 'text': {
|
|
385
|
+
return {
|
|
386
|
+
inlineData: {
|
|
387
|
+
mimeType: isFullMediaType(part.mediaType)
|
|
388
|
+
? part.mediaType
|
|
389
|
+
: 'text/plain',
|
|
390
|
+
data: convertToBase64(
|
|
391
|
+
new TextEncoder().encode(part.data.text),
|
|
392
|
+
),
|
|
393
|
+
},
|
|
394
|
+
...(providerOpts?.thought === true
|
|
395
|
+
? { thought: true }
|
|
396
|
+
: {}),
|
|
397
|
+
thoughtSignature,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
case 'data': {
|
|
401
|
+
return {
|
|
402
|
+
inlineData: {
|
|
403
|
+
mimeType: part.mediaType,
|
|
404
|
+
data: convertToBase64(part.data.data),
|
|
405
|
+
},
|
|
406
|
+
...(providerOpts?.thought === true
|
|
407
|
+
? { thought: true }
|
|
408
|
+
: {}),
|
|
409
|
+
thoughtSignature,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
case 'tool-call': {
|
|
417
|
+
const serverToolCallId =
|
|
418
|
+
providerOpts?.serverToolCallId != null
|
|
419
|
+
? String(providerOpts.serverToolCallId)
|
|
420
|
+
: undefined;
|
|
421
|
+
const serverToolType =
|
|
422
|
+
providerOpts?.serverToolType != null
|
|
423
|
+
? String(providerOpts.serverToolType)
|
|
424
|
+
: undefined;
|
|
425
|
+
|
|
426
|
+
if (serverToolCallId && serverToolType) {
|
|
427
|
+
return {
|
|
428
|
+
toolCall: {
|
|
429
|
+
toolType: serverToolType,
|
|
430
|
+
args:
|
|
431
|
+
typeof part.input === 'string'
|
|
432
|
+
? JSON.parse(part.input)
|
|
433
|
+
: part.input,
|
|
434
|
+
id: serverToolCallId,
|
|
435
|
+
},
|
|
436
|
+
thoughtSignature,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
functionCall: {
|
|
442
|
+
name: part.toolName,
|
|
443
|
+
args: part.input,
|
|
444
|
+
},
|
|
445
|
+
thoughtSignature,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
case 'tool-result': {
|
|
450
|
+
const serverToolCallId =
|
|
451
|
+
providerOpts?.serverToolCallId != null
|
|
452
|
+
? String(providerOpts.serverToolCallId)
|
|
453
|
+
: undefined;
|
|
454
|
+
const serverToolType =
|
|
455
|
+
providerOpts?.serverToolType != null
|
|
456
|
+
? String(providerOpts.serverToolType)
|
|
457
|
+
: undefined;
|
|
458
|
+
|
|
459
|
+
if (serverToolCallId && serverToolType) {
|
|
460
|
+
return {
|
|
461
|
+
toolResponse: {
|
|
462
|
+
toolType: serverToolType,
|
|
463
|
+
response:
|
|
464
|
+
part.output.type === 'json' ? part.output.value : {},
|
|
465
|
+
id: serverToolCallId,
|
|
466
|
+
},
|
|
467
|
+
thoughtSignature,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return undefined;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
.filter(part => part !== undefined),
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
case 'tool': {
|
|
482
|
+
systemMessagesAllowed = false;
|
|
483
|
+
|
|
484
|
+
const parts: GoogleContentPart[] = [];
|
|
485
|
+
|
|
486
|
+
for (const part of content) {
|
|
487
|
+
if (part.type === 'tool-approval-response') {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const partProviderOpts = readProviderOpts(part);
|
|
492
|
+
const serverToolCallId =
|
|
493
|
+
partProviderOpts?.serverToolCallId != null
|
|
494
|
+
? String(partProviderOpts.serverToolCallId)
|
|
495
|
+
: undefined;
|
|
496
|
+
const serverToolType =
|
|
497
|
+
partProviderOpts?.serverToolType != null
|
|
498
|
+
? String(partProviderOpts.serverToolType)
|
|
499
|
+
: undefined;
|
|
500
|
+
|
|
501
|
+
if (serverToolCallId && serverToolType) {
|
|
502
|
+
const serverThoughtSignature =
|
|
503
|
+
partProviderOpts?.thoughtSignature != null
|
|
504
|
+
? String(partProviderOpts.thoughtSignature)
|
|
505
|
+
: undefined;
|
|
506
|
+
|
|
507
|
+
if (contents.length > 0) {
|
|
508
|
+
const lastContent = contents[contents.length - 1];
|
|
509
|
+
if (lastContent.role === 'model') {
|
|
510
|
+
lastContent.parts.push({
|
|
511
|
+
toolResponse: {
|
|
512
|
+
toolType: serverToolType,
|
|
513
|
+
response:
|
|
514
|
+
part.output.type === 'json' ? part.output.value : {},
|
|
515
|
+
id: serverToolCallId,
|
|
516
|
+
},
|
|
517
|
+
thoughtSignature: serverThoughtSignature,
|
|
518
|
+
});
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const output = part.output;
|
|
525
|
+
|
|
526
|
+
if (output.type === 'content') {
|
|
527
|
+
if (supportsFunctionResponseParts) {
|
|
528
|
+
appendToolResultParts(parts, part.toolName, output.value);
|
|
529
|
+
} else {
|
|
530
|
+
appendLegacyToolResultParts(parts, part.toolName, output.value);
|
|
531
|
+
}
|
|
532
|
+
} else {
|
|
533
|
+
parts.push({
|
|
534
|
+
functionResponse: {
|
|
535
|
+
name: part.toolName,
|
|
536
|
+
response: {
|
|
537
|
+
name: part.toolName,
|
|
538
|
+
content:
|
|
539
|
+
output.type === 'execution-denied'
|
|
540
|
+
? (output.reason ?? 'Tool call execution denied.')
|
|
541
|
+
: output.value,
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
contents.push({
|
|
549
|
+
role: 'user',
|
|
550
|
+
parts,
|
|
551
|
+
});
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (
|
|
558
|
+
isGemmaModel &&
|
|
559
|
+
systemInstructionParts.length > 0 &&
|
|
560
|
+
contents.length > 0 &&
|
|
561
|
+
contents[0].role === 'user'
|
|
562
|
+
) {
|
|
563
|
+
const systemText = systemInstructionParts
|
|
564
|
+
.map(part => part.text)
|
|
565
|
+
.join('\n\n');
|
|
566
|
+
|
|
567
|
+
contents[0].parts.unshift({ text: systemText + '\n\n' });
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return {
|
|
571
|
+
systemInstruction:
|
|
572
|
+
systemInstructionParts.length > 0 && !isGemmaModel
|
|
573
|
+
? { parts: systemInstructionParts }
|
|
574
|
+
: undefined,
|
|
575
|
+
contents,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type InferSchema,
|
|
3
2
|
lazySchema,
|
|
4
3
|
zodSchema,
|
|
4
|
+
type InferSchema,
|
|
5
5
|
} from '@ai-sdk/provider-utils';
|
|
6
6
|
import { z } from 'zod/v4';
|
|
7
7
|
|
|
8
|
-
export type
|
|
8
|
+
export type GoogleEmbeddingModelId =
|
|
9
9
|
| 'gemini-embedding-001'
|
|
10
10
|
| 'gemini-embedding-2-preview'
|
|
11
11
|
| (string & {});
|
|
@@ -1,46 +1,59 @@
|
|
|
1
1
|
import {
|
|
2
|
-
EmbeddingModelV3,
|
|
3
2
|
TooManyEmbeddingValuesForCallError,
|
|
3
|
+
type EmbeddingModelV4,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
5
5
|
import {
|
|
6
6
|
combineHeaders,
|
|
7
7
|
createJsonResponseHandler,
|
|
8
|
-
FetchFunction,
|
|
9
8
|
lazySchema,
|
|
10
9
|
parseProviderOptions,
|
|
11
10
|
postJsonToApi,
|
|
12
11
|
resolve,
|
|
12
|
+
serializeModelOptions,
|
|
13
|
+
WORKFLOW_SERIALIZE,
|
|
14
|
+
WORKFLOW_DESERIALIZE,
|
|
13
15
|
zodSchema,
|
|
16
|
+
type FetchFunction,
|
|
14
17
|
} from '@ai-sdk/provider-utils';
|
|
15
18
|
import { z } from 'zod/v4';
|
|
16
19
|
import { googleFailedResponseHandler } from './google-error';
|
|
17
20
|
import {
|
|
18
|
-
GoogleGenerativeAIEmbeddingModelId,
|
|
19
21
|
googleEmbeddingModelOptions,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
type
|
|
22
|
+
type GoogleEmbeddingModelId,
|
|
23
|
+
} from './google-embedding-model-options';
|
|
24
|
+
type GoogleEmbeddingConfig = {
|
|
23
25
|
provider: string;
|
|
24
26
|
baseURL: string;
|
|
25
|
-
headers
|
|
27
|
+
headers?: () => Record<string, string | undefined>;
|
|
26
28
|
fetch?: FetchFunction;
|
|
27
29
|
};
|
|
28
30
|
|
|
29
|
-
export class
|
|
30
|
-
readonly specificationVersion = '
|
|
31
|
-
readonly modelId:
|
|
31
|
+
export class GoogleEmbeddingModel implements EmbeddingModelV4 {
|
|
32
|
+
readonly specificationVersion = 'v4';
|
|
33
|
+
readonly modelId: GoogleEmbeddingModelId;
|
|
32
34
|
readonly maxEmbeddingsPerCall = 2048;
|
|
33
35
|
readonly supportsParallelCalls = true;
|
|
34
36
|
|
|
35
|
-
private readonly config:
|
|
37
|
+
private readonly config: GoogleEmbeddingConfig;
|
|
38
|
+
|
|
39
|
+
static [WORKFLOW_SERIALIZE](model: GoogleEmbeddingModel) {
|
|
40
|
+
return serializeModelOptions({
|
|
41
|
+
modelId: model.modelId,
|
|
42
|
+
config: model.config,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
47
|
+
modelId: string;
|
|
48
|
+
config: GoogleEmbeddingConfig;
|
|
49
|
+
}) {
|
|
50
|
+
return new GoogleEmbeddingModel(options.modelId, options.config);
|
|
51
|
+
}
|
|
36
52
|
|
|
37
53
|
get provider(): string {
|
|
38
54
|
return this.config.provider;
|
|
39
55
|
}
|
|
40
|
-
constructor(
|
|
41
|
-
modelId: GoogleGenerativeAIEmbeddingModelId,
|
|
42
|
-
config: GoogleGenerativeAIEmbeddingConfig,
|
|
43
|
-
) {
|
|
56
|
+
constructor(modelId: GoogleEmbeddingModelId, config: GoogleEmbeddingConfig) {
|
|
44
57
|
this.modelId = modelId;
|
|
45
58
|
this.config = config;
|
|
46
59
|
}
|
|
@@ -50,8 +63,8 @@ export class GoogleGenerativeAIEmbeddingModel implements EmbeddingModelV3 {
|
|
|
50
63
|
headers,
|
|
51
64
|
abortSignal,
|
|
52
65
|
providerOptions,
|
|
53
|
-
}: Parameters<
|
|
54
|
-
Awaited<ReturnType<
|
|
66
|
+
}: Parameters<EmbeddingModelV4['doEmbed']>[0]): Promise<
|
|
67
|
+
Awaited<ReturnType<EmbeddingModelV4['doEmbed']>>
|
|
55
68
|
> {
|
|
56
69
|
// Parse provider options
|
|
57
70
|
const googleOptions = await parseProviderOptions({
|
|
@@ -70,7 +83,7 @@ export class GoogleGenerativeAIEmbeddingModel implements EmbeddingModelV3 {
|
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
const mergedHeaders = combineHeaders(
|
|
73
|
-
await resolve(this.config.headers),
|
|
86
|
+
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
74
87
|
headers,
|
|
75
88
|
);
|
|
76
89
|
|