@intrvls/langchain-google-genai 3.0.0-alpha.0
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 +346 -0
- package/LICENSE +21 -0
- package/README.md +201 -0
- package/dist/index.cjs +1880 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +336 -0
- package/dist/index.d.mts +336 -0
- package/dist/index.mjs +1878 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +68 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1880 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _google_genai = require("@google/genai");
|
|
3
|
+
let _langchain_core_utils_env = require("@langchain/core/utils/env");
|
|
4
|
+
let _langchain_core_language_models_chat_models = require("@langchain/core/language_models/chat_models");
|
|
5
|
+
let _langchain_core_utils_types = require("@langchain/core/utils/types");
|
|
6
|
+
let _langchain_core_utils_json_schema = require("@langchain/core/utils/json_schema");
|
|
7
|
+
let _langchain_core_utils_standard_schema = require("@langchain/core/utils/standard_schema");
|
|
8
|
+
let _langchain_core_messages = require("@langchain/core/messages");
|
|
9
|
+
let _langchain_core_outputs = require("@langchain/core/outputs");
|
|
10
|
+
let _langchain_core_utils_function_calling = require("@langchain/core/utils/function_calling");
|
|
11
|
+
let _langchain_core_language_models_base = require("@langchain/core/language_models/base");
|
|
12
|
+
let uuid = require("uuid");
|
|
13
|
+
let _langchain_core_output_parsers = require("@langchain/core/output_parsers");
|
|
14
|
+
let _langchain_core_language_models_structured_output = require("@langchain/core/language_models/structured_output");
|
|
15
|
+
let _langchain_core_embeddings = require("@langchain/core/embeddings");
|
|
16
|
+
let _langchain_core_utils_chunk_array = require("@langchain/core/utils/chunk_array");
|
|
17
|
+
//#region src/utils/zod_to_genai_parameters.ts
|
|
18
|
+
function removeAdditionalProperties(obj) {
|
|
19
|
+
if (typeof obj === "object" && obj !== null) {
|
|
20
|
+
const newObj = { ...obj };
|
|
21
|
+
if ("additionalProperties" in newObj) delete newObj.additionalProperties;
|
|
22
|
+
if ("$schema" in newObj) delete newObj.$schema;
|
|
23
|
+
if ("strict" in newObj) delete newObj.strict;
|
|
24
|
+
for (const key in newObj) if (key in newObj) {
|
|
25
|
+
if (Array.isArray(newObj[key])) newObj[key] = newObj[key].map(removeAdditionalProperties);
|
|
26
|
+
else if (typeof newObj[key] === "object" && newObj[key] !== null) newObj[key] = removeAdditionalProperties(newObj[key]);
|
|
27
|
+
}
|
|
28
|
+
return newObj;
|
|
29
|
+
}
|
|
30
|
+
return obj;
|
|
31
|
+
}
|
|
32
|
+
function schemaToGenerativeAIParameters(schema) {
|
|
33
|
+
const { $schema, ...rest } = removeAdditionalProperties((0, _langchain_core_utils_types.isInteropZodSchema)(schema) || (0, _langchain_core_utils_standard_schema.isSerializableSchema)(schema) ? (0, _langchain_core_utils_json_schema.toJsonSchema)(schema) : schema);
|
|
34
|
+
return rest;
|
|
35
|
+
}
|
|
36
|
+
function jsonSchemaToGeminiParameters(schema) {
|
|
37
|
+
const { $schema, ...rest } = removeAdditionalProperties(schema);
|
|
38
|
+
return rest;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/utils/validate_schema.ts
|
|
42
|
+
function assertNoEmptyStringEnums(schema, toolName, path = []) {
|
|
43
|
+
if (schema == null || typeof schema !== "object") return;
|
|
44
|
+
const obj = schema;
|
|
45
|
+
if (Array.isArray(obj.enum)) {
|
|
46
|
+
if (obj.enum.some((v) => v === "")) {
|
|
47
|
+
const pathStr = path.length ? ` at path "${path.join(".")}"` : "";
|
|
48
|
+
const toolStr = toolName ? ` in tool "${toolName}"` : "";
|
|
49
|
+
throw new Error(`Invalid enum: empty string not allowed${toolStr}${pathStr}. Gemini API rejects empty strings in enums.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (obj.type === "object" && obj.properties && typeof obj.properties === "object") for (const [prop, child] of Object.entries(obj.properties)) assertNoEmptyStringEnums(child, toolName, [...path, prop]);
|
|
53
|
+
if (obj.items) assertNoEmptyStringEnums(obj.items, toolName, [...path, "[]"]);
|
|
54
|
+
for (const k of [
|
|
55
|
+
"anyOf",
|
|
56
|
+
"oneOf",
|
|
57
|
+
"allOf"
|
|
58
|
+
]) {
|
|
59
|
+
const arr = obj[k];
|
|
60
|
+
if (Array.isArray(arr)) arr.forEach((child, i) => assertNoEmptyStringEnums(child, toolName, [...path, `${k}[${i}]`]));
|
|
61
|
+
}
|
|
62
|
+
if (obj.additionalProperties && typeof obj.additionalProperties === "object") assertNoEmptyStringEnums(obj.additionalProperties, toolName, [...path, "additionalProperties"]);
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/utils/common.ts
|
|
66
|
+
const _FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY = "__gemini_function_call_thought_signatures__";
|
|
67
|
+
const DUMMY_SIGNATURE = "ErYCCrMCAdHtim9kOoOkrPiCNVsmlpMIKd7ZMxgiFbVQOkgp7nlLcDMzVsZwIzvuT7nQROivoXA72ccC2lSDvR0Gh7dkWaGuj7ctv6t7ZceHnecx0QYa+ix8tYpRfjhyWozQ49lWiws6+YGjCt10KRTyWsZ2h6O7iHTYJwKIRwGUHRKy/qK/6kFxJm5ML00gLq4D8s5Z6DBpp2ZlR+uF4G8jJgeWQgyHWVdx2wGYElaceVAc66tZdPQRdOHpWtgYSI1YdaXgVI8KHY3/EfNc2YqqMIulvkDBAnuMhkAjV9xmBa54Tq+ih3Im4+r3DzqhGqYdsSkhS0kZMwte4Hjs65dZzCw9lANxIqYi1DJ639WNPYihp/DCJCos7o+/EeSPJaio5sgWDyUnMGkY1atsJZ+m7pj7DD5tvQ==";
|
|
68
|
+
const iife = (fn) => fn();
|
|
69
|
+
function getMessageAuthor(message) {
|
|
70
|
+
if (_langchain_core_messages.ChatMessage.isInstance(message)) return message.role;
|
|
71
|
+
return message.type;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Maps a message type to a Google Generative AI chat author.
|
|
75
|
+
* @param message The message to map.
|
|
76
|
+
* @param model The model to use for mapping.
|
|
77
|
+
* @returns The message type mapped to a Google Generative AI chat author.
|
|
78
|
+
*/
|
|
79
|
+
function convertAuthorToRole(author) {
|
|
80
|
+
switch (author) {
|
|
81
|
+
/**
|
|
82
|
+
* Note: Gemini currently is not supporting system messages
|
|
83
|
+
* we will convert them to human messages and merge with following
|
|
84
|
+
* */
|
|
85
|
+
case "supervisor":
|
|
86
|
+
case "ai":
|
|
87
|
+
case "model": return "model";
|
|
88
|
+
case "system": return "system";
|
|
89
|
+
case "human": return "user";
|
|
90
|
+
case "tool":
|
|
91
|
+
case "function": return "function";
|
|
92
|
+
default: throw new Error(`Unknown / unsupported author: ${author}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function messageContentMedia(content) {
|
|
96
|
+
if ("mimeType" in content && "data" in content) return { inlineData: {
|
|
97
|
+
mimeType: content.mimeType,
|
|
98
|
+
data: content.data
|
|
99
|
+
} };
|
|
100
|
+
if ("mimeType" in content && "fileUri" in content) return { fileData: {
|
|
101
|
+
mimeType: content.mimeType,
|
|
102
|
+
fileUri: content.fileUri
|
|
103
|
+
} };
|
|
104
|
+
throw new Error("Invalid media content");
|
|
105
|
+
}
|
|
106
|
+
function inferToolNameFromPreviousMessages(message, previousMessages) {
|
|
107
|
+
return previousMessages.map((msg) => {
|
|
108
|
+
if ((0, _langchain_core_messages.isAIMessage)(msg)) return msg.tool_calls ?? [];
|
|
109
|
+
return [];
|
|
110
|
+
}).flat().find((toolCall) => {
|
|
111
|
+
return toolCall.id === message.tool_call_id;
|
|
112
|
+
})?.name;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Converts a ContentBlock.Multimodal block (image, video, audio, file)
|
|
116
|
+
* to the appropriate Google Generative AI Part format.
|
|
117
|
+
*
|
|
118
|
+
* Handles three data record variants:
|
|
119
|
+
* - DataRecordBase64: has `data` property → InlineDataPart
|
|
120
|
+
* - DataRecordUrl: has `url` property → FileDataPart (or InlineDataPart for data: URLs)
|
|
121
|
+
* - DataRecordFileId: has `fileId` property → not directly supported, throws
|
|
122
|
+
*/
|
|
123
|
+
function _multimodalContentBlockToPart(block, defaultMimeType) {
|
|
124
|
+
if ("data" in block && block.data !== void 0) {
|
|
125
|
+
const data = block.data instanceof Uint8Array ? btoa(String.fromCharCode(...block.data)) : block.data;
|
|
126
|
+
return { inlineData: {
|
|
127
|
+
mimeType: block.mimeType || defaultMimeType,
|
|
128
|
+
data
|
|
129
|
+
} };
|
|
130
|
+
}
|
|
131
|
+
if ("url" in block && block.url !== void 0) {
|
|
132
|
+
const parsed = (0, _langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
|
|
133
|
+
if (parsed) return { inlineData: {
|
|
134
|
+
mimeType: parsed.mime_type,
|
|
135
|
+
data: parsed.data
|
|
136
|
+
} };
|
|
137
|
+
return { fileData: {
|
|
138
|
+
mimeType: block.mimeType || defaultMimeType,
|
|
139
|
+
fileUri: block.url
|
|
140
|
+
} };
|
|
141
|
+
}
|
|
142
|
+
if ("fileId" in block && block.fileId !== void 0) throw new Error("ContentBlock.Multimodal fileId is not supported by Google Generative AI. Use a URL or base64 data instead.");
|
|
143
|
+
throw new Error(`Invalid multimodal content block: must have "data", "url", or "fileId" property. Received: ${JSON.stringify(block)}`);
|
|
144
|
+
}
|
|
145
|
+
function _getStandardContentBlockConverter(isMultimodalModel) {
|
|
146
|
+
return {
|
|
147
|
+
providerName: "Google Gemini",
|
|
148
|
+
fromStandardTextBlock(block) {
|
|
149
|
+
return { text: block.text };
|
|
150
|
+
},
|
|
151
|
+
fromStandardImageBlock(block) {
|
|
152
|
+
if (!isMultimodalModel) throw new Error("This model does not support images");
|
|
153
|
+
if (block.source_type === "url") {
|
|
154
|
+
const data = (0, _langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
|
|
155
|
+
if (data) return { inlineData: {
|
|
156
|
+
mimeType: data.mime_type,
|
|
157
|
+
data: data.data
|
|
158
|
+
} };
|
|
159
|
+
else return { fileData: {
|
|
160
|
+
mimeType: block.mime_type ?? "",
|
|
161
|
+
fileUri: block.url
|
|
162
|
+
} };
|
|
163
|
+
}
|
|
164
|
+
if (block.source_type === "base64") return { inlineData: {
|
|
165
|
+
mimeType: block.mime_type ?? "",
|
|
166
|
+
data: block.data
|
|
167
|
+
} };
|
|
168
|
+
throw new Error(`Unsupported source type: ${block.source_type}`);
|
|
169
|
+
},
|
|
170
|
+
fromStandardAudioBlock(block) {
|
|
171
|
+
if (!isMultimodalModel) throw new Error("This model does not support audio");
|
|
172
|
+
if (block.source_type === "url") {
|
|
173
|
+
const data = (0, _langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
|
|
174
|
+
if (data) return { inlineData: {
|
|
175
|
+
mimeType: data.mime_type,
|
|
176
|
+
data: data.data
|
|
177
|
+
} };
|
|
178
|
+
else return { fileData: {
|
|
179
|
+
mimeType: block.mime_type ?? "",
|
|
180
|
+
fileUri: block.url
|
|
181
|
+
} };
|
|
182
|
+
}
|
|
183
|
+
if (block.source_type === "base64") return { inlineData: {
|
|
184
|
+
mimeType: block.mime_type ?? "",
|
|
185
|
+
data: block.data
|
|
186
|
+
} };
|
|
187
|
+
throw new Error(`Unsupported source type: ${block.source_type}`);
|
|
188
|
+
},
|
|
189
|
+
fromStandardFileBlock(block) {
|
|
190
|
+
if (!isMultimodalModel) throw new Error("This model does not support files");
|
|
191
|
+
if (block.source_type === "text") return { text: block.text };
|
|
192
|
+
if (block.source_type === "url") {
|
|
193
|
+
const data = (0, _langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
|
|
194
|
+
if (data) return { inlineData: {
|
|
195
|
+
mimeType: data.mime_type,
|
|
196
|
+
data: data.data
|
|
197
|
+
} };
|
|
198
|
+
else return { fileData: {
|
|
199
|
+
mimeType: block.mime_type ?? "",
|
|
200
|
+
fileUri: block.url
|
|
201
|
+
} };
|
|
202
|
+
}
|
|
203
|
+
if (block.source_type === "base64") return { inlineData: {
|
|
204
|
+
mimeType: block.mime_type ?? "",
|
|
205
|
+
data: block.data
|
|
206
|
+
} };
|
|
207
|
+
throw new Error(`Unsupported source type: ${block.source_type}`);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function _convertLangChainContentToPart(content, isMultimodalModel) {
|
|
212
|
+
if ((0, _langchain_core_messages.isDataContentBlock)(content)) return (0, _langchain_core_messages.convertToProviderContentBlock)(content, _getStandardContentBlockConverter(isMultimodalModel));
|
|
213
|
+
if (content.type === "text") return { text: content.text };
|
|
214
|
+
else if (content.type === "executableCode") return { executableCode: content.executableCode };
|
|
215
|
+
else if (content.type === "codeExecutionResult") return { codeExecutionResult: content.codeExecutionResult };
|
|
216
|
+
else if (content.type === "image_url") {
|
|
217
|
+
if (!isMultimodalModel) throw new Error(`This model does not support images`);
|
|
218
|
+
let source;
|
|
219
|
+
if (typeof content.image_url === "string") source = content.image_url;
|
|
220
|
+
else if (typeof content.image_url === "object" && "url" in content.image_url) source = content.image_url.url;
|
|
221
|
+
else throw new Error("Please provide image as base64 encoded data URL");
|
|
222
|
+
const [dm, data] = source.split(",");
|
|
223
|
+
if (!dm.startsWith("data:")) throw new Error("Please provide image as base64 encoded data URL");
|
|
224
|
+
const [mimeType, encoding] = dm.replace(/^data:/, "").split(";");
|
|
225
|
+
if (encoding !== "base64") throw new Error("Please provide image as base64 encoded data URL");
|
|
226
|
+
return { inlineData: {
|
|
227
|
+
data,
|
|
228
|
+
mimeType
|
|
229
|
+
} };
|
|
230
|
+
} else if (content.type === "media") return messageContentMedia(content);
|
|
231
|
+
else if (content.type === "image") return _multimodalContentBlockToPart(content, "image/png");
|
|
232
|
+
else if (content.type === "video") return _multimodalContentBlockToPart(content, "video/mp4");
|
|
233
|
+
else if (content.type === "audio") return _multimodalContentBlockToPart(content, "audio/mpeg");
|
|
234
|
+
else if (content.type === "file") return _multimodalContentBlockToPart(content, "application/octet-stream");
|
|
235
|
+
else if (content.type === "text-plain") {
|
|
236
|
+
if ("text" in content && typeof content.text === "string") return { text: content.text };
|
|
237
|
+
return _multimodalContentBlockToPart(content, "text/plain");
|
|
238
|
+
} else if (content.type === "tool_use") return { functionCall: {
|
|
239
|
+
name: content.name,
|
|
240
|
+
args: content.input
|
|
241
|
+
} };
|
|
242
|
+
else if (content.type === "tool_call") return { functionCall: {
|
|
243
|
+
name: content.name,
|
|
244
|
+
args: content.args
|
|
245
|
+
} };
|
|
246
|
+
else if (content.type?.includes("/") && content.type.split("/").length === 2 && "data" in content && typeof content.data === "string") return { inlineData: {
|
|
247
|
+
mimeType: content.type,
|
|
248
|
+
data: content.data
|
|
249
|
+
} };
|
|
250
|
+
else if (content.type === "thinking") {
|
|
251
|
+
const thinkingContent = content;
|
|
252
|
+
return {
|
|
253
|
+
text: thinkingContent.thinking,
|
|
254
|
+
thought: true,
|
|
255
|
+
...thinkingContent.signature ? { thoughtSignature: thinkingContent.signature } : {}
|
|
256
|
+
};
|
|
257
|
+
} else if ("functionCall" in content) return;
|
|
258
|
+
else if ("type" in content) throw new Error(`Unknown content type ${content.type}`);
|
|
259
|
+
else throw new Error(`Unknown content ${JSON.stringify(content)}`);
|
|
260
|
+
}
|
|
261
|
+
function convertMessageContentToParts(message, isMultimodalModel, previousMessages, model) {
|
|
262
|
+
if ((0, _langchain_core_messages.isToolMessage)(message)) {
|
|
263
|
+
const messageName = message.name ?? inferToolNameFromPreviousMessages(message, previousMessages);
|
|
264
|
+
if (messageName === void 0) throw new Error(`Google requires a tool name for each tool call response, and we could not infer a called tool name for ToolMessage "${message.id}" from your passed messages. Please populate a "name" field on that ToolMessage explicitly.`);
|
|
265
|
+
const result = Array.isArray(message.content) ? message.content.map((c) => _convertLangChainContentToPart(c, isMultimodalModel)).filter((p) => p !== void 0) : message.content;
|
|
266
|
+
if (message.status === "error") return [{ functionResponse: {
|
|
267
|
+
name: messageName,
|
|
268
|
+
response: { error: { details: result } }
|
|
269
|
+
} }];
|
|
270
|
+
return [{ functionResponse: {
|
|
271
|
+
name: messageName,
|
|
272
|
+
response: { result }
|
|
273
|
+
} }];
|
|
274
|
+
}
|
|
275
|
+
let functionCalls = [];
|
|
276
|
+
const messageParts = [];
|
|
277
|
+
if (typeof message.content === "string" && message.content) messageParts.push({ text: message.content });
|
|
278
|
+
if (Array.isArray(message.content)) messageParts.push(...message.content.map((c) => _convertLangChainContentToPart(c, isMultimodalModel)).filter((p) => p !== void 0));
|
|
279
|
+
const functionThoughtSignatures = message.additional_kwargs?.[_FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY];
|
|
280
|
+
if ((0, _langchain_core_messages.isAIMessage)(message) && message.tool_calls?.length) functionCalls = message.tool_calls.map((tc) => {
|
|
281
|
+
const thoughtSignature = iife(() => {
|
|
282
|
+
if (tc.id) {
|
|
283
|
+
const signature = functionThoughtSignatures?.[tc.id];
|
|
284
|
+
if (signature) return signature;
|
|
285
|
+
}
|
|
286
|
+
if (model?.includes("gemini-3")) return DUMMY_SIGNATURE;
|
|
287
|
+
return "";
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
functionCall: {
|
|
291
|
+
name: tc.name,
|
|
292
|
+
args: tc.args
|
|
293
|
+
},
|
|
294
|
+
...thoughtSignature ? { thoughtSignature } : {}
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
return [...messageParts, ...functionCalls];
|
|
298
|
+
}
|
|
299
|
+
function convertBaseMessagesToContent(messages, isMultimodalModel, convertSystemMessageToHumanContent = false, model) {
|
|
300
|
+
return messages.reduce((acc, message, index) => {
|
|
301
|
+
if (!(0, _langchain_core_messages.isBaseMessage)(message)) throw new Error("Unsupported message input");
|
|
302
|
+
const author = getMessageAuthor(message);
|
|
303
|
+
if (author === "system" && index !== 0) throw new Error("System message should be the first one");
|
|
304
|
+
const role = convertAuthorToRole(author);
|
|
305
|
+
const prevContent = acc.content[acc.content.length];
|
|
306
|
+
if (!acc.mergeWithPreviousContent && prevContent && prevContent.role === role) throw new Error("Google Generative AI requires alternate messages between authors");
|
|
307
|
+
const parts = convertMessageContentToParts(message, isMultimodalModel, messages.slice(0, index), model);
|
|
308
|
+
if (acc.mergeWithPreviousContent) {
|
|
309
|
+
const prevContent = acc.content[acc.content.length - 1];
|
|
310
|
+
if (!prevContent) throw new Error("There was a problem parsing your system message. Please try a prompt without one.");
|
|
311
|
+
prevContent.parts = [...prevContent.parts ?? [], ...parts];
|
|
312
|
+
return {
|
|
313
|
+
mergeWithPreviousContent: false,
|
|
314
|
+
content: acc.content
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
let actualRole = role;
|
|
318
|
+
if (actualRole === "function" || actualRole === "system" && !convertSystemMessageToHumanContent) actualRole = "user";
|
|
319
|
+
const content = {
|
|
320
|
+
role: actualRole,
|
|
321
|
+
parts
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
mergeWithPreviousContent: author === "system" && !convertSystemMessageToHumanContent,
|
|
325
|
+
content: [...acc.content, content]
|
|
326
|
+
};
|
|
327
|
+
}, {
|
|
328
|
+
content: [],
|
|
329
|
+
mergeWithPreviousContent: false
|
|
330
|
+
}).content;
|
|
331
|
+
}
|
|
332
|
+
function mapGenerateContentResultToChatResult(response, extra) {
|
|
333
|
+
if (!response.candidates || response.candidates.length === 0 || !response.candidates[0]) return {
|
|
334
|
+
generations: [],
|
|
335
|
+
llmOutput: { filters: response.promptFeedback }
|
|
336
|
+
};
|
|
337
|
+
const [candidate] = response.candidates;
|
|
338
|
+
const { content: candidateContent, ...generationInfo } = candidate;
|
|
339
|
+
const functionCalls = candidateContent?.parts?.reduce((acc, p) => {
|
|
340
|
+
if ("functionCall" in p && p.functionCall) acc.push({
|
|
341
|
+
...p,
|
|
342
|
+
id: "id" in p.functionCall && typeof p.functionCall.id === "string" ? p.functionCall.id : (0, uuid.v4)()
|
|
343
|
+
});
|
|
344
|
+
return acc;
|
|
345
|
+
}, []);
|
|
346
|
+
let content;
|
|
347
|
+
const parts = candidateContent?.parts;
|
|
348
|
+
if (Array.isArray(parts) && parts.length === 1 && "text" in parts[0] && parts[0].text && !parts[0].thought) content = parts[0].text;
|
|
349
|
+
else if (Array.isArray(parts) && parts.length > 0) content = parts.map((p) => {
|
|
350
|
+
if (p.thought && "text" in p && p.text) return {
|
|
351
|
+
type: "thinking",
|
|
352
|
+
thinking: p.text,
|
|
353
|
+
...p.thoughtSignature ? { signature: p.thoughtSignature } : {}
|
|
354
|
+
};
|
|
355
|
+
else if ("text" in p) return {
|
|
356
|
+
type: "text",
|
|
357
|
+
text: p.text
|
|
358
|
+
};
|
|
359
|
+
else if ("inlineData" in p) return {
|
|
360
|
+
type: "inlineData",
|
|
361
|
+
inlineData: p.inlineData
|
|
362
|
+
};
|
|
363
|
+
else if ("functionCall" in p) return {
|
|
364
|
+
type: "functionCall",
|
|
365
|
+
functionCall: p.functionCall
|
|
366
|
+
};
|
|
367
|
+
else if ("functionResponse" in p) return {
|
|
368
|
+
type: "functionResponse",
|
|
369
|
+
functionResponse: p.functionResponse
|
|
370
|
+
};
|
|
371
|
+
else if ("fileData" in p) return {
|
|
372
|
+
type: "fileData",
|
|
373
|
+
fileData: p.fileData
|
|
374
|
+
};
|
|
375
|
+
else if ("executableCode" in p) return {
|
|
376
|
+
type: "executableCode",
|
|
377
|
+
executableCode: p.executableCode
|
|
378
|
+
};
|
|
379
|
+
else if ("codeExecutionResult" in p) return {
|
|
380
|
+
type: "codeExecutionResult",
|
|
381
|
+
codeExecutionResult: p.codeExecutionResult
|
|
382
|
+
};
|
|
383
|
+
return p;
|
|
384
|
+
});
|
|
385
|
+
else content = [];
|
|
386
|
+
const functionThoughtSignatures = functionCalls?.reduce((acc, fc) => {
|
|
387
|
+
if ("thoughtSignature" in fc && typeof fc.thoughtSignature === "string") acc[fc.id] = fc.thoughtSignature;
|
|
388
|
+
return acc;
|
|
389
|
+
}, {});
|
|
390
|
+
let text = "";
|
|
391
|
+
if (typeof content === "string") text = content;
|
|
392
|
+
else if (Array.isArray(content) && content.length > 0) text = content.find((b) => "text" in b)?.text ?? text;
|
|
393
|
+
return {
|
|
394
|
+
generations: [{
|
|
395
|
+
text,
|
|
396
|
+
message: new _langchain_core_messages.AIMessage({
|
|
397
|
+
content: content ?? "",
|
|
398
|
+
tool_calls: functionCalls?.map((fc) => ({
|
|
399
|
+
type: "tool_call",
|
|
400
|
+
id: fc.id,
|
|
401
|
+
name: fc.functionCall?.name ?? "",
|
|
402
|
+
args: fc.functionCall?.args ?? {}
|
|
403
|
+
})),
|
|
404
|
+
additional_kwargs: {
|
|
405
|
+
...generationInfo,
|
|
406
|
+
[_FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY]: functionThoughtSignatures
|
|
407
|
+
},
|
|
408
|
+
response_metadata: { model_provider: "google-genai" },
|
|
409
|
+
usage_metadata: extra?.usageMetadata
|
|
410
|
+
}),
|
|
411
|
+
generationInfo
|
|
412
|
+
}],
|
|
413
|
+
llmOutput: { tokenUsage: {
|
|
414
|
+
promptTokens: extra?.usageMetadata?.input_tokens,
|
|
415
|
+
completionTokens: extra?.usageMetadata?.output_tokens,
|
|
416
|
+
totalTokens: extra?.usageMetadata?.total_tokens
|
|
417
|
+
} }
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function convertResponseContentToChatGenerationChunk(response, extra) {
|
|
421
|
+
if (!response.candidates || response.candidates.length === 0) return null;
|
|
422
|
+
const [candidate] = response.candidates;
|
|
423
|
+
const { content: candidateContent, ...generationInfo } = candidate;
|
|
424
|
+
const functionCalls = candidateContent?.parts?.reduce((acc, p) => {
|
|
425
|
+
if ("functionCall" in p && p.functionCall) acc.push({
|
|
426
|
+
...p,
|
|
427
|
+
id: "id" in p.functionCall && typeof p.functionCall.id === "string" ? p.functionCall.id : (0, uuid.v4)()
|
|
428
|
+
});
|
|
429
|
+
return acc;
|
|
430
|
+
}, []);
|
|
431
|
+
let content;
|
|
432
|
+
const streamParts = candidateContent?.parts;
|
|
433
|
+
if (Array.isArray(streamParts) && streamParts.every((p) => "text" in p && !p.thought)) content = streamParts.map((p) => p.text).join("");
|
|
434
|
+
else if (Array.isArray(streamParts)) content = streamParts.map((p) => {
|
|
435
|
+
if (p.thought && "text" in p && p.text) return {
|
|
436
|
+
type: "thinking",
|
|
437
|
+
thinking: p.text,
|
|
438
|
+
...p.thoughtSignature ? { signature: p.thoughtSignature } : {}
|
|
439
|
+
};
|
|
440
|
+
else if ("text" in p) return {
|
|
441
|
+
type: "text",
|
|
442
|
+
text: p.text
|
|
443
|
+
};
|
|
444
|
+
else if ("inlineData" in p) return {
|
|
445
|
+
type: "inlineData",
|
|
446
|
+
inlineData: p.inlineData
|
|
447
|
+
};
|
|
448
|
+
else if ("functionCall" in p) return {
|
|
449
|
+
type: "functionCall",
|
|
450
|
+
functionCall: p.functionCall
|
|
451
|
+
};
|
|
452
|
+
else if ("functionResponse" in p) return {
|
|
453
|
+
type: "functionResponse",
|
|
454
|
+
functionResponse: p.functionResponse
|
|
455
|
+
};
|
|
456
|
+
else if ("fileData" in p) return {
|
|
457
|
+
type: "fileData",
|
|
458
|
+
fileData: p.fileData
|
|
459
|
+
};
|
|
460
|
+
else if ("executableCode" in p) return {
|
|
461
|
+
type: "executableCode",
|
|
462
|
+
executableCode: p.executableCode
|
|
463
|
+
};
|
|
464
|
+
else if ("codeExecutionResult" in p) return {
|
|
465
|
+
type: "codeExecutionResult",
|
|
466
|
+
codeExecutionResult: p.codeExecutionResult
|
|
467
|
+
};
|
|
468
|
+
return p;
|
|
469
|
+
});
|
|
470
|
+
else content = [];
|
|
471
|
+
let text = "";
|
|
472
|
+
if (content && typeof content === "string") text = content;
|
|
473
|
+
else if (Array.isArray(content)) text = content.find((b) => "text" in b)?.text ?? "";
|
|
474
|
+
const toolCallChunks = [];
|
|
475
|
+
if (functionCalls) toolCallChunks.push(...functionCalls.map((fc) => ({
|
|
476
|
+
type: "tool_call_chunk",
|
|
477
|
+
id: fc.id,
|
|
478
|
+
name: fc.functionCall?.name,
|
|
479
|
+
args: JSON.stringify(fc.functionCall?.args)
|
|
480
|
+
})));
|
|
481
|
+
const functionThoughtSignatures = functionCalls?.reduce((acc, fc) => {
|
|
482
|
+
if ("thoughtSignature" in fc && typeof fc.thoughtSignature === "string") acc[fc.id] = fc.thoughtSignature;
|
|
483
|
+
return acc;
|
|
484
|
+
}, {});
|
|
485
|
+
return new _langchain_core_outputs.ChatGenerationChunk({
|
|
486
|
+
text,
|
|
487
|
+
message: new _langchain_core_messages.AIMessageChunk({
|
|
488
|
+
content: content || "",
|
|
489
|
+
name: !candidateContent ? void 0 : candidateContent.role,
|
|
490
|
+
tool_call_chunks: toolCallChunks,
|
|
491
|
+
additional_kwargs: { [_FUNCTION_CALL_THOUGHT_SIGNATURES_MAP_KEY]: functionThoughtSignatures },
|
|
492
|
+
response_metadata: { model_provider: "google-genai" },
|
|
493
|
+
usage_metadata: extra.usageMetadata
|
|
494
|
+
}),
|
|
495
|
+
generationInfo
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
function convertToGenerativeAITools(tools) {
|
|
499
|
+
if (tools.every((tool) => "functionDeclarations" in tool && Array.isArray(tool.functionDeclarations))) return tools;
|
|
500
|
+
return [{ functionDeclarations: tools.map((tool) => {
|
|
501
|
+
if ((0, _langchain_core_utils_function_calling.isLangChainTool)(tool)) {
|
|
502
|
+
const jsonSchema = schemaToGenerativeAIParameters(tool.schema);
|
|
503
|
+
if (jsonSchema.type === "object" && Object.keys(jsonSchema.properties ?? {}).length === 0) return {
|
|
504
|
+
name: tool.name,
|
|
505
|
+
description: tool.description
|
|
506
|
+
};
|
|
507
|
+
assertNoEmptyStringEnums(jsonSchema, tool.name);
|
|
508
|
+
return {
|
|
509
|
+
name: tool.name,
|
|
510
|
+
description: tool.description,
|
|
511
|
+
parameters: jsonSchema
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
if ((0, _langchain_core_language_models_base.isOpenAITool)(tool)) {
|
|
515
|
+
const params = jsonSchemaToGeminiParameters(tool.function.parameters);
|
|
516
|
+
assertNoEmptyStringEnums(params, tool.function.name);
|
|
517
|
+
return {
|
|
518
|
+
name: tool.function.name,
|
|
519
|
+
description: tool.function.description ?? `A function available to call.`,
|
|
520
|
+
parameters: params
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
return tool;
|
|
524
|
+
}) }];
|
|
525
|
+
}
|
|
526
|
+
function convertUsageMetadata(usageMetadata, model) {
|
|
527
|
+
const reasoningTokens = usageMetadata?.thoughtsTokenCount ?? 0;
|
|
528
|
+
const output = {
|
|
529
|
+
input_tokens: usageMetadata?.promptTokenCount ?? 0,
|
|
530
|
+
output_tokens: (usageMetadata?.candidatesTokenCount ?? 0) + reasoningTokens,
|
|
531
|
+
total_tokens: usageMetadata?.totalTokenCount ?? 0
|
|
532
|
+
};
|
|
533
|
+
if (reasoningTokens) output.output_token_details = {
|
|
534
|
+
...output.output_token_details,
|
|
535
|
+
reasoning: reasoningTokens
|
|
536
|
+
};
|
|
537
|
+
if (usageMetadata?.cachedContentTokenCount) {
|
|
538
|
+
output.input_token_details ??= {};
|
|
539
|
+
output.input_token_details.cache_read = usageMetadata.cachedContentTokenCount;
|
|
540
|
+
}
|
|
541
|
+
if (model.startsWith("gemini-3") && model.includes("pro")) {
|
|
542
|
+
const over200k = Math.max(0, (usageMetadata?.promptTokenCount ?? 0) - 2e5);
|
|
543
|
+
const cachedOver200k = Math.max(0, (usageMetadata?.cachedContentTokenCount ?? 0) - 2e5);
|
|
544
|
+
if (over200k) output.input_token_details = {
|
|
545
|
+
...output.input_token_details,
|
|
546
|
+
over_200k: over200k
|
|
547
|
+
};
|
|
548
|
+
if (cachedOver200k) output.input_token_details = {
|
|
549
|
+
...output.input_token_details,
|
|
550
|
+
cache_read_over_200k: cachedOver200k
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
return output;
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/output_parsers.ts
|
|
557
|
+
var GoogleGenerativeAIToolsOutputParser = class extends _langchain_core_output_parsers.BaseLLMOutputParser {
|
|
558
|
+
static lc_name() {
|
|
559
|
+
return "GoogleGenerativeAIToolsOutputParser";
|
|
560
|
+
}
|
|
561
|
+
lc_namespace = [
|
|
562
|
+
"langchain",
|
|
563
|
+
"google_genai",
|
|
564
|
+
"output_parsers"
|
|
565
|
+
];
|
|
566
|
+
returnId = false;
|
|
567
|
+
/** The type of tool calls to return. */
|
|
568
|
+
keyName;
|
|
569
|
+
/** Whether to return only the first tool call. */
|
|
570
|
+
returnSingle = false;
|
|
571
|
+
zodSchema;
|
|
572
|
+
serializableSchema;
|
|
573
|
+
constructor(params) {
|
|
574
|
+
super(params);
|
|
575
|
+
this.keyName = params.keyName;
|
|
576
|
+
this.returnSingle = params.returnSingle ?? this.returnSingle;
|
|
577
|
+
this.zodSchema = params.zodSchema;
|
|
578
|
+
this.serializableSchema = params.serializableSchema;
|
|
579
|
+
}
|
|
580
|
+
async _validateResult(result) {
|
|
581
|
+
if (this.serializableSchema !== void 0) {
|
|
582
|
+
const validated = await this.serializableSchema["~standard"].validate(result);
|
|
583
|
+
if (validated.issues) throw new _langchain_core_output_parsers.OutputParserException(`Failed to parse. Text: "${JSON.stringify(result, null, 2)}". Error: ${JSON.stringify(validated.issues)}`, JSON.stringify(result, null, 2));
|
|
584
|
+
return validated.value;
|
|
585
|
+
}
|
|
586
|
+
if (this.zodSchema === void 0) return result;
|
|
587
|
+
const zodParsedResult = await (0, _langchain_core_utils_types.interopSafeParseAsync)(this.zodSchema, result);
|
|
588
|
+
if (zodParsedResult.success) return zodParsedResult.data;
|
|
589
|
+
else throw new _langchain_core_output_parsers.OutputParserException(`Failed to parse. Text: "${JSON.stringify(result, null, 2)}". Error: ${JSON.stringify(zodParsedResult.error.issues)}`, JSON.stringify(result, null, 2));
|
|
590
|
+
}
|
|
591
|
+
async parseResult(generations) {
|
|
592
|
+
const tools = generations.flatMap((generation) => {
|
|
593
|
+
const { message } = generation;
|
|
594
|
+
if (!("tool_calls" in message) || !Array.isArray(message.tool_calls)) return [];
|
|
595
|
+
return message.tool_calls;
|
|
596
|
+
});
|
|
597
|
+
if (tools[0] === void 0) throw new Error("No parseable tool calls provided to GoogleGenerativeAIToolsOutputParser.");
|
|
598
|
+
const [tool] = tools;
|
|
599
|
+
return await this._validateResult(tool.args);
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
//#endregion
|
|
603
|
+
//#region src/utils/tools.ts
|
|
604
|
+
function convertToolsToGenAI(tools, extra) {
|
|
605
|
+
const genAITools = processTools(tools);
|
|
606
|
+
return {
|
|
607
|
+
tools: genAITools,
|
|
608
|
+
toolConfig: createToolConfig(genAITools, extra)
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
function processTools(tools) {
|
|
612
|
+
let functionDeclarationTools = [];
|
|
613
|
+
const genAITools = [];
|
|
614
|
+
tools.forEach((tool) => {
|
|
615
|
+
if ((0, _langchain_core_utils_function_calling.isLangChainTool)(tool)) {
|
|
616
|
+
const [convertedTool] = convertToGenerativeAITools([tool]);
|
|
617
|
+
if (convertedTool.functionDeclarations) functionDeclarationTools.push(...convertedTool.functionDeclarations);
|
|
618
|
+
} else if ((0, _langchain_core_language_models_base.isOpenAITool)(tool)) {
|
|
619
|
+
const { functionDeclarations } = convertOpenAIToolToGenAI(tool);
|
|
620
|
+
if (functionDeclarations) functionDeclarationTools.push(...functionDeclarations);
|
|
621
|
+
else throw new Error("Failed to convert OpenAI structured tool to GenerativeAI tool");
|
|
622
|
+
} else genAITools.push(normalizeGenAITool(tool));
|
|
623
|
+
});
|
|
624
|
+
if (genAITools.find((t) => "functionDeclarations" in t)) return genAITools.map((tool) => {
|
|
625
|
+
if (functionDeclarationTools?.length > 0 && "functionDeclarations" in tool) {
|
|
626
|
+
const newTool = { functionDeclarations: [...tool.functionDeclarations || [], ...functionDeclarationTools] };
|
|
627
|
+
functionDeclarationTools = [];
|
|
628
|
+
return newTool;
|
|
629
|
+
}
|
|
630
|
+
return tool;
|
|
631
|
+
});
|
|
632
|
+
return [...genAITools, ...functionDeclarationTools.length > 0 ? [{ functionDeclarations: functionDeclarationTools }] : []];
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Current Gemini models (2.x and newer) removed the legacy
|
|
636
|
+
* `googleSearchRetrieval` tool (with its `dynamicRetrievalConfig`) and reject
|
|
637
|
+
* it with `google_search_retrieval is not supported. Please use google_search
|
|
638
|
+
* tool instead.`. Transparently rewrite it to the `googleSearch` tool so
|
|
639
|
+
* existing code keeps working against the newest models.
|
|
640
|
+
*/
|
|
641
|
+
function normalizeGenAITool(tool) {
|
|
642
|
+
if (tool && "googleSearchRetrieval" in tool && !("googleSearch" in tool)) {
|
|
643
|
+
const { googleSearchRetrieval, ...rest } = tool;
|
|
644
|
+
return {
|
|
645
|
+
...rest,
|
|
646
|
+
googleSearch: {}
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
return tool;
|
|
650
|
+
}
|
|
651
|
+
function convertOpenAIToolToGenAI(tool) {
|
|
652
|
+
return { functionDeclarations: [{
|
|
653
|
+
name: tool.function.name,
|
|
654
|
+
description: tool.function.description,
|
|
655
|
+
parameters: removeAdditionalProperties(tool.function.parameters)
|
|
656
|
+
}] };
|
|
657
|
+
}
|
|
658
|
+
function createToolConfig(genAITools, extra) {
|
|
659
|
+
if (!genAITools.length || !extra) return void 0;
|
|
660
|
+
const { toolChoice, allowedFunctionNames } = extra;
|
|
661
|
+
const modeMap = {
|
|
662
|
+
any: _google_genai.FunctionCallingConfigMode.ANY,
|
|
663
|
+
auto: _google_genai.FunctionCallingConfigMode.AUTO,
|
|
664
|
+
none: _google_genai.FunctionCallingConfigMode.NONE
|
|
665
|
+
};
|
|
666
|
+
if (toolChoice && [
|
|
667
|
+
"any",
|
|
668
|
+
"auto",
|
|
669
|
+
"none"
|
|
670
|
+
].includes(toolChoice)) return { functionCallingConfig: {
|
|
671
|
+
mode: modeMap[toolChoice] ?? _google_genai.FunctionCallingConfigMode.MODE_UNSPECIFIED,
|
|
672
|
+
allowedFunctionNames
|
|
673
|
+
} };
|
|
674
|
+
if (typeof toolChoice === "string" || allowedFunctionNames) return { functionCallingConfig: {
|
|
675
|
+
mode: _google_genai.FunctionCallingConfigMode.ANY,
|
|
676
|
+
allowedFunctionNames: [...allowedFunctionNames ?? [], ...toolChoice && typeof toolChoice === "string" ? [toolChoice] : []]
|
|
677
|
+
} };
|
|
678
|
+
}
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/profiles.ts
|
|
681
|
+
const PROFILES = {
|
|
682
|
+
"gemini-embedding-001": {
|
|
683
|
+
maxInputTokens: 2048,
|
|
684
|
+
imageInputs: false,
|
|
685
|
+
audioInputs: false,
|
|
686
|
+
pdfInputs: false,
|
|
687
|
+
videoInputs: false,
|
|
688
|
+
maxOutputTokens: 3072,
|
|
689
|
+
reasoningOutput: false,
|
|
690
|
+
imageOutputs: false,
|
|
691
|
+
audioOutputs: false,
|
|
692
|
+
videoOutputs: false,
|
|
693
|
+
toolCalling: false,
|
|
694
|
+
structuredOutput: false
|
|
695
|
+
},
|
|
696
|
+
"gemini-2.5-flash-lite-preview-09-2025": {
|
|
697
|
+
maxInputTokens: 1048576,
|
|
698
|
+
imageInputs: true,
|
|
699
|
+
audioInputs: true,
|
|
700
|
+
pdfInputs: true,
|
|
701
|
+
videoInputs: true,
|
|
702
|
+
maxOutputTokens: 65536,
|
|
703
|
+
reasoningOutput: true,
|
|
704
|
+
imageOutputs: false,
|
|
705
|
+
audioOutputs: false,
|
|
706
|
+
videoOutputs: false,
|
|
707
|
+
toolCalling: true,
|
|
708
|
+
structuredOutput: true
|
|
709
|
+
},
|
|
710
|
+
"gemini-2.5-pro-preview-06-05": {
|
|
711
|
+
maxInputTokens: 1048576,
|
|
712
|
+
imageInputs: true,
|
|
713
|
+
audioInputs: true,
|
|
714
|
+
pdfInputs: true,
|
|
715
|
+
videoInputs: true,
|
|
716
|
+
maxOutputTokens: 65536,
|
|
717
|
+
reasoningOutput: true,
|
|
718
|
+
imageOutputs: false,
|
|
719
|
+
audioOutputs: false,
|
|
720
|
+
videoOutputs: false,
|
|
721
|
+
toolCalling: true,
|
|
722
|
+
structuredOutput: true
|
|
723
|
+
},
|
|
724
|
+
"gemini-2.5-flash-preview-04-17": {
|
|
725
|
+
maxInputTokens: 1048576,
|
|
726
|
+
imageInputs: true,
|
|
727
|
+
audioInputs: true,
|
|
728
|
+
pdfInputs: true,
|
|
729
|
+
videoInputs: true,
|
|
730
|
+
maxOutputTokens: 65536,
|
|
731
|
+
reasoningOutput: true,
|
|
732
|
+
imageOutputs: false,
|
|
733
|
+
audioOutputs: false,
|
|
734
|
+
videoOutputs: false,
|
|
735
|
+
toolCalling: true,
|
|
736
|
+
structuredOutput: false
|
|
737
|
+
},
|
|
738
|
+
"gemini-2.5-flash-preview-09-2025": {
|
|
739
|
+
maxInputTokens: 1048576,
|
|
740
|
+
imageInputs: true,
|
|
741
|
+
audioInputs: true,
|
|
742
|
+
pdfInputs: true,
|
|
743
|
+
videoInputs: true,
|
|
744
|
+
maxOutputTokens: 65536,
|
|
745
|
+
reasoningOutput: true,
|
|
746
|
+
imageOutputs: false,
|
|
747
|
+
audioOutputs: false,
|
|
748
|
+
videoOutputs: false,
|
|
749
|
+
toolCalling: true,
|
|
750
|
+
structuredOutput: true
|
|
751
|
+
},
|
|
752
|
+
"gemini-2.5-pro-preview-05-06": {
|
|
753
|
+
maxInputTokens: 1048576,
|
|
754
|
+
imageInputs: true,
|
|
755
|
+
audioInputs: true,
|
|
756
|
+
pdfInputs: true,
|
|
757
|
+
videoInputs: true,
|
|
758
|
+
maxOutputTokens: 65536,
|
|
759
|
+
reasoningOutput: true,
|
|
760
|
+
imageOutputs: false,
|
|
761
|
+
audioOutputs: false,
|
|
762
|
+
videoOutputs: false,
|
|
763
|
+
toolCalling: true,
|
|
764
|
+
structuredOutput: true
|
|
765
|
+
},
|
|
766
|
+
"gemini-2.5-flash-preview-05-20": {
|
|
767
|
+
maxInputTokens: 1048576,
|
|
768
|
+
imageInputs: true,
|
|
769
|
+
audioInputs: true,
|
|
770
|
+
pdfInputs: true,
|
|
771
|
+
videoInputs: true,
|
|
772
|
+
maxOutputTokens: 65536,
|
|
773
|
+
reasoningOutput: true,
|
|
774
|
+
imageOutputs: false,
|
|
775
|
+
audioOutputs: false,
|
|
776
|
+
videoOutputs: false,
|
|
777
|
+
toolCalling: true,
|
|
778
|
+
structuredOutput: true
|
|
779
|
+
},
|
|
780
|
+
"gemini-2.5-flash": {
|
|
781
|
+
maxInputTokens: 1048576,
|
|
782
|
+
imageInputs: true,
|
|
783
|
+
audioInputs: true,
|
|
784
|
+
pdfInputs: true,
|
|
785
|
+
videoInputs: true,
|
|
786
|
+
maxOutputTokens: 65536,
|
|
787
|
+
reasoningOutput: true,
|
|
788
|
+
imageOutputs: false,
|
|
789
|
+
audioOutputs: false,
|
|
790
|
+
videoOutputs: false,
|
|
791
|
+
toolCalling: true,
|
|
792
|
+
structuredOutput: true
|
|
793
|
+
},
|
|
794
|
+
"gemini-live-2.5-flash": {
|
|
795
|
+
maxInputTokens: 128e3,
|
|
796
|
+
imageInputs: true,
|
|
797
|
+
audioInputs: true,
|
|
798
|
+
pdfInputs: false,
|
|
799
|
+
videoInputs: true,
|
|
800
|
+
maxOutputTokens: 8e3,
|
|
801
|
+
reasoningOutput: true,
|
|
802
|
+
imageOutputs: false,
|
|
803
|
+
audioOutputs: true,
|
|
804
|
+
videoOutputs: false,
|
|
805
|
+
toolCalling: true,
|
|
806
|
+
structuredOutput: false
|
|
807
|
+
},
|
|
808
|
+
"gemini-3-flash-preview": {
|
|
809
|
+
maxInputTokens: 1048576,
|
|
810
|
+
imageInputs: true,
|
|
811
|
+
audioInputs: true,
|
|
812
|
+
pdfInputs: true,
|
|
813
|
+
videoInputs: true,
|
|
814
|
+
maxOutputTokens: 65536,
|
|
815
|
+
reasoningOutput: true,
|
|
816
|
+
imageOutputs: false,
|
|
817
|
+
audioOutputs: false,
|
|
818
|
+
videoOutputs: false,
|
|
819
|
+
toolCalling: true,
|
|
820
|
+
structuredOutput: true
|
|
821
|
+
},
|
|
822
|
+
"gemini-3.5-flash": {
|
|
823
|
+
maxInputTokens: 1048576,
|
|
824
|
+
imageInputs: true,
|
|
825
|
+
audioInputs: true,
|
|
826
|
+
pdfInputs: true,
|
|
827
|
+
videoInputs: true,
|
|
828
|
+
maxOutputTokens: 65536,
|
|
829
|
+
reasoningOutput: true,
|
|
830
|
+
imageOutputs: false,
|
|
831
|
+
audioOutputs: false,
|
|
832
|
+
videoOutputs: false,
|
|
833
|
+
toolCalling: true,
|
|
834
|
+
structuredOutput: true
|
|
835
|
+
},
|
|
836
|
+
"gemini-3.1-pro-preview": {
|
|
837
|
+
maxInputTokens: 1048576,
|
|
838
|
+
imageInputs: true,
|
|
839
|
+
audioInputs: true,
|
|
840
|
+
pdfInputs: true,
|
|
841
|
+
videoInputs: true,
|
|
842
|
+
maxOutputTokens: 65536,
|
|
843
|
+
reasoningOutput: true,
|
|
844
|
+
imageOutputs: false,
|
|
845
|
+
audioOutputs: false,
|
|
846
|
+
videoOutputs: false,
|
|
847
|
+
toolCalling: true,
|
|
848
|
+
structuredOutput: true
|
|
849
|
+
},
|
|
850
|
+
"gemini-3.1-flash-lite": {
|
|
851
|
+
maxInputTokens: 1048576,
|
|
852
|
+
imageInputs: true,
|
|
853
|
+
audioInputs: true,
|
|
854
|
+
pdfInputs: true,
|
|
855
|
+
videoInputs: true,
|
|
856
|
+
maxOutputTokens: 65536,
|
|
857
|
+
reasoningOutput: true,
|
|
858
|
+
imageOutputs: false,
|
|
859
|
+
audioOutputs: false,
|
|
860
|
+
videoOutputs: false,
|
|
861
|
+
toolCalling: true,
|
|
862
|
+
structuredOutput: true
|
|
863
|
+
},
|
|
864
|
+
"gemini-3.1-flash-lite-preview": {
|
|
865
|
+
maxInputTokens: 1048576,
|
|
866
|
+
imageInputs: true,
|
|
867
|
+
audioInputs: true,
|
|
868
|
+
pdfInputs: true,
|
|
869
|
+
videoInputs: true,
|
|
870
|
+
maxOutputTokens: 65536,
|
|
871
|
+
reasoningOutput: true,
|
|
872
|
+
imageOutputs: false,
|
|
873
|
+
audioOutputs: false,
|
|
874
|
+
videoOutputs: false,
|
|
875
|
+
toolCalling: true,
|
|
876
|
+
structuredOutput: true
|
|
877
|
+
},
|
|
878
|
+
"gemini-pro-latest": {
|
|
879
|
+
maxInputTokens: 1048576,
|
|
880
|
+
imageInputs: true,
|
|
881
|
+
audioInputs: true,
|
|
882
|
+
pdfInputs: true,
|
|
883
|
+
videoInputs: true,
|
|
884
|
+
maxOutputTokens: 65536,
|
|
885
|
+
reasoningOutput: true,
|
|
886
|
+
imageOutputs: false,
|
|
887
|
+
audioOutputs: false,
|
|
888
|
+
videoOutputs: false,
|
|
889
|
+
toolCalling: true,
|
|
890
|
+
structuredOutput: true
|
|
891
|
+
},
|
|
892
|
+
"gemini-live-2.5-flash-preview-native-audio": {
|
|
893
|
+
maxInputTokens: 131072,
|
|
894
|
+
imageInputs: false,
|
|
895
|
+
audioInputs: true,
|
|
896
|
+
pdfInputs: false,
|
|
897
|
+
videoInputs: true,
|
|
898
|
+
maxOutputTokens: 65536,
|
|
899
|
+
reasoningOutput: true,
|
|
900
|
+
imageOutputs: false,
|
|
901
|
+
audioOutputs: true,
|
|
902
|
+
videoOutputs: false,
|
|
903
|
+
toolCalling: true,
|
|
904
|
+
structuredOutput: false
|
|
905
|
+
},
|
|
906
|
+
"gemini-2.5-flash-lite": {
|
|
907
|
+
maxInputTokens: 1048576,
|
|
908
|
+
imageInputs: true,
|
|
909
|
+
audioInputs: true,
|
|
910
|
+
pdfInputs: true,
|
|
911
|
+
videoInputs: true,
|
|
912
|
+
maxOutputTokens: 65536,
|
|
913
|
+
reasoningOutput: true,
|
|
914
|
+
imageOutputs: false,
|
|
915
|
+
audioOutputs: false,
|
|
916
|
+
videoOutputs: false,
|
|
917
|
+
toolCalling: true,
|
|
918
|
+
structuredOutput: true
|
|
919
|
+
},
|
|
920
|
+
"gemini-2.5-flash-preview-tts": {
|
|
921
|
+
maxInputTokens: 8e3,
|
|
922
|
+
imageInputs: false,
|
|
923
|
+
audioInputs: false,
|
|
924
|
+
pdfInputs: false,
|
|
925
|
+
videoInputs: false,
|
|
926
|
+
maxOutputTokens: 16e3,
|
|
927
|
+
reasoningOutput: false,
|
|
928
|
+
imageOutputs: false,
|
|
929
|
+
audioOutputs: true,
|
|
930
|
+
videoOutputs: false,
|
|
931
|
+
toolCalling: false,
|
|
932
|
+
structuredOutput: false
|
|
933
|
+
},
|
|
934
|
+
"gemini-flash-latest": {
|
|
935
|
+
maxInputTokens: 1048576,
|
|
936
|
+
imageInputs: true,
|
|
937
|
+
audioInputs: true,
|
|
938
|
+
pdfInputs: true,
|
|
939
|
+
videoInputs: true,
|
|
940
|
+
maxOutputTokens: 65536,
|
|
941
|
+
reasoningOutput: true,
|
|
942
|
+
imageOutputs: false,
|
|
943
|
+
audioOutputs: false,
|
|
944
|
+
videoOutputs: false,
|
|
945
|
+
toolCalling: true,
|
|
946
|
+
structuredOutput: true
|
|
947
|
+
},
|
|
948
|
+
"gemini-2.5-flash-lite-preview-06-17": {
|
|
949
|
+
maxInputTokens: 1048576,
|
|
950
|
+
imageInputs: true,
|
|
951
|
+
audioInputs: true,
|
|
952
|
+
pdfInputs: true,
|
|
953
|
+
videoInputs: true,
|
|
954
|
+
maxOutputTokens: 65536,
|
|
955
|
+
reasoningOutput: true,
|
|
956
|
+
imageOutputs: false,
|
|
957
|
+
audioOutputs: false,
|
|
958
|
+
videoOutputs: false,
|
|
959
|
+
toolCalling: true,
|
|
960
|
+
structuredOutput: false
|
|
961
|
+
},
|
|
962
|
+
"gemini-2.5-flash-image": {
|
|
963
|
+
maxInputTokens: 32768,
|
|
964
|
+
imageInputs: true,
|
|
965
|
+
audioInputs: false,
|
|
966
|
+
pdfInputs: false,
|
|
967
|
+
videoInputs: false,
|
|
968
|
+
maxOutputTokens: 32768,
|
|
969
|
+
reasoningOutput: true,
|
|
970
|
+
imageOutputs: true,
|
|
971
|
+
audioOutputs: false,
|
|
972
|
+
videoOutputs: false,
|
|
973
|
+
toolCalling: false,
|
|
974
|
+
structuredOutput: false
|
|
975
|
+
},
|
|
976
|
+
"gemini-2.5-pro-preview-tts": {
|
|
977
|
+
maxInputTokens: 8e3,
|
|
978
|
+
imageInputs: false,
|
|
979
|
+
audioInputs: false,
|
|
980
|
+
pdfInputs: false,
|
|
981
|
+
videoInputs: false,
|
|
982
|
+
maxOutputTokens: 16e3,
|
|
983
|
+
reasoningOutput: false,
|
|
984
|
+
imageOutputs: false,
|
|
985
|
+
audioOutputs: true,
|
|
986
|
+
videoOutputs: false,
|
|
987
|
+
toolCalling: false,
|
|
988
|
+
structuredOutput: false
|
|
989
|
+
},
|
|
990
|
+
"gemini-2.5-flash-image-preview": {
|
|
991
|
+
maxInputTokens: 32768,
|
|
992
|
+
imageInputs: true,
|
|
993
|
+
audioInputs: false,
|
|
994
|
+
pdfInputs: false,
|
|
995
|
+
videoInputs: false,
|
|
996
|
+
maxOutputTokens: 32768,
|
|
997
|
+
reasoningOutput: true,
|
|
998
|
+
imageOutputs: true,
|
|
999
|
+
audioOutputs: false,
|
|
1000
|
+
videoOutputs: false,
|
|
1001
|
+
toolCalling: false,
|
|
1002
|
+
structuredOutput: false
|
|
1003
|
+
},
|
|
1004
|
+
"gemini-1.5-flash-8b": {
|
|
1005
|
+
maxInputTokens: 1e6,
|
|
1006
|
+
imageInputs: true,
|
|
1007
|
+
audioInputs: true,
|
|
1008
|
+
pdfInputs: false,
|
|
1009
|
+
videoInputs: true,
|
|
1010
|
+
maxOutputTokens: 8192,
|
|
1011
|
+
reasoningOutput: false,
|
|
1012
|
+
imageOutputs: false,
|
|
1013
|
+
audioOutputs: false,
|
|
1014
|
+
videoOutputs: false,
|
|
1015
|
+
toolCalling: true,
|
|
1016
|
+
structuredOutput: false
|
|
1017
|
+
},
|
|
1018
|
+
"gemini-3-pro-preview": {
|
|
1019
|
+
maxInputTokens: 1e6,
|
|
1020
|
+
imageInputs: true,
|
|
1021
|
+
audioInputs: true,
|
|
1022
|
+
pdfInputs: true,
|
|
1023
|
+
videoInputs: true,
|
|
1024
|
+
maxOutputTokens: 64e3,
|
|
1025
|
+
reasoningOutput: true,
|
|
1026
|
+
imageOutputs: false,
|
|
1027
|
+
audioOutputs: false,
|
|
1028
|
+
videoOutputs: false,
|
|
1029
|
+
toolCalling: true,
|
|
1030
|
+
structuredOutput: true
|
|
1031
|
+
},
|
|
1032
|
+
"gemini-2.0-flash-lite": {
|
|
1033
|
+
maxInputTokens: 1048576,
|
|
1034
|
+
imageInputs: true,
|
|
1035
|
+
audioInputs: true,
|
|
1036
|
+
pdfInputs: true,
|
|
1037
|
+
videoInputs: true,
|
|
1038
|
+
maxOutputTokens: 8192,
|
|
1039
|
+
reasoningOutput: false,
|
|
1040
|
+
imageOutputs: false,
|
|
1041
|
+
audioOutputs: false,
|
|
1042
|
+
videoOutputs: false,
|
|
1043
|
+
toolCalling: true,
|
|
1044
|
+
structuredOutput: true
|
|
1045
|
+
},
|
|
1046
|
+
"gemini-1.5-flash": {
|
|
1047
|
+
maxInputTokens: 1e6,
|
|
1048
|
+
imageInputs: true,
|
|
1049
|
+
audioInputs: true,
|
|
1050
|
+
pdfInputs: false,
|
|
1051
|
+
videoInputs: true,
|
|
1052
|
+
maxOutputTokens: 8192,
|
|
1053
|
+
reasoningOutput: false,
|
|
1054
|
+
imageOutputs: false,
|
|
1055
|
+
audioOutputs: false,
|
|
1056
|
+
videoOutputs: false,
|
|
1057
|
+
toolCalling: true,
|
|
1058
|
+
structuredOutput: false
|
|
1059
|
+
},
|
|
1060
|
+
"gemini-flash-lite-latest": {
|
|
1061
|
+
maxInputTokens: 1048576,
|
|
1062
|
+
imageInputs: true,
|
|
1063
|
+
audioInputs: true,
|
|
1064
|
+
pdfInputs: true,
|
|
1065
|
+
videoInputs: true,
|
|
1066
|
+
maxOutputTokens: 65536,
|
|
1067
|
+
reasoningOutput: true,
|
|
1068
|
+
imageOutputs: false,
|
|
1069
|
+
audioOutputs: false,
|
|
1070
|
+
videoOutputs: false,
|
|
1071
|
+
toolCalling: true,
|
|
1072
|
+
structuredOutput: true
|
|
1073
|
+
},
|
|
1074
|
+
"gemini-2.5-pro": {
|
|
1075
|
+
maxInputTokens: 1048576,
|
|
1076
|
+
imageInputs: true,
|
|
1077
|
+
audioInputs: true,
|
|
1078
|
+
pdfInputs: true,
|
|
1079
|
+
videoInputs: true,
|
|
1080
|
+
maxOutputTokens: 65536,
|
|
1081
|
+
reasoningOutput: true,
|
|
1082
|
+
imageOutputs: false,
|
|
1083
|
+
audioOutputs: false,
|
|
1084
|
+
videoOutputs: false,
|
|
1085
|
+
toolCalling: true,
|
|
1086
|
+
structuredOutput: true
|
|
1087
|
+
},
|
|
1088
|
+
"gemini-2.0-flash": {
|
|
1089
|
+
maxInputTokens: 1048576,
|
|
1090
|
+
imageInputs: true,
|
|
1091
|
+
audioInputs: true,
|
|
1092
|
+
pdfInputs: true,
|
|
1093
|
+
videoInputs: true,
|
|
1094
|
+
maxOutputTokens: 8192,
|
|
1095
|
+
reasoningOutput: false,
|
|
1096
|
+
imageOutputs: false,
|
|
1097
|
+
audioOutputs: false,
|
|
1098
|
+
videoOutputs: false,
|
|
1099
|
+
toolCalling: true,
|
|
1100
|
+
structuredOutput: true
|
|
1101
|
+
},
|
|
1102
|
+
"gemini-1.5-pro": {
|
|
1103
|
+
maxInputTokens: 1e6,
|
|
1104
|
+
imageInputs: true,
|
|
1105
|
+
audioInputs: true,
|
|
1106
|
+
pdfInputs: false,
|
|
1107
|
+
videoInputs: true,
|
|
1108
|
+
maxOutputTokens: 8192,
|
|
1109
|
+
reasoningOutput: false,
|
|
1110
|
+
imageOutputs: false,
|
|
1111
|
+
audioOutputs: false,
|
|
1112
|
+
videoOutputs: false,
|
|
1113
|
+
toolCalling: true,
|
|
1114
|
+
structuredOutput: false
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
//#endregion
|
|
1118
|
+
//#region src/chat_models.ts
|
|
1119
|
+
/**
|
|
1120
|
+
* Google Generative AI chat model integration.
|
|
1121
|
+
*
|
|
1122
|
+
* Setup:
|
|
1123
|
+
* Install `@intrvls/langchain-google-genai` and set an environment variable named `GOOGLE_API_KEY`.
|
|
1124
|
+
*
|
|
1125
|
+
* ```bash
|
|
1126
|
+
* npm install @intrvls/langchain-google-genai
|
|
1127
|
+
* export GOOGLE_API_KEY="your-api-key"
|
|
1128
|
+
* ```
|
|
1129
|
+
*
|
|
1130
|
+
* ## [Constructor args](https://api.js.langchain.com/classes/langchain_google_genai.ChatGoogleGenerativeAI.html#constructor)
|
|
1131
|
+
*
|
|
1132
|
+
* ## [Runtime args](https://api.js.langchain.com/interfaces/langchain_google_genai.GoogleGenerativeAIChatCallOptions.html)
|
|
1133
|
+
*
|
|
1134
|
+
* Runtime args can be passed as the second argument to any of the base runnable methods `.invoke`. `.stream`, `.batch`, etc.
|
|
1135
|
+
* They can also be passed via `.withConfig`, or the second arg in `.bindTools`, like shown in the examples below:
|
|
1136
|
+
*
|
|
1137
|
+
* ```typescript
|
|
1138
|
+
* // When calling `.withConfig`, call options should be passed via the first argument
|
|
1139
|
+
* const llmWithArgsBound = llm.withConfig({
|
|
1140
|
+
* stop: ["\n"],
|
|
1141
|
+
* });
|
|
1142
|
+
*
|
|
1143
|
+
* // When calling `.bindTools`, call options should be passed via the second argument
|
|
1144
|
+
* const llmWithTools = llm.bindTools(
|
|
1145
|
+
* [...],
|
|
1146
|
+
* {
|
|
1147
|
+
* stop: ["\n"],
|
|
1148
|
+
* }
|
|
1149
|
+
* );
|
|
1150
|
+
* ```
|
|
1151
|
+
*
|
|
1152
|
+
* ## Examples
|
|
1153
|
+
*
|
|
1154
|
+
* <details open>
|
|
1155
|
+
* <summary><strong>Instantiate</strong></summary>
|
|
1156
|
+
*
|
|
1157
|
+
* ```typescript
|
|
1158
|
+
* import { ChatGoogleGenerativeAI } from '@intrvls/langchain-google-genai';
|
|
1159
|
+
*
|
|
1160
|
+
* const llm = new ChatGoogleGenerativeAI({
|
|
1161
|
+
* model: "gemini-3.5-flash",
|
|
1162
|
+
* temperature: 0,
|
|
1163
|
+
* maxRetries: 2,
|
|
1164
|
+
* // apiKey: "...",
|
|
1165
|
+
* // other params...
|
|
1166
|
+
* });
|
|
1167
|
+
* ```
|
|
1168
|
+
* </details>
|
|
1169
|
+
*
|
|
1170
|
+
* <br />
|
|
1171
|
+
*
|
|
1172
|
+
* <details>
|
|
1173
|
+
* <summary><strong>Invoking</strong></summary>
|
|
1174
|
+
*
|
|
1175
|
+
* ```typescript
|
|
1176
|
+
* const input = `Translate "I love programming" into French.`;
|
|
1177
|
+
*
|
|
1178
|
+
* // Models also accept a list of chat messages or a formatted prompt
|
|
1179
|
+
* const result = await llm.invoke(input);
|
|
1180
|
+
* console.log(result);
|
|
1181
|
+
* ```
|
|
1182
|
+
*
|
|
1183
|
+
* ```txt
|
|
1184
|
+
* AIMessage {
|
|
1185
|
+
* "content": "There are a few ways to translate \"I love programming\" into French, depending on the level of formality and nuance you want to convey:\n\n**Formal:**\n\n* **J'aime la programmation.** (This is the most literal and formal translation.)\n\n**Informal:**\n\n* **J'adore programmer.** (This is a more enthusiastic and informal translation.)\n* **J'aime beaucoup programmer.** (This is a slightly less enthusiastic but still informal translation.)\n\n**More specific:**\n\n* **J'aime beaucoup coder.** (This specifically refers to writing code.)\n* **J'aime beaucoup développer des logiciels.** (This specifically refers to developing software.)\n\nThe best translation will depend on the context and your intended audience. \n",
|
|
1186
|
+
* "response_metadata": {
|
|
1187
|
+
* "finishReason": "STOP",
|
|
1188
|
+
* "index": 0,
|
|
1189
|
+
* "safetyRatings": [
|
|
1190
|
+
* {
|
|
1191
|
+
* "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
1192
|
+
* "probability": "NEGLIGIBLE"
|
|
1193
|
+
* },
|
|
1194
|
+
* {
|
|
1195
|
+
* "category": "HARM_CATEGORY_HATE_SPEECH",
|
|
1196
|
+
* "probability": "NEGLIGIBLE"
|
|
1197
|
+
* },
|
|
1198
|
+
* {
|
|
1199
|
+
* "category": "HARM_CATEGORY_HARASSMENT",
|
|
1200
|
+
* "probability": "NEGLIGIBLE"
|
|
1201
|
+
* },
|
|
1202
|
+
* {
|
|
1203
|
+
* "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
1204
|
+
* "probability": "NEGLIGIBLE"
|
|
1205
|
+
* }
|
|
1206
|
+
* ]
|
|
1207
|
+
* },
|
|
1208
|
+
* "usage_metadata": {
|
|
1209
|
+
* "input_tokens": 10,
|
|
1210
|
+
* "output_tokens": 149,
|
|
1211
|
+
* "total_tokens": 159
|
|
1212
|
+
* }
|
|
1213
|
+
* }
|
|
1214
|
+
* ```
|
|
1215
|
+
* </details>
|
|
1216
|
+
*
|
|
1217
|
+
* <br />
|
|
1218
|
+
*
|
|
1219
|
+
* <details>
|
|
1220
|
+
* <summary><strong>Streaming Chunks</strong></summary>
|
|
1221
|
+
*
|
|
1222
|
+
* ```typescript
|
|
1223
|
+
* for await (const chunk of await llm.stream(input)) {
|
|
1224
|
+
* console.log(chunk);
|
|
1225
|
+
* }
|
|
1226
|
+
* ```
|
|
1227
|
+
*
|
|
1228
|
+
* ```txt
|
|
1229
|
+
* AIMessageChunk {
|
|
1230
|
+
* "content": "There",
|
|
1231
|
+
* "response_metadata": {
|
|
1232
|
+
* "index": 0
|
|
1233
|
+
* }
|
|
1234
|
+
* "usage_metadata": {
|
|
1235
|
+
* "input_tokens": 10,
|
|
1236
|
+
* "output_tokens": 1,
|
|
1237
|
+
* "total_tokens": 11
|
|
1238
|
+
* }
|
|
1239
|
+
* }
|
|
1240
|
+
* AIMessageChunk {
|
|
1241
|
+
* "content": " are a few ways to translate \"I love programming\" into French, depending on",
|
|
1242
|
+
* }
|
|
1243
|
+
* AIMessageChunk {
|
|
1244
|
+
* "content": " the level of formality and nuance you want to convey:\n\n**Formal:**\n\n",
|
|
1245
|
+
* }
|
|
1246
|
+
* AIMessageChunk {
|
|
1247
|
+
* "content": "* **J'aime la programmation.** (This is the most literal and formal translation.)\n\n**Informal:**\n\n* **J'adore programmer.** (This",
|
|
1248
|
+
* }
|
|
1249
|
+
* AIMessageChunk {
|
|
1250
|
+
* "content": " is a more enthusiastic and informal translation.)\n* **J'aime beaucoup programmer.** (This is a slightly less enthusiastic but still informal translation.)\n\n**More",
|
|
1251
|
+
* }
|
|
1252
|
+
* AIMessageChunk {
|
|
1253
|
+
* "content": " specific:**\n\n* **J'aime beaucoup coder.** (This specifically refers to writing code.)\n* **J'aime beaucoup développer des logiciels.** (This specifically refers to developing software.)\n\nThe best translation will depend on the context and",
|
|
1254
|
+
* }
|
|
1255
|
+
* AIMessageChunk {
|
|
1256
|
+
* "content": " your intended audience. \n",
|
|
1257
|
+
* }
|
|
1258
|
+
* ```
|
|
1259
|
+
* </details>
|
|
1260
|
+
*
|
|
1261
|
+
* <br />
|
|
1262
|
+
*
|
|
1263
|
+
* <details>
|
|
1264
|
+
* <summary><strong>Aggregate Streamed Chunks</strong></summary>
|
|
1265
|
+
*
|
|
1266
|
+
* ```typescript
|
|
1267
|
+
* import { AIMessageChunk } from '@langchain/core/messages';
|
|
1268
|
+
* import { concat } from '@langchain/core/utils/stream';
|
|
1269
|
+
*
|
|
1270
|
+
* const stream = await llm.stream(input);
|
|
1271
|
+
* let full: AIMessageChunk | undefined;
|
|
1272
|
+
* for await (const chunk of stream) {
|
|
1273
|
+
* full = !full ? chunk : concat(full, chunk);
|
|
1274
|
+
* }
|
|
1275
|
+
* console.log(full);
|
|
1276
|
+
* ```
|
|
1277
|
+
*
|
|
1278
|
+
* ```txt
|
|
1279
|
+
* AIMessageChunk {
|
|
1280
|
+
* "content": "There are a few ways to translate \"I love programming\" into French, depending on the level of formality and nuance you want to convey:\n\n**Formal:**\n\n* **J'aime la programmation.** (This is the most literal and formal translation.)\n\n**Informal:**\n\n* **J'adore programmer.** (This is a more enthusiastic and informal translation.)\n* **J'aime beaucoup programmer.** (This is a slightly less enthusiastic but still informal translation.)\n\n**More specific:**\n\n* **J'aime beaucoup coder.** (This specifically refers to writing code.)\n* **J'aime beaucoup développer des logiciels.** (This specifically refers to developing software.)\n\nThe best translation will depend on the context and your intended audience. \n",
|
|
1281
|
+
* "usage_metadata": {
|
|
1282
|
+
* "input_tokens": 10,
|
|
1283
|
+
* "output_tokens": 277,
|
|
1284
|
+
* "total_tokens": 287
|
|
1285
|
+
* }
|
|
1286
|
+
* }
|
|
1287
|
+
* ```
|
|
1288
|
+
* </details>
|
|
1289
|
+
*
|
|
1290
|
+
* <br />
|
|
1291
|
+
*
|
|
1292
|
+
* <details>
|
|
1293
|
+
* <summary><strong>Bind tools</strong></summary>
|
|
1294
|
+
*
|
|
1295
|
+
* ```typescript
|
|
1296
|
+
* import { z } from 'zod';
|
|
1297
|
+
*
|
|
1298
|
+
* const GetWeather = {
|
|
1299
|
+
* name: "GetWeather",
|
|
1300
|
+
* description: "Get the current weather in a given location",
|
|
1301
|
+
* schema: z.object({
|
|
1302
|
+
* location: z.string().describe("The city and state, e.g. San Francisco, CA")
|
|
1303
|
+
* }),
|
|
1304
|
+
* }
|
|
1305
|
+
*
|
|
1306
|
+
* const GetPopulation = {
|
|
1307
|
+
* name: "GetPopulation",
|
|
1308
|
+
* description: "Get the current population in a given location",
|
|
1309
|
+
* schema: z.object({
|
|
1310
|
+
* location: z.string().describe("The city and state, e.g. San Francisco, CA")
|
|
1311
|
+
* }),
|
|
1312
|
+
* }
|
|
1313
|
+
*
|
|
1314
|
+
* const llmWithTools = llm.bindTools([GetWeather, GetPopulation]);
|
|
1315
|
+
* const aiMsg = await llmWithTools.invoke(
|
|
1316
|
+
* "Which city is hotter today and which is bigger: LA or NY?"
|
|
1317
|
+
* );
|
|
1318
|
+
* console.log(aiMsg.tool_calls);
|
|
1319
|
+
* ```
|
|
1320
|
+
*
|
|
1321
|
+
* ```txt
|
|
1322
|
+
* [
|
|
1323
|
+
* {
|
|
1324
|
+
* name: 'GetWeather',
|
|
1325
|
+
* args: { location: 'Los Angeles, CA' },
|
|
1326
|
+
* type: 'tool_call'
|
|
1327
|
+
* },
|
|
1328
|
+
* {
|
|
1329
|
+
* name: 'GetWeather',
|
|
1330
|
+
* args: { location: 'New York, NY' },
|
|
1331
|
+
* type: 'tool_call'
|
|
1332
|
+
* },
|
|
1333
|
+
* {
|
|
1334
|
+
* name: 'GetPopulation',
|
|
1335
|
+
* args: { location: 'Los Angeles, CA' },
|
|
1336
|
+
* type: 'tool_call'
|
|
1337
|
+
* },
|
|
1338
|
+
* {
|
|
1339
|
+
* name: 'GetPopulation',
|
|
1340
|
+
* args: { location: 'New York, NY' },
|
|
1341
|
+
* type: 'tool_call'
|
|
1342
|
+
* }
|
|
1343
|
+
* ]
|
|
1344
|
+
* ```
|
|
1345
|
+
* </details>
|
|
1346
|
+
*
|
|
1347
|
+
* <br />
|
|
1348
|
+
*
|
|
1349
|
+
* <details>
|
|
1350
|
+
* <summary><strong>Structured Output</strong></summary>
|
|
1351
|
+
*
|
|
1352
|
+
* ```typescript
|
|
1353
|
+
* const Joke = z.object({
|
|
1354
|
+
* setup: z.string().describe("The setup of the joke"),
|
|
1355
|
+
* punchline: z.string().describe("The punchline to the joke"),
|
|
1356
|
+
* rating: z.number().optional().describe("How funny the joke is, from 1 to 10")
|
|
1357
|
+
* }).describe('Joke to tell user.');
|
|
1358
|
+
*
|
|
1359
|
+
* const structuredLlm = llm.withStructuredOutput(Joke, { name: "Joke" });
|
|
1360
|
+
* const jokeResult = await structuredLlm.invoke("Tell me a joke about cats");
|
|
1361
|
+
* console.log(jokeResult);
|
|
1362
|
+
* ```
|
|
1363
|
+
*
|
|
1364
|
+
* ```txt
|
|
1365
|
+
* {
|
|
1366
|
+
* setup: "Why don\\'t cats play poker?",
|
|
1367
|
+
* punchline: "Why don\\'t cats play poker? Because they always have an ace up their sleeve!"
|
|
1368
|
+
* }
|
|
1369
|
+
* ```
|
|
1370
|
+
* </details>
|
|
1371
|
+
*
|
|
1372
|
+
* <br />
|
|
1373
|
+
*
|
|
1374
|
+
* <details>
|
|
1375
|
+
* <summary><strong>Multimodal</strong></summary>
|
|
1376
|
+
*
|
|
1377
|
+
* ```typescript
|
|
1378
|
+
* import { HumanMessage } from '@langchain/core/messages';
|
|
1379
|
+
*
|
|
1380
|
+
* const imageUrl = "https://example.com/image.jpg";
|
|
1381
|
+
* const imageData = await fetch(imageUrl).then(res => res.arrayBuffer());
|
|
1382
|
+
* const base64Image = Buffer.from(imageData).toString('base64');
|
|
1383
|
+
*
|
|
1384
|
+
* const message = new HumanMessage({
|
|
1385
|
+
* content: [
|
|
1386
|
+
* { type: "text", text: "describe the weather in this image" },
|
|
1387
|
+
* {
|
|
1388
|
+
* type: "image_url",
|
|
1389
|
+
* image_url: { url: `data:image/jpeg;base64,${base64Image}` },
|
|
1390
|
+
* },
|
|
1391
|
+
* ]
|
|
1392
|
+
* });
|
|
1393
|
+
*
|
|
1394
|
+
* const imageDescriptionAiMsg = await llm.invoke([message]);
|
|
1395
|
+
* console.log(imageDescriptionAiMsg.content);
|
|
1396
|
+
* ```
|
|
1397
|
+
*
|
|
1398
|
+
* ```txt
|
|
1399
|
+
* The weather in the image appears to be clear and sunny. The sky is mostly blue with a few scattered white clouds, indicating fair weather. The bright sunlight is casting shadows on the green, grassy hill, suggesting it is a pleasant day with good visibility. There are no signs of rain or stormy conditions.
|
|
1400
|
+
* ```
|
|
1401
|
+
* </details>
|
|
1402
|
+
*
|
|
1403
|
+
* <br />
|
|
1404
|
+
*
|
|
1405
|
+
* <details>
|
|
1406
|
+
* <summary><strong>Usage Metadata</strong></summary>
|
|
1407
|
+
*
|
|
1408
|
+
* ```typescript
|
|
1409
|
+
* const aiMsgForMetadata = await llm.invoke(input);
|
|
1410
|
+
* console.log(aiMsgForMetadata.usage_metadata);
|
|
1411
|
+
* ```
|
|
1412
|
+
*
|
|
1413
|
+
* ```txt
|
|
1414
|
+
* { input_tokens: 10, output_tokens: 149, total_tokens: 159 }
|
|
1415
|
+
* ```
|
|
1416
|
+
* </details>
|
|
1417
|
+
*
|
|
1418
|
+
* <br />
|
|
1419
|
+
*
|
|
1420
|
+
* <details>
|
|
1421
|
+
* <summary><strong>Response Metadata</strong></summary>
|
|
1422
|
+
*
|
|
1423
|
+
* ```typescript
|
|
1424
|
+
* const aiMsgForResponseMetadata = await llm.invoke(input);
|
|
1425
|
+
* console.log(aiMsgForResponseMetadata.response_metadata);
|
|
1426
|
+
* ```
|
|
1427
|
+
*
|
|
1428
|
+
* ```txt
|
|
1429
|
+
* {
|
|
1430
|
+
* finishReason: 'STOP',
|
|
1431
|
+
* index: 0,
|
|
1432
|
+
* safetyRatings: [
|
|
1433
|
+
* {
|
|
1434
|
+
* category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
1435
|
+
* probability: 'NEGLIGIBLE'
|
|
1436
|
+
* },
|
|
1437
|
+
* {
|
|
1438
|
+
* category: 'HARM_CATEGORY_HATE_SPEECH',
|
|
1439
|
+
* probability: 'NEGLIGIBLE'
|
|
1440
|
+
* },
|
|
1441
|
+
* { category: 'HARM_CATEGORY_HARASSMENT', probability: 'NEGLIGIBLE' },
|
|
1442
|
+
* {
|
|
1443
|
+
* category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
|
|
1444
|
+
* probability: 'NEGLIGIBLE'
|
|
1445
|
+
* }
|
|
1446
|
+
* ]
|
|
1447
|
+
* }
|
|
1448
|
+
* ```
|
|
1449
|
+
* </details>
|
|
1450
|
+
*
|
|
1451
|
+
* <br />
|
|
1452
|
+
*
|
|
1453
|
+
* <details>
|
|
1454
|
+
* <summary><strong>Document Messages</strong></summary>
|
|
1455
|
+
*
|
|
1456
|
+
* This example will show you how to pass documents such as PDFs to Google
|
|
1457
|
+
* Generative AI through messages.
|
|
1458
|
+
*
|
|
1459
|
+
* ```typescript
|
|
1460
|
+
* const pdfPath = "/Users/my_user/Downloads/invoice.pdf";
|
|
1461
|
+
* const pdfBase64 = await fs.readFile(pdfPath, "base64");
|
|
1462
|
+
*
|
|
1463
|
+
* const response = await llm.invoke([
|
|
1464
|
+
* ["system", "Use the provided documents to answer the question"],
|
|
1465
|
+
* [
|
|
1466
|
+
* "user",
|
|
1467
|
+
* [
|
|
1468
|
+
* {
|
|
1469
|
+
* type: "application/pdf", // If the `type` field includes a single slash (`/`), it will be treated as inline data.
|
|
1470
|
+
* data: pdfBase64,
|
|
1471
|
+
* },
|
|
1472
|
+
* {
|
|
1473
|
+
* type: "text",
|
|
1474
|
+
* text: "Summarize the contents of this PDF",
|
|
1475
|
+
* },
|
|
1476
|
+
* ],
|
|
1477
|
+
* ],
|
|
1478
|
+
* ]);
|
|
1479
|
+
*
|
|
1480
|
+
* console.log(response.content);
|
|
1481
|
+
* ```
|
|
1482
|
+
*
|
|
1483
|
+
* ```txt
|
|
1484
|
+
* This is a billing invoice from Twitter Developers for X API Basic Access. The transaction date is January 7, 2025,
|
|
1485
|
+
* and the amount is $194.34, which has been paid. The subscription period is from January 7, 2025 21:02 to February 7, 2025 00:00 (UTC).
|
|
1486
|
+
* The tax is $0.00, with a tax rate of 0%. The total amount is $194.34. The payment was made using a Visa card ending in 7022,
|
|
1487
|
+
* expiring in 12/2026. The billing address is Brace Sproul, 1234 Main Street, San Francisco, CA, US 94103. The company being billed is
|
|
1488
|
+
* X Corp, located at 865 FM 1209 Building 2, Bastrop, TX, US 78602. Terms and conditions apply.
|
|
1489
|
+
* ```
|
|
1490
|
+
* </details>
|
|
1491
|
+
*
|
|
1492
|
+
* <br />
|
|
1493
|
+
*/
|
|
1494
|
+
/**
|
|
1495
|
+
* Whether the given model id is Gemini 3 or a later major version. Strips an
|
|
1496
|
+
* optional `models/` prefix and reads the major version number, so
|
|
1497
|
+
* `gemini-3.1-pro-preview`, `gemini-3.5-flash`, and a future `gemini-4-*` all
|
|
1498
|
+
* return true, while `gemini-2.5-flash` returns false.
|
|
1499
|
+
*/
|
|
1500
|
+
function isGemini3OrLater(model) {
|
|
1501
|
+
const match = model.toLowerCase().replace(/^models\//, "").match(/gemini-(\d+)/);
|
|
1502
|
+
return match ? Number(match[1]) >= 3 : false;
|
|
1503
|
+
}
|
|
1504
|
+
var ChatGoogleGenerativeAI = class extends _langchain_core_language_models_chat_models.BaseChatModel {
|
|
1505
|
+
static lc_name() {
|
|
1506
|
+
return "ChatGoogleGenerativeAI";
|
|
1507
|
+
}
|
|
1508
|
+
lc_serializable = true;
|
|
1509
|
+
get lc_secrets() {
|
|
1510
|
+
return { apiKey: "GOOGLE_API_KEY" };
|
|
1511
|
+
}
|
|
1512
|
+
lc_namespace = [
|
|
1513
|
+
"langchain",
|
|
1514
|
+
"chat_models",
|
|
1515
|
+
"google_genai"
|
|
1516
|
+
];
|
|
1517
|
+
get lc_aliases() {
|
|
1518
|
+
return { apiKey: "google_api_key" };
|
|
1519
|
+
}
|
|
1520
|
+
model;
|
|
1521
|
+
temperature;
|
|
1522
|
+
maxOutputTokens;
|
|
1523
|
+
topP;
|
|
1524
|
+
topK;
|
|
1525
|
+
stopSequences = [];
|
|
1526
|
+
safetySettings;
|
|
1527
|
+
apiKey;
|
|
1528
|
+
streaming = false;
|
|
1529
|
+
json;
|
|
1530
|
+
streamUsage = true;
|
|
1531
|
+
convertSystemMessageToHumanContent;
|
|
1532
|
+
thinkingConfig;
|
|
1533
|
+
cachedContent;
|
|
1534
|
+
client;
|
|
1535
|
+
get _isMultimodalModel() {
|
|
1536
|
+
return this.model.includes("vision") || this.model.startsWith("gemini-1.5") || this.model.startsWith("gemini-2") || this.model.startsWith("gemma-3-") && !this.model.startsWith("gemma-3-1b") || this.model.startsWith("gemini-3");
|
|
1537
|
+
}
|
|
1538
|
+
constructor(modelOrFields, fieldsArg) {
|
|
1539
|
+
const fields = typeof modelOrFields === "string" ? {
|
|
1540
|
+
...fieldsArg ?? {},
|
|
1541
|
+
model: modelOrFields
|
|
1542
|
+
} : modelOrFields;
|
|
1543
|
+
super(fields);
|
|
1544
|
+
this._addVersion("@intrvls/langchain-google-genai", "3.0.0-alpha.0");
|
|
1545
|
+
this.model = fields.model.replace(/^models\//, "");
|
|
1546
|
+
this.maxOutputTokens = fields.maxOutputTokens ?? this.maxOutputTokens;
|
|
1547
|
+
if (this.maxOutputTokens && this.maxOutputTokens < 0) throw new Error("`maxOutputTokens` must be a positive integer");
|
|
1548
|
+
this.temperature = fields.temperature ?? this.temperature;
|
|
1549
|
+
if (this.temperature && (this.temperature < 0 || this.temperature > 2)) throw new Error("`temperature` must be in the range of [0.0,2.0]");
|
|
1550
|
+
if (fields.temperature === void 0 && isGemini3OrLater(this.model)) this.temperature = 1;
|
|
1551
|
+
this.topP = fields.topP ?? this.topP;
|
|
1552
|
+
if (this.topP && this.topP < 0) throw new Error("`topP` must be a positive integer");
|
|
1553
|
+
if (this.topP && this.topP > 1) throw new Error("`topP` must be below 1.");
|
|
1554
|
+
this.topK = fields.topK ?? this.topK;
|
|
1555
|
+
if (this.topK && this.topK < 0) throw new Error("`topK` must be a positive integer");
|
|
1556
|
+
this.stopSequences = fields.stopSequences ?? this.stopSequences;
|
|
1557
|
+
this.apiKey = fields.apiKey ?? (0, _langchain_core_utils_env.getEnvironmentVariable)("GOOGLE_API_KEY") ?? (0, _langchain_core_utils_env.getEnvironmentVariable)("GEMINI_API_KEY");
|
|
1558
|
+
if (!this.apiKey) throw new Error("Please set an API key for Google GenerativeAI in the environment variable GOOGLE_API_KEY (or GEMINI_API_KEY) or in the `apiKey` field of the ChatGoogleGenerativeAI constructor");
|
|
1559
|
+
this.safetySettings = fields.safetySettings ?? this.safetySettings;
|
|
1560
|
+
if (this.safetySettings && this.safetySettings.length > 0) {
|
|
1561
|
+
if (new Set(this.safetySettings.map((s) => s.category)).size !== this.safetySettings.length) throw new Error("The categories in `safetySettings` array must be unique");
|
|
1562
|
+
}
|
|
1563
|
+
this.streaming = fields.streaming ?? this.streaming;
|
|
1564
|
+
this.json = fields.json;
|
|
1565
|
+
this.thinkingConfig = fields.thinkingConfig ?? this.thinkingConfig;
|
|
1566
|
+
this.cachedContent = fields.cachedContent ?? this.cachedContent;
|
|
1567
|
+
this.client = new _google_genai.GoogleGenAI({
|
|
1568
|
+
apiKey: this.apiKey,
|
|
1569
|
+
httpOptions: {
|
|
1570
|
+
apiVersion: fields.apiVersion,
|
|
1571
|
+
baseUrl: fields.baseUrl,
|
|
1572
|
+
headers: fields.customHeaders
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
this.streamUsage = fields.streamUsage ?? this.streamUsage;
|
|
1576
|
+
}
|
|
1577
|
+
get useSystemInstruction() {
|
|
1578
|
+
return typeof this.convertSystemMessageToHumanContent === "boolean" ? !this.convertSystemMessageToHumanContent : this.computeUseSystemInstruction;
|
|
1579
|
+
}
|
|
1580
|
+
get computeUseSystemInstruction() {
|
|
1581
|
+
if (this.model === "gemini-1.0-pro-001") return false;
|
|
1582
|
+
else if (this.model.startsWith("gemini-pro-vision")) return false;
|
|
1583
|
+
else if (this.model.startsWith("gemini-1.0-pro-vision")) return false;
|
|
1584
|
+
else if (this.model === "gemini-pro") return false;
|
|
1585
|
+
return true;
|
|
1586
|
+
}
|
|
1587
|
+
getLsParams(options) {
|
|
1588
|
+
return {
|
|
1589
|
+
ls_provider: "google_genai",
|
|
1590
|
+
ls_model_name: this.model,
|
|
1591
|
+
ls_model_type: "chat",
|
|
1592
|
+
ls_temperature: this.temperature,
|
|
1593
|
+
ls_max_tokens: this.maxOutputTokens,
|
|
1594
|
+
ls_stop: options.stop
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
_combineLLMOutput() {
|
|
1598
|
+
return [];
|
|
1599
|
+
}
|
|
1600
|
+
_llmType() {
|
|
1601
|
+
return "googlegenerativeai";
|
|
1602
|
+
}
|
|
1603
|
+
bindTools(tools, kwargs) {
|
|
1604
|
+
return this.withConfig({
|
|
1605
|
+
tools: convertToolsToGenAI(tools)?.tools,
|
|
1606
|
+
...kwargs
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
invocationParams(options) {
|
|
1610
|
+
const toolsAndConfig = options?.tools?.length ? convertToolsToGenAI(options.tools, {
|
|
1611
|
+
toolChoice: options.tool_choice,
|
|
1612
|
+
allowedFunctionNames: options.allowedFunctionNames
|
|
1613
|
+
}) : void 0;
|
|
1614
|
+
return {
|
|
1615
|
+
...toolsAndConfig?.tools ? { tools: toolsAndConfig.tools } : {},
|
|
1616
|
+
...toolsAndConfig?.toolConfig ? { toolConfig: toolsAndConfig.toolConfig } : {},
|
|
1617
|
+
...this.safetySettings ? { safetySettings: this.safetySettings } : {},
|
|
1618
|
+
...this.cachedContent ? { cachedContent: this.cachedContent } : {},
|
|
1619
|
+
stopSequences: this.stopSequences,
|
|
1620
|
+
maxOutputTokens: this.maxOutputTokens,
|
|
1621
|
+
temperature: this.temperature,
|
|
1622
|
+
topP: this.topP,
|
|
1623
|
+
topK: this.topK,
|
|
1624
|
+
...this.json ? { responseMimeType: "application/json" } : {},
|
|
1625
|
+
...this.thinkingConfig ? { thinkingConfig: this.thinkingConfig } : {},
|
|
1626
|
+
...options?.responseSchema ? {
|
|
1627
|
+
responseSchema: options.responseSchema,
|
|
1628
|
+
responseMimeType: "application/json"
|
|
1629
|
+
} : {}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
_buildGenerateContentRequest(messages, options) {
|
|
1633
|
+
const prompt = convertBaseMessagesToContent(messages, this._isMultimodalModel, this.useSystemInstruction, this.model);
|
|
1634
|
+
let actualPrompt = prompt;
|
|
1635
|
+
let systemInstruction;
|
|
1636
|
+
if (prompt[0]?.role === "system") [systemInstruction, ...actualPrompt] = prompt;
|
|
1637
|
+
const config = this.invocationParams(options);
|
|
1638
|
+
return {
|
|
1639
|
+
model: this.model,
|
|
1640
|
+
contents: actualPrompt,
|
|
1641
|
+
config: {
|
|
1642
|
+
...config,
|
|
1643
|
+
...systemInstruction ? { systemInstruction } : {},
|
|
1644
|
+
...options.signal ? { abortSignal: options.signal } : {}
|
|
1645
|
+
}
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
async _generate(messages, options, runManager) {
|
|
1649
|
+
options.signal?.throwIfAborted();
|
|
1650
|
+
if (this.streaming) {
|
|
1651
|
+
const tokenUsage = {};
|
|
1652
|
+
const stream = this._streamResponseChunks(messages, options, runManager);
|
|
1653
|
+
const finalChunks = [];
|
|
1654
|
+
for await (const chunk of stream) {
|
|
1655
|
+
const index = chunk.generationInfo?.completion ?? 0;
|
|
1656
|
+
if (finalChunks[index] === void 0) finalChunks[index] = chunk;
|
|
1657
|
+
else finalChunks[index] = finalChunks[index].concat(chunk);
|
|
1658
|
+
}
|
|
1659
|
+
return {
|
|
1660
|
+
generations: finalChunks.filter((c) => c !== void 0),
|
|
1661
|
+
llmOutput: { estimatedTokenUsage: tokenUsage }
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
const res = await this.completionWithRetry(this._buildGenerateContentRequest(messages, options));
|
|
1665
|
+
let usageMetadata;
|
|
1666
|
+
if (res.usageMetadata) usageMetadata = convertUsageMetadata(res.usageMetadata, this.model);
|
|
1667
|
+
const generationResult = mapGenerateContentResultToChatResult(res, { usageMetadata });
|
|
1668
|
+
if (generationResult.generations?.length > 0) await runManager?.handleLLMNewToken(generationResult.generations[0]?.text ?? "");
|
|
1669
|
+
return generationResult;
|
|
1670
|
+
}
|
|
1671
|
+
async *_streamResponseChunks(messages, options, runManager) {
|
|
1672
|
+
const request = this._buildGenerateContentRequest(messages, options);
|
|
1673
|
+
const stream = await this.caller.callWithOptions({ signal: options?.signal }, async () => {
|
|
1674
|
+
return this.client.models.generateContentStream(request);
|
|
1675
|
+
});
|
|
1676
|
+
let usageMetadata;
|
|
1677
|
+
let prevPromptTokenCount = 0;
|
|
1678
|
+
let prevCandidatesTokenCount = 0;
|
|
1679
|
+
let prevThoughtsTokenCount = 0;
|
|
1680
|
+
let prevTotalTokenCount = 0;
|
|
1681
|
+
let index = 0;
|
|
1682
|
+
for await (const response of stream) {
|
|
1683
|
+
if (options.signal?.aborted) return;
|
|
1684
|
+
if ("usageMetadata" in response && response.usageMetadata !== void 0 && this.streamUsage !== false && options.streamUsage !== false) {
|
|
1685
|
+
usageMetadata = convertUsageMetadata(response.usageMetadata, this.model);
|
|
1686
|
+
const newPromptTokenCount = response.usageMetadata.promptTokenCount ?? 0;
|
|
1687
|
+
usageMetadata.input_tokens = Math.max(0, newPromptTokenCount - prevPromptTokenCount);
|
|
1688
|
+
prevPromptTokenCount = newPromptTokenCount;
|
|
1689
|
+
const newCandidatesTokenCount = response.usageMetadata.candidatesTokenCount ?? 0;
|
|
1690
|
+
const candidatesDelta = Math.max(0, newCandidatesTokenCount - prevCandidatesTokenCount);
|
|
1691
|
+
prevCandidatesTokenCount = newCandidatesTokenCount;
|
|
1692
|
+
const newThoughtsTokenCount = response.usageMetadata.thoughtsTokenCount ?? 0;
|
|
1693
|
+
const reasoningDelta = Math.max(0, newThoughtsTokenCount - prevThoughtsTokenCount);
|
|
1694
|
+
prevThoughtsTokenCount = newThoughtsTokenCount;
|
|
1695
|
+
usageMetadata.output_tokens = candidatesDelta + reasoningDelta;
|
|
1696
|
+
usageMetadata.output_token_details = reasoningDelta ? {
|
|
1697
|
+
...usageMetadata.output_token_details,
|
|
1698
|
+
reasoning: reasoningDelta
|
|
1699
|
+
} : usageMetadata.output_token_details;
|
|
1700
|
+
const newTotalTokenCount = response.usageMetadata.totalTokenCount ?? 0;
|
|
1701
|
+
usageMetadata.total_tokens = Math.max(0, newTotalTokenCount - prevTotalTokenCount);
|
|
1702
|
+
prevTotalTokenCount = newTotalTokenCount;
|
|
1703
|
+
}
|
|
1704
|
+
const chunk = convertResponseContentToChatGenerationChunk(response, {
|
|
1705
|
+
usageMetadata,
|
|
1706
|
+
index
|
|
1707
|
+
});
|
|
1708
|
+
index += 1;
|
|
1709
|
+
if (!chunk) continue;
|
|
1710
|
+
yield chunk;
|
|
1711
|
+
await runManager?.handleLLMNewToken(chunk.text ?? "");
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
async completionWithRetry(request, options) {
|
|
1715
|
+
return this.caller.callWithOptions({ signal: options?.signal }, async () => {
|
|
1716
|
+
try {
|
|
1717
|
+
return await this.client.models.generateContent(request);
|
|
1718
|
+
} catch (e) {
|
|
1719
|
+
if (e.message?.includes("400 Bad Request")) e.status = 400;
|
|
1720
|
+
throw e;
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Return profiling information for the model.
|
|
1726
|
+
*
|
|
1727
|
+
* Provides information about the model's capabilities and constraints,
|
|
1728
|
+
* including token limits, multimodal support, and advanced features like
|
|
1729
|
+
* tool calling and structured output.
|
|
1730
|
+
*
|
|
1731
|
+
* @returns {ModelProfile} An object describing the model's capabilities and constraints
|
|
1732
|
+
*
|
|
1733
|
+
* @example
|
|
1734
|
+
* ```typescript
|
|
1735
|
+
* const model = new ChatGoogleGenerativeAI({ model: "gemini-2.5-flash" });
|
|
1736
|
+
* const profile = model.profile;
|
|
1737
|
+
* console.log(profile.maxInputTokens); // 1048576
|
|
1738
|
+
* console.log(profile.imageInputs); // true
|
|
1739
|
+
* ```
|
|
1740
|
+
*/
|
|
1741
|
+
get profile() {
|
|
1742
|
+
return PROFILES[this.model] ?? {};
|
|
1743
|
+
}
|
|
1744
|
+
withStructuredOutput(outputSchema, config) {
|
|
1745
|
+
const schema = outputSchema;
|
|
1746
|
+
const name = config?.name;
|
|
1747
|
+
const method = config?.method;
|
|
1748
|
+
const includeRaw = config?.includeRaw;
|
|
1749
|
+
if (method === "jsonMode") throw new Error(`ChatGoogleGenerativeAI only supports "jsonSchema" or "functionCalling" as a method.`);
|
|
1750
|
+
let llm;
|
|
1751
|
+
let outputParser;
|
|
1752
|
+
if (method === "functionCalling") {
|
|
1753
|
+
let functionName = name ?? "extract";
|
|
1754
|
+
let geminiFunctionDeclaration;
|
|
1755
|
+
if ((0, _langchain_core_utils_types.isInteropZodSchema)(schema) || (0, _langchain_core_utils_standard_schema.isSerializableSchema)(schema)) {
|
|
1756
|
+
const jsonSchema = schemaToGenerativeAIParameters(schema);
|
|
1757
|
+
geminiFunctionDeclaration = {
|
|
1758
|
+
name: functionName,
|
|
1759
|
+
description: jsonSchema.description ?? "A function available to call.",
|
|
1760
|
+
parameters: jsonSchema
|
|
1761
|
+
};
|
|
1762
|
+
} else if (typeof schema.name === "string" && typeof schema.parameters === "object" && schema.parameters != null) {
|
|
1763
|
+
geminiFunctionDeclaration = schema;
|
|
1764
|
+
geminiFunctionDeclaration.parameters = removeAdditionalProperties(schema.parameters);
|
|
1765
|
+
functionName = schema.name;
|
|
1766
|
+
} else geminiFunctionDeclaration = {
|
|
1767
|
+
name: functionName,
|
|
1768
|
+
description: schema.description ?? "",
|
|
1769
|
+
parameters: removeAdditionalProperties(schema)
|
|
1770
|
+
};
|
|
1771
|
+
const tools = [{ functionDeclarations: [geminiFunctionDeclaration] }];
|
|
1772
|
+
llm = this.bindTools(tools).withConfig({ allowedFunctionNames: [functionName] });
|
|
1773
|
+
outputParser = (0, _langchain_core_language_models_structured_output.createFunctionCallingParser)(schema, functionName, GoogleGenerativeAIToolsOutputParser);
|
|
1774
|
+
} else {
|
|
1775
|
+
const jsonSchema = schemaToGenerativeAIParameters(schema);
|
|
1776
|
+
llm = this.withConfig({ responseSchema: jsonSchema });
|
|
1777
|
+
outputParser = (0, _langchain_core_language_models_structured_output.createContentParser)(schema);
|
|
1778
|
+
}
|
|
1779
|
+
return (0, _langchain_core_language_models_structured_output.assembleStructuredOutputPipeline)(llm, outputParser, includeRaw, includeRaw ? "StructuredOutputRunnable" : "ChatGoogleGenerativeAIStructuredOutput");
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
//#endregion
|
|
1783
|
+
//#region src/embeddings.ts
|
|
1784
|
+
/**
|
|
1785
|
+
* Class that extends the Embeddings class and provides methods for
|
|
1786
|
+
* generating embeddings using the Google Palm API.
|
|
1787
|
+
* @example
|
|
1788
|
+
* ```typescript
|
|
1789
|
+
* const model = new GoogleGenerativeAIEmbeddings({
|
|
1790
|
+
* apiKey: "<YOUR API KEY>",
|
|
1791
|
+
* modelName: "embedding-001",
|
|
1792
|
+
* });
|
|
1793
|
+
*
|
|
1794
|
+
* // Embed a single query
|
|
1795
|
+
* const res = await model.embedQuery(
|
|
1796
|
+
* "What would be a good company name for a company that makes colorful socks?"
|
|
1797
|
+
* );
|
|
1798
|
+
* console.log({ res });
|
|
1799
|
+
*
|
|
1800
|
+
* // Embed multiple documents
|
|
1801
|
+
* const documentRes = await model.embedDocuments(["Hello world", "Bye bye"]);
|
|
1802
|
+
* console.log({ documentRes });
|
|
1803
|
+
* ```
|
|
1804
|
+
*/
|
|
1805
|
+
var GoogleGenerativeAIEmbeddings = class extends _langchain_core_embeddings.Embeddings {
|
|
1806
|
+
apiKey;
|
|
1807
|
+
modelName = "gemini-embedding-001";
|
|
1808
|
+
model = "gemini-embedding-001";
|
|
1809
|
+
taskType;
|
|
1810
|
+
title;
|
|
1811
|
+
stripNewLines = true;
|
|
1812
|
+
maxBatchSize = 100;
|
|
1813
|
+
client;
|
|
1814
|
+
constructor(fields) {
|
|
1815
|
+
super(fields ?? {});
|
|
1816
|
+
this.modelName = fields?.model?.replace(/^models\//, "") ?? fields?.modelName?.replace(/^models\//, "") ?? this.modelName;
|
|
1817
|
+
this.model = this.modelName;
|
|
1818
|
+
this.taskType = fields?.taskType ?? this.taskType;
|
|
1819
|
+
this.title = fields?.title ?? this.title;
|
|
1820
|
+
if (this.title && this.taskType !== "RETRIEVAL_DOCUMENT") throw new Error("title can only be sepcified with TaskType.RETRIEVAL_DOCUMENT");
|
|
1821
|
+
this.apiKey = fields?.apiKey ?? (0, _langchain_core_utils_env.getEnvironmentVariable)("GOOGLE_API_KEY") ?? (0, _langchain_core_utils_env.getEnvironmentVariable)("GEMINI_API_KEY");
|
|
1822
|
+
if (!this.apiKey) throw new Error("Please set an API key for Google GenerativeAI in the environment variable GOOGLE_API_KEY (or GEMINI_API_KEY) or in the `apiKey` field of the GoogleGenerativeAIEmbeddings constructor");
|
|
1823
|
+
this.client = new _google_genai.GoogleGenAI({
|
|
1824
|
+
apiKey: this.apiKey,
|
|
1825
|
+
httpOptions: { baseUrl: fields?.baseUrl }
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
_cleanText(text) {
|
|
1829
|
+
return this.stripNewLines ? text.replace(/\n/g, " ") : text;
|
|
1830
|
+
}
|
|
1831
|
+
get _embedConfig() {
|
|
1832
|
+
return {
|
|
1833
|
+
taskType: this.taskType,
|
|
1834
|
+
title: this.title
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
async _embedQueryContent(text) {
|
|
1838
|
+
return (await this.client.models.embedContent({
|
|
1839
|
+
model: this.model,
|
|
1840
|
+
contents: [this._cleanText(text)],
|
|
1841
|
+
config: this._embedConfig
|
|
1842
|
+
})).embeddings?.[0]?.values ?? [];
|
|
1843
|
+
}
|
|
1844
|
+
async _embedDocumentsContent(documents) {
|
|
1845
|
+
const batchEmbedChunks = (0, _langchain_core_utils_chunk_array.chunkArray)(documents, this.maxBatchSize);
|
|
1846
|
+
return (await Promise.allSettled(batchEmbedChunks.map((chunk) => this.client.models.embedContent({
|
|
1847
|
+
model: this.model,
|
|
1848
|
+
contents: chunk.map((doc) => this._cleanText(doc)),
|
|
1849
|
+
config: this._embedConfig
|
|
1850
|
+
})))).flatMap((res, idx) => {
|
|
1851
|
+
if (res.status === "fulfilled") return (res.value.embeddings ?? []).map((e) => e.values ?? []);
|
|
1852
|
+
else return Array(batchEmbedChunks[idx].length).fill([]);
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Method that takes a document as input and returns a promise that
|
|
1857
|
+
* resolves to an embedding for the document. It calls the _embedText
|
|
1858
|
+
* method with the document as the input.
|
|
1859
|
+
* @param document Document for which to generate an embedding.
|
|
1860
|
+
* @returns Promise that resolves to an embedding for the input document.
|
|
1861
|
+
*/
|
|
1862
|
+
embedQuery(document) {
|
|
1863
|
+
return this.caller.call(this._embedQueryContent.bind(this), document);
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Method that takes an array of documents as input and returns a promise
|
|
1867
|
+
* that resolves to a 2D array of embeddings for each document. It calls
|
|
1868
|
+
* the _embedText method for each document in the array.
|
|
1869
|
+
* @param documents Array of documents for which to generate embeddings.
|
|
1870
|
+
* @returns Promise that resolves to a 2D array of embeddings for each input document.
|
|
1871
|
+
*/
|
|
1872
|
+
embedDocuments(documents) {
|
|
1873
|
+
return this.caller.call(this._embedDocumentsContent.bind(this), documents);
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
//#endregion
|
|
1877
|
+
exports.ChatGoogleGenerativeAI = ChatGoogleGenerativeAI;
|
|
1878
|
+
exports.GoogleGenerativeAIEmbeddings = GoogleGenerativeAIEmbeddings;
|
|
1879
|
+
|
|
1880
|
+
//# sourceMappingURL=index.cjs.map
|