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