@ai-sdk/google 3.0.50 → 3.0.52
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 +21 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +132 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +129 -43
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.d.mts +4 -1
- package/dist/internal/index.d.ts +4 -1
- package/dist/internal/index.js +131 -45
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +128 -42
- package/dist/internal/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/convert-to-google-generative-ai-messages.ts +163 -32
- package/src/google-generative-ai-language-model.ts +25 -12
- package/src/google-generative-ai-prompt.ts +21 -2
|
@@ -2,22 +2,179 @@ import {
|
|
|
2
2
|
LanguageModelV3Prompt,
|
|
3
3
|
UnsupportedFunctionalityError,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
5
|
+
import { convertToBase64 } from '@ai-sdk/provider-utils';
|
|
5
6
|
import {
|
|
6
7
|
GoogleGenerativeAIContent,
|
|
7
8
|
GoogleGenerativeAIContentPart,
|
|
9
|
+
GoogleGenerativeAIFunctionResponsePart,
|
|
8
10
|
GoogleGenerativeAIPrompt,
|
|
9
11
|
} from './google-generative-ai-prompt';
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
const dataUrlRegex = /^data:([^;,]+);base64,(.+)$/s;
|
|
14
|
+
|
|
15
|
+
function parseBase64DataUrl(
|
|
16
|
+
value: string,
|
|
17
|
+
): { mediaType: string; data: string } | undefined {
|
|
18
|
+
const match = dataUrlRegex.exec(value);
|
|
19
|
+
if (match == null) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
mediaType: match[1],
|
|
25
|
+
data: match[2],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function convertUrlToolResultPart(
|
|
30
|
+
url: string,
|
|
31
|
+
): GoogleGenerativeAIFunctionResponsePart | undefined {
|
|
32
|
+
// Per https://ai.google.dev/api/caching#FunctionResponsePart, only inline data is supported.
|
|
33
|
+
// https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/function-calling#functionresponsepart suggests that this
|
|
34
|
+
// may be different for Vertex, but this needs to be confirmed and further tested for both APIs.
|
|
35
|
+
const parsedDataUrl = parseBase64DataUrl(url);
|
|
36
|
+
if (parsedDataUrl == null) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
inlineData: {
|
|
42
|
+
mimeType: parsedDataUrl.mediaType,
|
|
43
|
+
data: parsedDataUrl.data,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* Appends tool result content parts to the message using the functionResponse
|
|
50
|
+
* format with support for multimodal parts (e.g. inline images/files alongside
|
|
51
|
+
* text). This format is supported by Gemini 3+ models.
|
|
52
|
+
*/
|
|
53
|
+
function appendToolResultParts(
|
|
54
|
+
parts: GoogleGenerativeAIContentPart[],
|
|
55
|
+
toolName: string,
|
|
56
|
+
outputValue: Array<{
|
|
57
|
+
type: string;
|
|
58
|
+
[key: string]: unknown;
|
|
59
|
+
}>,
|
|
60
|
+
): void {
|
|
61
|
+
const functionResponseParts: GoogleGenerativeAIFunctionResponsePart[] = [];
|
|
62
|
+
const responseTextParts: string[] = [];
|
|
63
|
+
|
|
64
|
+
for (const contentPart of outputValue) {
|
|
65
|
+
switch (contentPart.type) {
|
|
66
|
+
case 'text': {
|
|
67
|
+
responseTextParts.push(contentPart.text as string);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case 'image-data':
|
|
71
|
+
case 'file-data': {
|
|
72
|
+
functionResponseParts.push({
|
|
73
|
+
inlineData: {
|
|
74
|
+
mimeType: contentPart.mediaType as string,
|
|
75
|
+
data: contentPart.data as string,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case 'image-url':
|
|
81
|
+
case 'file-url': {
|
|
82
|
+
const functionResponsePart = convertUrlToolResultPart(
|
|
83
|
+
contentPart.url as string,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (functionResponsePart != null) {
|
|
87
|
+
functionResponseParts.push(functionResponsePart);
|
|
88
|
+
} else {
|
|
89
|
+
responseTextParts.push(JSON.stringify(contentPart));
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
default: {
|
|
94
|
+
responseTextParts.push(JSON.stringify(contentPart));
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
parts.push({
|
|
101
|
+
functionResponse: {
|
|
102
|
+
name: toolName,
|
|
103
|
+
response: {
|
|
104
|
+
name: toolName,
|
|
105
|
+
content:
|
|
106
|
+
responseTextParts.length > 0
|
|
107
|
+
? responseTextParts.join('\n')
|
|
108
|
+
: 'Tool executed successfully.',
|
|
109
|
+
},
|
|
110
|
+
...(functionResponseParts.length > 0
|
|
111
|
+
? { parts: functionResponseParts }
|
|
112
|
+
: {}),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* Appends tool result content parts using a legacy format for pre-Gemini 3
|
|
119
|
+
* models that do not support multimodal parts within functionResponse. Instead,
|
|
120
|
+
* non-text content like images is sent as separate top-level inlineData parts.
|
|
121
|
+
*/
|
|
122
|
+
function appendLegacyToolResultParts(
|
|
123
|
+
parts: GoogleGenerativeAIContentPart[],
|
|
124
|
+
toolName: string,
|
|
125
|
+
outputValue: Array<{
|
|
126
|
+
type: string;
|
|
127
|
+
[key: string]: unknown;
|
|
128
|
+
}>,
|
|
129
|
+
): void {
|
|
130
|
+
for (const contentPart of outputValue) {
|
|
131
|
+
switch (contentPart.type) {
|
|
132
|
+
case 'text':
|
|
133
|
+
parts.push({
|
|
134
|
+
functionResponse: {
|
|
135
|
+
name: toolName,
|
|
136
|
+
response: {
|
|
137
|
+
name: toolName,
|
|
138
|
+
content: contentPart.text,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
break;
|
|
143
|
+
case 'image-data':
|
|
144
|
+
parts.push(
|
|
145
|
+
{
|
|
146
|
+
inlineData: {
|
|
147
|
+
mimeType: String(contentPart.mediaType),
|
|
148
|
+
data: String(contentPart.data),
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
text: 'Tool executed successfully and returned this image as a response',
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
break;
|
|
156
|
+
default:
|
|
157
|
+
parts.push({ text: JSON.stringify(contentPart) });
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
11
162
|
|
|
12
163
|
export function convertToGoogleGenerativeAIMessages(
|
|
13
164
|
prompt: LanguageModelV3Prompt,
|
|
14
|
-
options?: {
|
|
165
|
+
options?: {
|
|
166
|
+
isGemmaModel?: boolean;
|
|
167
|
+
providerOptionsName?: string;
|
|
168
|
+
supportsFunctionResponseParts?: boolean;
|
|
169
|
+
},
|
|
15
170
|
): GoogleGenerativeAIPrompt {
|
|
16
171
|
const systemInstructionParts: Array<{ text: string }> = [];
|
|
17
172
|
const contents: Array<GoogleGenerativeAIContent> = [];
|
|
18
173
|
let systemMessagesAllowed = true;
|
|
19
174
|
const isGemmaModel = options?.isGemmaModel ?? false;
|
|
20
175
|
const providerOptionsName = options?.providerOptionsName ?? 'google';
|
|
176
|
+
const supportsFunctionResponseParts =
|
|
177
|
+
options?.supportsFunctionResponseParts ?? true;
|
|
21
178
|
|
|
22
179
|
for (const { role, content } of prompt) {
|
|
23
180
|
switch (role) {
|
|
@@ -160,36 +317,10 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
160
317
|
const output = part.output;
|
|
161
318
|
|
|
162
319
|
if (output.type === 'content') {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
functionResponse: {
|
|
168
|
-
name: part.toolName,
|
|
169
|
-
response: {
|
|
170
|
-
name: part.toolName,
|
|
171
|
-
content: contentPart.text,
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
break;
|
|
176
|
-
case 'image-data':
|
|
177
|
-
parts.push(
|
|
178
|
-
{
|
|
179
|
-
inlineData: {
|
|
180
|
-
mimeType: contentPart.mediaType,
|
|
181
|
-
data: contentPart.data,
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
text: 'Tool executed successfully and returned this image as a response',
|
|
186
|
-
},
|
|
187
|
-
);
|
|
188
|
-
break;
|
|
189
|
-
default:
|
|
190
|
-
parts.push({ text: JSON.stringify(contentPart) });
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
320
|
+
if (supportsFunctionResponseParts) {
|
|
321
|
+
appendToolResultParts(parts, part.toolName, output.value);
|
|
322
|
+
} else {
|
|
323
|
+
appendLegacyToolResultParts(parts, part.toolName, output.value);
|
|
193
324
|
}
|
|
194
325
|
} else {
|
|
195
326
|
parts.push({
|
|
@@ -38,7 +38,10 @@ import {
|
|
|
38
38
|
GoogleGenerativeAIModelId,
|
|
39
39
|
googleLanguageModelOptions,
|
|
40
40
|
} from './google-generative-ai-options';
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
GoogleGenerativeAIContentPart,
|
|
43
|
+
GoogleGenerativeAIProviderMetadata,
|
|
44
|
+
} from './google-generative-ai-prompt';
|
|
42
45
|
import { prepareTools } from './google-prepare-tools';
|
|
43
46
|
import { mapGoogleGenerativeAIFinishReason } from './map-google-generative-ai-finish-reason';
|
|
44
47
|
|
|
@@ -132,10 +135,15 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
const isGemmaModel = this.modelId.toLowerCase().startsWith('gemma-');
|
|
138
|
+
const supportsFunctionResponseParts = this.modelId.startsWith('gemini-3');
|
|
135
139
|
|
|
136
140
|
const { contents, systemInstruction } = convertToGoogleGenerativeAIMessages(
|
|
137
141
|
prompt,
|
|
138
|
-
{
|
|
142
|
+
{
|
|
143
|
+
isGemmaModel,
|
|
144
|
+
providerOptionsName,
|
|
145
|
+
supportsFunctionResponseParts,
|
|
146
|
+
},
|
|
139
147
|
);
|
|
140
148
|
|
|
141
149
|
const {
|
|
@@ -355,7 +363,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
355
363
|
urlContextMetadata: candidate.urlContextMetadata ?? null,
|
|
356
364
|
safetyRatings: candidate.safetyRatings ?? null,
|
|
357
365
|
usageMetadata: usageMetadata ?? null,
|
|
358
|
-
|
|
366
|
+
finishMessage: candidate.finishMessage ?? null,
|
|
367
|
+
} satisfies GoogleGenerativeAIProviderMetadata,
|
|
359
368
|
},
|
|
360
369
|
request: { body: args },
|
|
361
370
|
response: {
|
|
@@ -674,16 +683,10 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
674
683
|
groundingMetadata: lastGroundingMetadata,
|
|
675
684
|
urlContextMetadata: lastUrlContextMetadata,
|
|
676
685
|
safetyRatings: candidate.safetyRatings ?? null,
|
|
677
|
-
|
|
686
|
+
usageMetadata: usageMetadata ?? null,
|
|
687
|
+
finishMessage: candidate.finishMessage ?? null,
|
|
688
|
+
} satisfies GoogleGenerativeAIProviderMetadata,
|
|
678
689
|
};
|
|
679
|
-
if (usageMetadata != null) {
|
|
680
|
-
(
|
|
681
|
-
providerMetadata[providerOptionsName] as Record<
|
|
682
|
-
string,
|
|
683
|
-
unknown
|
|
684
|
-
>
|
|
685
|
-
).usageMetadata = usageMetadata;
|
|
686
|
-
}
|
|
687
690
|
}
|
|
688
691
|
},
|
|
689
692
|
|
|
@@ -1012,6 +1015,7 @@ const responseSchema = lazySchema(() =>
|
|
|
1012
1015
|
z.object({
|
|
1013
1016
|
content: getContentSchema().nullish().or(z.object({}).strict()),
|
|
1014
1017
|
finishReason: z.string().nullish(),
|
|
1018
|
+
finishMessage: z.string().nullish(),
|
|
1015
1019
|
safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
|
|
1016
1020
|
groundingMetadata: getGroundingMetadataSchema().nullish(),
|
|
1017
1021
|
urlContextMetadata: getUrlContextMetadataSchema().nullish(),
|
|
@@ -1047,6 +1051,14 @@ export type SafetyRatingSchema = NonNullable<
|
|
|
1047
1051
|
InferSchema<typeof responseSchema>['candidates'][number]['safetyRatings']
|
|
1048
1052
|
>[number];
|
|
1049
1053
|
|
|
1054
|
+
export type PromptFeedbackSchema = NonNullable<
|
|
1055
|
+
InferSchema<typeof responseSchema>['promptFeedback']
|
|
1056
|
+
>;
|
|
1057
|
+
|
|
1058
|
+
export type UsageMetadataSchema = NonNullable<
|
|
1059
|
+
InferSchema<typeof responseSchema>['usageMetadata']
|
|
1060
|
+
>;
|
|
1061
|
+
|
|
1050
1062
|
// limited version of the schema, focussed on what is needed for the implementation
|
|
1051
1063
|
// this approach limits breakages when the API changes and increases efficiency
|
|
1052
1064
|
const chunkSchema = lazySchema(() =>
|
|
@@ -1057,6 +1069,7 @@ const chunkSchema = lazySchema(() =>
|
|
|
1057
1069
|
z.object({
|
|
1058
1070
|
content: getContentSchema().nullish(),
|
|
1059
1071
|
finishReason: z.string().nullish(),
|
|
1072
|
+
finishMessage: z.string().nullish(),
|
|
1060
1073
|
safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
|
|
1061
1074
|
groundingMetadata: getGroundingMetadataSchema().nullish(),
|
|
1062
1075
|
urlContextMetadata: getUrlContextMetadataSchema().nullish(),
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GroundingMetadataSchema,
|
|
3
|
+
PromptFeedbackSchema,
|
|
3
4
|
UrlContextMetadataSchema,
|
|
5
|
+
type SafetyRatingSchema,
|
|
6
|
+
UsageMetadataSchema,
|
|
4
7
|
} from './google-generative-ai-language-model';
|
|
5
|
-
import { type SafetyRatingSchema } from './google-generative-ai-language-model';
|
|
6
8
|
|
|
7
9
|
export type GoogleGenerativeAIPrompt = {
|
|
8
10
|
systemInstruction?: GoogleGenerativeAISystemInstruction;
|
|
@@ -22,17 +24,34 @@ export type GoogleGenerativeAIContentPart =
|
|
|
22
24
|
| { text: string; thought?: boolean; thoughtSignature?: string }
|
|
23
25
|
| { inlineData: { mimeType: string; data: string } }
|
|
24
26
|
| { functionCall: { name: string; args: unknown }; thoughtSignature?: string }
|
|
25
|
-
| {
|
|
27
|
+
| {
|
|
28
|
+
functionResponse: {
|
|
29
|
+
name: string;
|
|
30
|
+
response: unknown;
|
|
31
|
+
parts?: Array<GoogleGenerativeAIFunctionResponsePart>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
26
34
|
| { fileData: { mimeType: string; fileUri: string } };
|
|
27
35
|
|
|
36
|
+
export type GoogleGenerativeAIFunctionResponsePart = {
|
|
37
|
+
inlineData: { mimeType: string; data: string };
|
|
38
|
+
};
|
|
39
|
+
|
|
28
40
|
export type GoogleGenerativeAIGroundingMetadata = GroundingMetadataSchema;
|
|
29
41
|
|
|
30
42
|
export type GoogleGenerativeAIUrlContextMetadata = UrlContextMetadataSchema;
|
|
31
43
|
|
|
32
44
|
export type GoogleGenerativeAISafetyRating = SafetyRatingSchema;
|
|
33
45
|
|
|
46
|
+
export type GoogleGenerativeAIPromptFeedback = PromptFeedbackSchema;
|
|
47
|
+
|
|
48
|
+
export type GoogleGenerativeAIUsageMetadata = UsageMetadataSchema;
|
|
49
|
+
|
|
34
50
|
export interface GoogleGenerativeAIProviderMetadata {
|
|
51
|
+
promptFeedback: GoogleGenerativeAIPromptFeedback | null;
|
|
35
52
|
groundingMetadata: GoogleGenerativeAIGroundingMetadata | null;
|
|
36
53
|
urlContextMetadata: GoogleGenerativeAIUrlContextMetadata | null;
|
|
37
54
|
safetyRatings: GoogleGenerativeAISafetyRating[] | null;
|
|
55
|
+
usageMetadata: GoogleGenerativeAIUsageMetadata | null;
|
|
56
|
+
finishMessage: string | null;
|
|
38
57
|
}
|