@langchain/google 0.1.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +97 -0
  4. package/dist/_virtual/rolldown_runtime.cjs +25 -0
  5. package/dist/chat_models/api-types.d.cts +1506 -0
  6. package/dist/chat_models/api-types.d.ts +1506 -0
  7. package/dist/chat_models/base.cjs +445 -0
  8. package/dist/chat_models/base.cjs.map +1 -0
  9. package/dist/chat_models/base.d.cts +319 -0
  10. package/dist/chat_models/base.d.ts +319 -0
  11. package/dist/chat_models/base.js +442 -0
  12. package/dist/chat_models/base.js.map +1 -0
  13. package/dist/chat_models/index.cjs +23 -0
  14. package/dist/chat_models/index.cjs.map +1 -0
  15. package/dist/chat_models/index.d.cts +39 -0
  16. package/dist/chat_models/index.d.ts +39 -0
  17. package/dist/chat_models/index.js +22 -0
  18. package/dist/chat_models/index.js.map +1 -0
  19. package/dist/chat_models/node.cjs +35 -0
  20. package/dist/chat_models/node.cjs.map +1 -0
  21. package/dist/chat_models/node.d.cts +13 -0
  22. package/dist/chat_models/node.d.ts +13 -0
  23. package/dist/chat_models/node.js +34 -0
  24. package/dist/chat_models/node.js.map +1 -0
  25. package/dist/chat_models/profiles.cjs +373 -0
  26. package/dist/chat_models/profiles.cjs.map +1 -0
  27. package/dist/chat_models/profiles.js +372 -0
  28. package/dist/chat_models/profiles.js.map +1 -0
  29. package/dist/chat_models/types.d.cts +250 -0
  30. package/dist/chat_models/types.d.ts +250 -0
  31. package/dist/clients/index.cjs +219 -0
  32. package/dist/clients/index.cjs.map +1 -0
  33. package/dist/clients/index.d.cts +49 -0
  34. package/dist/clients/index.d.ts +49 -0
  35. package/dist/clients/index.js +217 -0
  36. package/dist/clients/index.js.map +1 -0
  37. package/dist/clients/node.cjs +327 -0
  38. package/dist/clients/node.cjs.map +1 -0
  39. package/dist/clients/node.d.cts +59 -0
  40. package/dist/clients/node.d.ts +59 -0
  41. package/dist/clients/node.js +325 -0
  42. package/dist/clients/node.js.map +1 -0
  43. package/dist/const.cjs +13 -0
  44. package/dist/const.cjs.map +1 -0
  45. package/dist/const.js +9 -0
  46. package/dist/const.js.map +1 -0
  47. package/dist/converters/messages.cjs +697 -0
  48. package/dist/converters/messages.cjs.map +1 -0
  49. package/dist/converters/messages.js +691 -0
  50. package/dist/converters/messages.js.map +1 -0
  51. package/dist/converters/params.cjs +205 -0
  52. package/dist/converters/params.cjs.map +1 -0
  53. package/dist/converters/params.js +202 -0
  54. package/dist/converters/params.js.map +1 -0
  55. package/dist/converters/tools.cjs +356 -0
  56. package/dist/converters/tools.cjs.map +1 -0
  57. package/dist/converters/tools.js +353 -0
  58. package/dist/converters/tools.js.map +1 -0
  59. package/dist/index.cjs +7 -0
  60. package/dist/index.d.cts +3 -0
  61. package/dist/index.d.ts +3 -0
  62. package/dist/index.js +4 -0
  63. package/dist/node.cjs +7 -0
  64. package/dist/node.d.cts +3 -0
  65. package/dist/node.d.ts +3 -0
  66. package/dist/node.js +4 -0
  67. package/dist/utils/errors.cjs +543 -0
  68. package/dist/utils/errors.cjs.map +1 -0
  69. package/dist/utils/errors.js +535 -0
  70. package/dist/utils/errors.js.map +1 -0
  71. package/dist/utils/gcp-auth.cjs +188 -0
  72. package/dist/utils/gcp-auth.cjs.map +1 -0
  73. package/dist/utils/gcp-auth.d.cts +43 -0
  74. package/dist/utils/gcp-auth.d.ts +43 -0
  75. package/dist/utils/gcp-auth.js +186 -0
  76. package/dist/utils/gcp-auth.js.map +1 -0
  77. package/dist/utils/handler.cjs +67 -0
  78. package/dist/utils/handler.cjs.map +1 -0
  79. package/dist/utils/handler.d.cts +34 -0
  80. package/dist/utils/handler.d.ts +34 -0
  81. package/dist/utils/handler.js +64 -0
  82. package/dist/utils/handler.js.map +1 -0
  83. package/dist/utils/misc.cjs +10 -0
  84. package/dist/utils/misc.cjs.map +1 -0
  85. package/dist/utils/misc.d.cts +14 -0
  86. package/dist/utils/misc.d.ts +14 -0
  87. package/dist/utils/misc.js +9 -0
  88. package/dist/utils/misc.js.map +1 -0
  89. package/dist/utils/stream.cjs +26 -0
  90. package/dist/utils/stream.cjs.map +1 -0
  91. package/dist/utils/stream.js +25 -0
  92. package/dist/utils/stream.js.map +1 -0
  93. package/package.json +96 -0
@@ -0,0 +1,697 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_misc = require('../utils/misc.cjs');
3
+ const require_errors = require('../utils/errors.cjs');
4
+ const __langchain_core_messages = require_rolldown_runtime.__toESM(require("@langchain/core/messages"));
5
+
6
+ //#region src/converters/messages.ts
7
+ /**
8
+ * Standard content block converter for Google Gemini API.
9
+ * Converts deprecated Data content blocks to Gemini Part format.
10
+ *
11
+ * @deprecated This converter is for backward compatibility with older message formats.
12
+ * Use ContentBlock.Multimodal.Standard instead.
13
+ */
14
+ const geminiContentBlockConverter = {
15
+ providerName: "ChatGoogle",
16
+ fromStandardTextBlock(block) {
17
+ return { text: block.text };
18
+ },
19
+ fromStandardImageBlock(block) {
20
+ if ((0, __langchain_core_messages.isDataContentBlock)(block)) {
21
+ if (block.source_type === "base64") {
22
+ if (!block.mime_type) throw new require_errors.InvalidInputError("mime_type is required for base64 image blocks");
23
+ const dataStr = typeof block.data === "string" ? block.data : typeof block.data === "object" && block.data !== null ? btoa(Array.from(block.data).map((byte) => String.fromCharCode(byte)).join("")) : String(block.data);
24
+ return { inlineData: {
25
+ mimeType: block.mime_type,
26
+ data: dataStr
27
+ } };
28
+ }
29
+ if (block.source_type === "url") {
30
+ const parsed = (0, __langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
31
+ if (parsed) {
32
+ const dataStr = typeof parsed.data === "string" ? parsed.data : btoa(Array.from(parsed.data).map((byte) => String.fromCharCode(byte)).join(""));
33
+ return { inlineData: {
34
+ mimeType: parsed.mime_type || block.mime_type || "image/jpeg",
35
+ data: dataStr
36
+ } };
37
+ }
38
+ return { fileData: {
39
+ mimeType: block.mime_type || "image/jpeg",
40
+ fileUri: block.url
41
+ } };
42
+ }
43
+ if (block.source_type === "id") return { fileData: {
44
+ mimeType: block.mime_type || "image/jpeg",
45
+ fileUri: block.id
46
+ } };
47
+ }
48
+ throw new require_errors.InvalidInputError(`Image content blocks with source_type "${block.source_type}" are not supported for ChatGoogle. Supported source types are: "base64", "url", "id".`);
49
+ },
50
+ fromStandardAudioBlock(block) {
51
+ if ((0, __langchain_core_messages.isDataContentBlock)(block)) {
52
+ if (block.source_type === "base64") {
53
+ if (!block.mime_type) throw new require_errors.InvalidInputError("mime_type is required for base64 audio blocks");
54
+ const dataStr = typeof block.data === "string" ? block.data : typeof block.data === "object" && block.data !== null ? btoa(Array.from(block.data).map((byte) => String.fromCharCode(byte)).join("")) : String(block.data);
55
+ return { inlineData: {
56
+ mimeType: block.mime_type,
57
+ data: dataStr
58
+ } };
59
+ }
60
+ if (block.source_type === "url") {
61
+ const parsed = (0, __langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
62
+ if (parsed) {
63
+ const dataStr = typeof parsed.data === "string" ? parsed.data : btoa(Array.from(parsed.data).map((byte) => String.fromCharCode(byte)).join(""));
64
+ return { inlineData: {
65
+ mimeType: parsed.mime_type || block.mime_type || "audio/mpeg",
66
+ data: dataStr
67
+ } };
68
+ }
69
+ return { fileData: {
70
+ mimeType: block.mime_type || "audio/mpeg",
71
+ fileUri: block.url
72
+ } };
73
+ }
74
+ if (block.source_type === "id") return { fileData: {
75
+ mimeType: block.mime_type || "audio/mpeg",
76
+ fileUri: block.id
77
+ } };
78
+ }
79
+ throw new require_errors.InvalidInputError(`Audio content blocks with source_type "${block.source_type}" are not supported for ChatGoogle. Supported source types are: "base64", "url", "id".`);
80
+ },
81
+ fromStandardFileBlock(block) {
82
+ if ((0, __langchain_core_messages.isDataContentBlock)(block)) {
83
+ if (block.source_type === "base64") {
84
+ if (!block.mime_type) throw new require_errors.InvalidInputError("mime_type is required for base64 file blocks");
85
+ const dataStr = typeof block.data === "string" ? block.data : typeof block.data === "object" && block.data !== null ? btoa(Array.from(block.data).map((byte) => String.fromCharCode(byte)).join("")) : String(block.data);
86
+ return { inlineData: {
87
+ mimeType: block.mime_type,
88
+ data: dataStr
89
+ } };
90
+ }
91
+ if (block.source_type === "url") {
92
+ const parsed = (0, __langchain_core_messages.parseBase64DataUrl)({ dataUrl: block.url });
93
+ if (parsed) {
94
+ const dataStr = typeof parsed.data === "string" ? parsed.data : btoa(Array.from(parsed.data).map((byte) => String.fromCharCode(byte)).join(""));
95
+ return { inlineData: {
96
+ mimeType: parsed.mime_type || block.mime_type || "application/octet-stream",
97
+ data: dataStr
98
+ } };
99
+ }
100
+ return { fileData: {
101
+ mimeType: block.mime_type || "application/octet-stream",
102
+ fileUri: block.url
103
+ } };
104
+ }
105
+ if (block.source_type === "id") return { fileData: {
106
+ mimeType: block.mime_type || "application/octet-stream",
107
+ fileUri: block.id
108
+ } };
109
+ }
110
+ if ("source_type" in block && block.source_type === "text" && "text" in block) return { text: block.text };
111
+ throw new require_errors.InvalidInputError(`File content blocks with source_type "${block.source_type}" are not supported for ChatGoogle. Supported source types are: "base64", "url", "id", "text".`);
112
+ }
113
+ };
114
+ function convertStandardDataContentBlockToGeminiPart(block) {
115
+ function uint8arrayToString(data) {
116
+ return btoa(Array.from(data).map((byte) => String.fromCharCode(byte)).join(""));
117
+ }
118
+ function extractMimeType(str) {
119
+ if (str.startsWith("data:")) return {
120
+ mimeType: str.split(":")[1].split(";")[0],
121
+ data: str.split(",")[1]
122
+ };
123
+ return {
124
+ mimeType: null,
125
+ data: null
126
+ };
127
+ }
128
+ if ("mimeType" in block && "data" in block) {
129
+ const mimeType = block.mimeType;
130
+ const data = typeof block.data === "string" ? block.data : uint8arrayToString(block.data);
131
+ return { inlineData: {
132
+ mimeType,
133
+ data
134
+ } };
135
+ } else if ("mimeType" in block && "url" in block) {
136
+ const mimeType = block.mimeType;
137
+ const fileUri = block.url;
138
+ return { fileData: {
139
+ mimeType,
140
+ fileUri
141
+ } };
142
+ } else if ("url" in block && block.url?.startsWith("data:")) {
143
+ const { mimeType, data } = extractMimeType(block.url);
144
+ if (mimeType && data) return { inlineData: {
145
+ mimeType,
146
+ data
147
+ } };
148
+ }
149
+ return null;
150
+ }
151
+ function convertStandardVideoContentBlockToGeminiPart(block) {
152
+ const ret = convertStandardDataContentBlockToGeminiPart(block);
153
+ if (ret && block.metadata && "videoMetadata" in block.metadata) ret.videoMetadata = block.metadata.videoMetadata;
154
+ return ret;
155
+ }
156
+ /**
157
+ * Converts a single LangChain standard content block (v1 format)
158
+ * into a Gemini.Part.
159
+ *
160
+ * This is intended to be called from `convertStandardContentMessageToGeminiContent`
161
+ */
162
+ function convertStandardContentBlockToGeminiPart(block) {
163
+ switch (block.type) {
164
+ case "text": return { text: block.text };
165
+ case "image":
166
+ case "audio": return convertStandardDataContentBlockToGeminiPart(block);
167
+ case "video": return convertStandardVideoContentBlockToGeminiPart(block);
168
+ default: return null;
169
+ }
170
+ }
171
+ /**
172
+ * Converts a single LangChain message with standard content blocks (v1 format) to Gemini Content.
173
+ * This handles messages that have `response_metadata.output_version === "v1"`.
174
+ *
175
+ * This is intended to be called from `convertMessagesToGeminiContent`
176
+ */
177
+ function convertStandardContentMessageToGeminiContent(message) {
178
+ if (__langchain_core_messages.SystemMessage.isInstance(message)) return null;
179
+ let role;
180
+ if (__langchain_core_messages.HumanMessage.isInstance(message)) role = "user";
181
+ else if (__langchain_core_messages.AIMessage.isInstance(message)) role = "model";
182
+ else if (__langchain_core_messages.ToolMessage.isInstance(message)) role = "function";
183
+ else if (__langchain_core_messages.ChatMessage.isInstance(message)) {
184
+ const msgRole = message.role.toLowerCase();
185
+ if (msgRole === "user" || msgRole === "human") role = "user";
186
+ else if (msgRole === "assistant" || msgRole === "ai" || msgRole === "model") role = "model";
187
+ else if (msgRole === "function" || msgRole === "tool") role = "function";
188
+ else role = "user";
189
+ } else role = "user";
190
+ const parts = [];
191
+ message.contentBlocks.forEach((block) => {
192
+ const contentBlock = message.additional_kwargs.originalTextContentBlock || block;
193
+ const part = convertStandardContentBlockToGeminiPart(contentBlock);
194
+ if (part) parts.push(part);
195
+ });
196
+ if (__langchain_core_messages.ToolMessage.isInstance(message) && message.tool_call_id) {
197
+ const responseContent = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
198
+ parts.push({ functionResponse: {
199
+ name: message.name || "unknown",
200
+ response: { result: responseContent }
201
+ } });
202
+ }
203
+ if (parts.length === 0) return null;
204
+ return {
205
+ role,
206
+ parts
207
+ };
208
+ }
209
+ /**
210
+ * Converts a single LangChain message with legacy content blocks (v0 format) to Gemini Content.
211
+ * This handles messages that have `response_metadata.output_version === "v0"`.
212
+ *
213
+ * This is intended to be called from `convertMessagesToGeminiContent`
214
+ */
215
+ function convertLegacyContentMessageToGeminiContent(message, messages) {
216
+ if (__langchain_core_messages.SystemMessage.isInstance(message)) return null;
217
+ /**
218
+ * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only
219
+ */
220
+ function isMessageContentText(content) {
221
+ return typeof content === "object" && content !== null && "text" in content;
222
+ }
223
+ /**
224
+ * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only
225
+ */
226
+ function isMessageContentImageUrl(content) {
227
+ return typeof content === "object" && content !== null && "image_url" in content;
228
+ }
229
+ /**
230
+ * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only
231
+ */
232
+ function isMessageContentMedia(content) {
233
+ return typeof content === "object" && content !== null && "type" in content && content.type === "media";
234
+ }
235
+ /**
236
+ * Infers the MIME type from a URL based on its file extension.
237
+ * This is used as a fallback when the MIME type is not provided.
238
+ *
239
+ * @param url - The URL to infer the MIME type from
240
+ * @returns The inferred MIME type or a generic type if it cannot be determined
241
+ */
242
+ function inferMimeTypeFromUrl(url) {
243
+ const mimeTypeMap = {
244
+ jpg: "image/jpeg",
245
+ jpeg: "image/jpeg",
246
+ png: "image/png",
247
+ gif: "image/gif",
248
+ webp: "image/webp",
249
+ bmp: "image/bmp",
250
+ svg: "image/svg+xml",
251
+ ico: "image/x-icon",
252
+ tiff: "image/tiff",
253
+ tif: "image/tiff"
254
+ };
255
+ try {
256
+ const pathname = new URL(url).pathname;
257
+ const extension = pathname.split(".").pop()?.toLowerCase().split(/[?#]/)[0];
258
+ return extension ? mimeTypeMap[extension] : "application/octet-stream";
259
+ } catch {
260
+ const match = url.match(/\.([a-zA-Z0-9]+)(?:[?#]|$)/);
261
+ if (match) {
262
+ const extension = match[1].toLowerCase();
263
+ return mimeTypeMap[extension];
264
+ }
265
+ return "application/octet-stream";
266
+ }
267
+ }
268
+ function messageContentImageUrlData(content) {
269
+ const url = typeof content.image_url === "string" ? content.image_url : content.image_url.url;
270
+ if (!url) throw new require_errors.InvalidInputError("Missing image URL in image_url content block.");
271
+ const dataUrl = (0, __langchain_core_messages.parseBase64DataUrl)({ dataUrl: url });
272
+ if (dataUrl?.data && dataUrl?.mime_type) return { inlineData: {
273
+ data: dataUrl.data,
274
+ mimeType: dataUrl.mime_type
275
+ } };
276
+ else {
277
+ const mimeType = inferMimeTypeFromUrl(url) || "image/png";
278
+ return { fileData: {
279
+ mimeType,
280
+ fileUri: url
281
+ } };
282
+ }
283
+ }
284
+ function messageContentImageUrl(content) {
285
+ const ret = messageContentImageUrlData(content);
286
+ supplementVideoMetadata(content, ret);
287
+ return ret;
288
+ }
289
+ function messageContentMediaData(content) {
290
+ if ("mimeType" in content && "data" in content) return { inlineData: {
291
+ mimeType: content.mimeType,
292
+ data: content.data
293
+ } };
294
+ else if ("mimeType" in content && "fileUri" in content) return { fileData: {
295
+ mimeType: content.mimeType,
296
+ fileUri: content.fileUri
297
+ } };
298
+ throw new require_errors.InvalidInputError(`Invalid media content: ${JSON.stringify(content, null, 1)}. Expected either { mimeType, data } for inline data or { mimeType, fileUri } for file references.`);
299
+ }
300
+ function supplementVideoMetadata(content, ret) {
301
+ if ("videoMetadata" in content && typeof ret === "object") ret.videoMetadata = content.videoMetadata;
302
+ return ret;
303
+ }
304
+ function messageContentMedia(content) {
305
+ const ret = messageContentMediaData(content);
306
+ supplementVideoMetadata(content, ret);
307
+ return ret;
308
+ }
309
+ const role = require_misc.iife(() => {
310
+ if (__langchain_core_messages.HumanMessage.isInstance(message)) return "user";
311
+ else if (__langchain_core_messages.AIMessage.isInstance(message)) return "model";
312
+ else if (__langchain_core_messages.ToolMessage.isInstance(message)) return "function";
313
+ else if (__langchain_core_messages.ChatMessage.isInstance(message)) {
314
+ const msgRole = message.role.toLowerCase();
315
+ if (msgRole === "user" || msgRole === "human") return "user";
316
+ else if (msgRole === "assistant" || msgRole === "ai" || msgRole === "model") return "model";
317
+ else if (msgRole === "function" || msgRole === "tool") return "function";
318
+ else return "user";
319
+ } else return "user";
320
+ });
321
+ let parts = [];
322
+ if (typeof message.content === "string") {
323
+ if (message.content.trim()) parts.push({ text: message.content });
324
+ } else if (Array.isArray(message.content)) {
325
+ for (const item of message.content) if (typeof item === "string") parts.push({ text: item });
326
+ else if (typeof item === "object" && item !== null) if (isMessageContentText(item)) parts.push({ text: item.text });
327
+ else if ((0, __langchain_core_messages.isDataContentBlock)(item)) parts.push((0, __langchain_core_messages.convertToProviderContentBlock)(item, geminiContentBlockConverter));
328
+ else if (item?.type === "functionCall") {
329
+ const { type, functionCall,...etc } = item;
330
+ parts.push({
331
+ ...etc,
332
+ functionCall
333
+ });
334
+ } else if (isMessageContentImageUrl(item)) parts.push(messageContentImageUrl(item));
335
+ else if (isMessageContentMedia(item)) parts.push(messageContentMedia(item));
336
+ else parts.push(item);
337
+ }
338
+ if (__langchain_core_messages.ToolMessage.isInstance(message) && message.tool_call_id) {
339
+ const responseContent = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
340
+ const toolCall = messages.filter(__langchain_core_messages.AIMessage.isInstance).find((msg) => msg.tool_calls?.find((tc) => tc.id === message.tool_call_id));
341
+ if (!toolCall) throw new require_errors.ToolCallNotFoundError(message.tool_call_id);
342
+ parts.push({ functionResponse: {
343
+ name: toolCall?.name || "unknown",
344
+ response: { result: responseContent }
345
+ } });
346
+ }
347
+ if (role === "function") parts = parts.filter((part) => "functionResponse" in part);
348
+ if (parts.length > 0) return {
349
+ role,
350
+ parts
351
+ };
352
+ return null;
353
+ }
354
+ /**
355
+ * Converts an array of LangChain messages to Gemini API content format.
356
+ *
357
+ * This converter transforms LangChain's message types into the content structure
358
+ * expected by Google's Gemini API.
359
+ *
360
+ * @remarks
361
+ * The converter processes messages in two distinct ways based on their format:
362
+ *
363
+ * **V1 Standard Content Format**:
364
+ * Messages with `output_version: "v1"` in their response metadata use the
365
+ * standardized content block format. These are processed through
366
+ * `convertStandardContentMessageToGeminiContent`, which extracts text blocks
367
+ * from the `contentBlocks` array and maps message roles appropriately.
368
+ *
369
+ * **Google Format**:
370
+ * Messages without the v1 marker are processed using generic logic that handles:
371
+ * - String content: Simple text strings are converted to text parts
372
+ * - Array content: Arrays of strings or content blocks are processed individually
373
+ * - Tool messages: Converted to function response parts with tool call metadata
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * // Simple conversation with text messages
378
+ * const messages = [
379
+ * new HumanMessage("What is the weather?"),
380
+ * new AIMessage("I'll check that for you.")
381
+ * ];
382
+ * const contents = convertMessagesToGeminiContents(messages);
383
+ * // Returns: [
384
+ * // { role: "user", parts: [{ text: "What is the weather?" }] },
385
+ * // { role: "model", parts: [{ text: "I'll check that for you." }] }
386
+ * // ]
387
+ * ```
388
+ */
389
+ const convertMessagesToGeminiContents = (messages) => {
390
+ const contents = [];
391
+ for (const message of messages) {
392
+ const content = require_misc.iife(() => {
393
+ const outputVersion = "output_version" in message.response_metadata ? message.response_metadata?.output_version : "v0";
394
+ switch (outputVersion) {
395
+ case "v1": return convertStandardContentMessageToGeminiContent(message);
396
+ case "v0":
397
+ default: return convertLegacyContentMessageToGeminiContent(message, messages);
398
+ }
399
+ });
400
+ if (content) contents.push(content);
401
+ }
402
+ return contents;
403
+ };
404
+ /**
405
+ * Converts LangChain system messages to Gemini API system instruction format.
406
+ *
407
+ * This converter extracts system messages from a message array and transforms them
408
+ * into the format expected by Google's Gemini API for system instructions. System
409
+ * instructions provide context and guidelines that influence the model's behavior
410
+ * throughout the conversation.
411
+ *
412
+ * @remarks
413
+ * The converter handles two distinct message formats:
414
+ *
415
+ * 1. **V1 Standard Content Format**: Messages with `output_version: "v1"` in their
416
+ * response metadata use the standardized content block format. Only text blocks
417
+ * are extracted from the `contentBlocks` array.
418
+ *
419
+ * 2. **Legacy Google Format**: Messages without the v1 marker use the legacy format
420
+ * where content can be:
421
+ * - A simple string (trimmed and converted to a text part)
422
+ * - An array of strings or objects with `type: "text"` and a `text` property
423
+ *
424
+ * The converter filters out non-SystemMessage instances and only processes text
425
+ * content, ignoring other content types like images or tool calls that may be
426
+ * present in the message array.
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * // Simple string system message
431
+ * const messages = [
432
+ * new SystemMessage("You are a helpful assistant.")
433
+ * ];
434
+ * const instruction = convertMessagesToGeminiSystemInstruction(messages);
435
+ * // Returns: { parts: [{ text: "You are a helpful assistant." }] }
436
+ * ```
437
+ *
438
+ * @param messages - Array of LangChain messages to extract system instructions from
439
+ * @returns A Gemini system instruction object with text parts, or undefined if no
440
+ * system messages with text content are found
441
+ */
442
+ const convertMessagesToGeminiSystemInstruction = (messages) => {
443
+ const systemParts = [];
444
+ for (const message of messages) if (__langchain_core_messages.SystemMessage.isInstance(message)) {
445
+ if (message.text.trim()) systemParts.push({ text: message.text });
446
+ }
447
+ if (systemParts.length === 0) return void 0;
448
+ return { parts: systemParts };
449
+ };
450
+ /**
451
+ * Converts an array of Gemini API parts into an array of LangChain tool calls.
452
+ *
453
+ * This function iterates through a given array of `Gemini.Part` objects,
454
+ * identifies parts that represent a function call, and transforms them into
455
+ * LangChain's standardized `ToolCall` format. Each resulting tool call is
456
+ * assigned a unique, sequential ID.
457
+ *
458
+ * @param parts - An array of `Gemini.Part` objects, which may or may not
459
+ * contain function calls.
460
+ * @returns An array of `ContentBlock.Tools.ToolCall` objects. If no function
461
+ * call parts are found, an empty array is returned.
462
+ *
463
+ * @remarks
464
+ * The `id` for each tool call is generated sequentially in the format `call_0`,
465
+ * `call_1`, and so on, based on its order of appearance in the input array.
466
+ *
467
+ * @example
468
+ * ```typescript
469
+ * const geminiParts: Gemini.Part[] = [
470
+ * { text: "Thinking about what to do..." },
471
+ * {
472
+ * functionCall: {
473
+ * name: "search_web",
474
+ * args: { query: "latest AI news" }
475
+ * }
476
+ * },
477
+ * {
478
+ * functionCall: {
479
+ * name: "calculator",
480
+ * args: { expression: "2 + 2" }
481
+ * }
482
+ * }
483
+ * ];
484
+ *
485
+ * const toolCalls = convertGeminiPartsToToolCalls(geminiParts);
486
+ * // toolCalls will be:
487
+ * // [
488
+ * // { type: "tool_call", id: "call_0", name: "search_web", args: { query: "latest AI news" } },
489
+ * // { type: "tool_call", id: "call_1", name: "calculator", args: { expression: "2 + 2" } }
490
+ * // ]
491
+ * ```
492
+ */
493
+ const convertGeminiPartsToToolCalls = (parts) => {
494
+ const toolCalls = [];
495
+ for (let i = 0; i < parts.length; i++) {
496
+ const part = parts[i];
497
+ if ("functionCall" in part && part.functionCall) {
498
+ const functionCallPart = part;
499
+ toolCalls.push({
500
+ type: "tool_call",
501
+ id: `call_${toolCalls.length}`,
502
+ name: functionCallPart.functionCall.name,
503
+ args: functionCallPart.functionCall.args ?? {},
504
+ thoughtSignature: functionCallPart.thoughtSignature
505
+ });
506
+ }
507
+ }
508
+ return toolCalls;
509
+ };
510
+ const convertGeminiPartToContentBlock = (part) => {
511
+ const block = require_misc.iife(() => {
512
+ if ("text" in part && typeof part.text === "string") return {
513
+ type: "text",
514
+ text: part.text
515
+ };
516
+ else if ("inlineData" in part && part.inlineData) return {
517
+ type: "inlineData",
518
+ inlineData: part.inlineData
519
+ };
520
+ else if ("fileData" in part && part.fileData) return {
521
+ type: "fileData",
522
+ fileData: part.fileData
523
+ };
524
+ else if ("functionCall" in part && part.functionCall) return {
525
+ type: "functionCall",
526
+ functionCall: part.functionCall
527
+ };
528
+ else if ("functionResponse" in part && part.functionResponse) return {
529
+ type: "functionResponse",
530
+ functionResponse: part.functionResponse
531
+ };
532
+ else if ("executableCode" in part && part.executableCode) return {
533
+ type: "executableCode",
534
+ executableCode: part.executableCode
535
+ };
536
+ else if ("codeExecutionResult" in part && part.codeExecutionResult) return {
537
+ type: "codeExecutionResult",
538
+ codeExecutionResult: part.codeExecutionResult
539
+ };
540
+ return part;
541
+ });
542
+ const ret = {
543
+ thought: part.thought,
544
+ thoughtSignature: part.thoughtSignature,
545
+ partMetadata: part.partMetadata,
546
+ ...block
547
+ };
548
+ for (const attribute in ret) if (ret[attribute] === void 0) delete ret[attribute];
549
+ return ret;
550
+ };
551
+ /**
552
+ * Converts a Gemini API candidate response to a LangChain AIMessage.
553
+ *
554
+ * This converter transforms the response from Google's Gemini API into a standardized
555
+ * AIMessage format that can be used throughout the LangChain ecosystem. It handles
556
+ * various types of content including text, inline data, file data, function calls,
557
+ * and code execution results.
558
+ *
559
+ * @remarks
560
+ * The converter performs the following transformations:
561
+ * - Extracts tool calls from function call parts and assigns sequential IDs
562
+ * - Converts single text responses to string format for simplicity
563
+ * - Converts multi-part responses to ContentBlock array format
564
+ * - Preserves metadata including finish reason, safety ratings, and citations
565
+ * - Handles various Gemini part types (text, inlineData, fileData, functionCall, etc.)
566
+ *
567
+ * @example
568
+ * ```typescript
569
+ * const candidate: Gemini.Candidate = {
570
+ * content: {
571
+ * parts: [
572
+ * { text: "Hello, how can I help you?" }
573
+ * ]
574
+ * },
575
+ * finishReason: "STOP",
576
+ * safetyRatings: [...]
577
+ * };
578
+ *
579
+ * const aiMessage = convertGeminiCandidateToAIMessage(candidate);
580
+ * // Returns: AIMessage with content "Hello, how can I help you?"
581
+ * ```
582
+ *
583
+ * @param candidate - The Gemini API candidate response containing content parts and metadata
584
+ * @returns An AIMessage instance with converted content, tool calls, and metadata
585
+ */
586
+ const convertGeminiCandidateToAIMessage = (candidate) => {
587
+ function groundingSupportByPart(groundingSupports) {
588
+ const ret = [];
589
+ if (!groundingSupports || groundingSupports.length === 0) return [[]];
590
+ groundingSupports?.forEach((groundingSupport) => {
591
+ const segment = groundingSupport?.segment;
592
+ const partIndex = segment?.partIndex ?? 0;
593
+ if (ret[partIndex]) ret[partIndex].push(groundingSupport);
594
+ else ret[partIndex] = [groundingSupport];
595
+ });
596
+ return ret;
597
+ }
598
+ const parts = candidate.content?.parts || [];
599
+ const toolCalls = convertGeminiPartsToToolCalls(parts);
600
+ let content;
601
+ let originalTextContentBlock = void 0;
602
+ const groundingMetadata = candidate?.groundingMetadata;
603
+ const citationMetadata = candidate?.citationMetadata;
604
+ const groundingParts = groundingSupportByPart(groundingMetadata?.groundingSupports);
605
+ const hasContentPayload = (p) => !!p.text || !!p.thought || !!p.inlineData || !!p.fileData || !!p.functionCall || !!p.functionResponse || !!p.executableCode || !!p.codeExecutionResult;
606
+ const contentParts = parts.filter(hasContentPayload);
607
+ if (contentParts.length === 0) {
608
+ content = "";
609
+ if (parts.length > 0) originalTextContentBlock = convertGeminiPartToContentBlock(parts[0]);
610
+ } else if (contentParts.length === 1 && "text" in contentParts[0] && !("thought" in contentParts[0])) {
611
+ content = contentParts[0].text ?? "";
612
+ const mergedPart = { ...contentParts[0] };
613
+ for (const p of parts) if (p !== contentParts[0] && p.thoughtSignature) mergedPart.thoughtSignature = p.thoughtSignature;
614
+ originalTextContentBlock = convertGeminiPartToContentBlock(mergedPart);
615
+ } else content = parts.map((p) => convertGeminiPartToContentBlock(p));
616
+ const additional_kwargs = {
617
+ finishReason: candidate.finishReason,
618
+ finishMessage: candidate.finishMessage,
619
+ safetyRatings: candidate.safetyRatings,
620
+ tokenCount: candidate.tokenCount,
621
+ citationMetadata: candidate.citationMetadata,
622
+ groundingMetadata: candidate.groundingMetadata,
623
+ groundingAttributions: candidate.groundingAttributions,
624
+ urlRetrievalMetadata: candidate.urlRetrievalMetadata,
625
+ urlContextMetadata: candidate.urlContextMetadata,
626
+ avgLogprobs: candidate.avgLogprobs,
627
+ logprobsResult: candidate.logprobsResult,
628
+ originalTextContentBlock
629
+ };
630
+ const response_metadata = {
631
+ model_provider: "google",
632
+ finish_reason: candidate.finishReason,
633
+ safety_ratings: candidate.safetyRatings,
634
+ citation_metadata: candidate.citationMetadata,
635
+ grounding_metadata: candidate.groundingMetadata,
636
+ grounding_attributions: candidate.groundingAttributions,
637
+ url_retrieval_metadata: candidate.urlRetrievalMetadata,
638
+ url_context_metadata: candidate.urlContextMetadata,
639
+ avg_logprobs: candidate.avgLogprobs,
640
+ logprobs_result: candidate.logprobsResult,
641
+ citationMetadata,
642
+ groundingMetadata,
643
+ groundingSupport: groundingParts[0]
644
+ };
645
+ for (const attribute in additional_kwargs) if (additional_kwargs[attribute] === void 0) delete additional_kwargs[attribute];
646
+ for (const attribute in response_metadata) if (response_metadata[attribute] === void 0) delete response_metadata[attribute];
647
+ return new __langchain_core_messages.AIMessage({
648
+ content,
649
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
650
+ additional_kwargs,
651
+ response_metadata
652
+ });
653
+ };
654
+ const convertAIMessageToText = (message) => {
655
+ if (typeof message.content === "string") return message.content;
656
+ else if (Array.isArray(message.content)) return message.content.filter((c) => typeof c === "string" || c.type === "text").map((c) => typeof c === "string" ? c : c.text || "").join("");
657
+ else return "";
658
+ };
659
+ const convertGeminiGenerateContentResponseToUsageMetadata = (data) => {
660
+ function addModalityCounts(modalityTokenCounts, details) {
661
+ modalityTokenCounts?.forEach((modalityTokenCount) => {
662
+ const { modality, tokenCount } = modalityTokenCount;
663
+ if (typeof modality === "undefined" || typeof tokenCount === "undefined") return;
664
+ const modalityLc = modality.toLowerCase();
665
+ const currentCount = details[modalityLc] ?? 0;
666
+ details[modalityLc] = currentCount + tokenCount;
667
+ });
668
+ }
669
+ const usageMetadata = data.usageMetadata;
670
+ const inputTokenCount = usageMetadata?.promptTokenCount ?? 0;
671
+ const candidatesTokenCount = usageMetadata?.candidatesTokenCount ?? 0;
672
+ const thoughtsTokenCount = usageMetadata?.thoughtsTokenCount ?? 0;
673
+ const outputTokenCount = candidatesTokenCount + thoughtsTokenCount;
674
+ const totalTokens = usageMetadata?.totalTokenCount ?? inputTokenCount + outputTokenCount;
675
+ const input_token_details = {};
676
+ addModalityCounts(usageMetadata?.promptTokensDetails, input_token_details);
677
+ input_token_details.cache_read = usageMetadata?.cachedContentTokenCount ?? 0;
678
+ const output_token_details = {};
679
+ addModalityCounts(usageMetadata?.candidatesTokensDetails, output_token_details);
680
+ output_token_details.reasoning = usageMetadata?.thoughtsTokenCount ?? 0;
681
+ return {
682
+ input_tokens: inputTokenCount,
683
+ output_tokens: outputTokenCount,
684
+ total_tokens: totalTokens,
685
+ input_token_details,
686
+ output_token_details
687
+ };
688
+ };
689
+
690
+ //#endregion
691
+ exports.convertAIMessageToText = convertAIMessageToText;
692
+ exports.convertGeminiCandidateToAIMessage = convertGeminiCandidateToAIMessage;
693
+ exports.convertGeminiGenerateContentResponseToUsageMetadata = convertGeminiGenerateContentResponseToUsageMetadata;
694
+ exports.convertGeminiPartsToToolCalls = convertGeminiPartsToToolCalls;
695
+ exports.convertMessagesToGeminiContents = convertMessagesToGeminiContents;
696
+ exports.convertMessagesToGeminiSystemInstruction = convertMessagesToGeminiSystemInstruction;
697
+ //# sourceMappingURL=messages.cjs.map