@langchain/google 0.2.12 → 0.2.14

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 (118) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/chat_models/api_types/generate_content.d.cts +0 -2
  3. package/dist/chat_models/api_types/generate_content.d.ts +0 -2
  4. package/dist/chat_models/api_types/interactions.d.cts +1423 -0
  5. package/dist/chat_models/api_types/interactions.d.ts +1423 -0
  6. package/dist/chat_models/base.cjs +12 -11
  7. package/dist/chat_models/base.cjs.map +1 -1
  8. package/dist/chat_models/base.d.cts +3 -5
  9. package/dist/chat_models/base.d.ts +3 -5
  10. package/dist/chat_models/base.js +12 -11
  11. package/dist/chat_models/base.js.map +1 -1
  12. package/dist/chat_models/handlers/base.cjs +1 -2
  13. package/dist/chat_models/handlers/base.d.cts +0 -2
  14. package/dist/chat_models/handlers/base.d.ts +0 -2
  15. package/dist/chat_models/handlers/base.js +1 -2
  16. package/dist/chat_models/handlers/generate_content.cjs +7 -7
  17. package/dist/chat_models/handlers/generate_content.cjs.map +1 -1
  18. package/dist/chat_models/handlers/generate_content.d.cts +0 -2
  19. package/dist/chat_models/handlers/generate_content.d.ts +0 -2
  20. package/dist/chat_models/handlers/generate_content.js +7 -7
  21. package/dist/chat_models/handlers/generate_content.js.map +1 -1
  22. package/dist/chat_models/handlers/index.cjs +2 -0
  23. package/dist/chat_models/handlers/index.d.cts +2 -1
  24. package/dist/chat_models/handlers/index.d.ts +2 -1
  25. package/dist/chat_models/handlers/index.js +2 -1
  26. package/dist/chat_models/handlers/interactions.cjs +303 -0
  27. package/dist/chat_models/handlers/interactions.cjs.map +1 -0
  28. package/dist/chat_models/handlers/interactions.d.cts +33 -0
  29. package/dist/chat_models/handlers/interactions.d.ts +33 -0
  30. package/dist/chat_models/handlers/interactions.js +303 -0
  31. package/dist/chat_models/handlers/interactions.js.map +1 -0
  32. package/dist/chat_models/index.cjs +5 -4
  33. package/dist/chat_models/index.cjs.map +1 -1
  34. package/dist/chat_models/index.d.cts +4 -5
  35. package/dist/chat_models/index.d.ts +4 -5
  36. package/dist/chat_models/index.js +5 -5
  37. package/dist/chat_models/index.js.map +1 -1
  38. package/dist/chat_models/node.cjs +3 -4
  39. package/dist/chat_models/node.d.cts +1 -3
  40. package/dist/chat_models/node.d.ts +1 -3
  41. package/dist/chat_models/node.js +3 -4
  42. package/dist/chat_models/profiles.cjs +1 -2
  43. package/dist/chat_models/profiles.js +1 -2
  44. package/dist/chat_models/types.cjs +1 -2
  45. package/dist/chat_models/types.cjs.map +1 -1
  46. package/dist/chat_models/types.d.cts +524 -23
  47. package/dist/chat_models/types.d.ts +524 -23
  48. package/dist/chat_models/types.js +1 -2
  49. package/dist/chat_models/types.js.map +1 -1
  50. package/dist/clients/index.cjs +3 -4
  51. package/dist/clients/index.d.cts +0 -2
  52. package/dist/clients/index.d.ts +0 -2
  53. package/dist/clients/index.js +3 -4
  54. package/dist/clients/node.cjs +4 -5
  55. package/dist/clients/node.d.cts +0 -2
  56. package/dist/clients/node.d.ts +0 -2
  57. package/dist/clients/node.js +4 -5
  58. package/dist/const.cjs +1 -2
  59. package/dist/const.js +1 -2
  60. package/dist/converters/generate_content/messages.cjs +2 -2
  61. package/dist/converters/generate_content/messages.cjs.map +1 -1
  62. package/dist/converters/generate_content/messages.js +2 -2
  63. package/dist/converters/generate_content/messages.js.map +1 -1
  64. package/dist/converters/generate_content/params.cjs +1 -2
  65. package/dist/converters/generate_content/params.js +1 -2
  66. package/dist/converters/generate_content/stream_events.cjs +9 -3
  67. package/dist/converters/generate_content/stream_events.cjs.map +1 -1
  68. package/dist/converters/generate_content/stream_events.js +9 -3
  69. package/dist/converters/generate_content/stream_events.js.map +1 -1
  70. package/dist/converters/generate_content/tools.cjs +1 -2
  71. package/dist/converters/generate_content/tools.js +1 -2
  72. package/dist/converters/interactions/messages.cjs +766 -0
  73. package/dist/converters/interactions/messages.cjs.map +1 -0
  74. package/dist/converters/interactions/messages.js +759 -0
  75. package/dist/converters/interactions/messages.js.map +1 -0
  76. package/dist/converters/interactions/params.cjs +244 -0
  77. package/dist/converters/interactions/params.cjs.map +1 -0
  78. package/dist/converters/interactions/params.js +237 -0
  79. package/dist/converters/interactions/params.js.map +1 -0
  80. package/dist/converters/interactions/stream_events.cjs +769 -0
  81. package/dist/converters/interactions/stream_events.cjs.map +1 -0
  82. package/dist/converters/interactions/stream_events.js +768 -0
  83. package/dist/converters/interactions/stream_events.js.map +1 -0
  84. package/dist/converters/interactions/tools.cjs +229 -0
  85. package/dist/converters/interactions/tools.cjs.map +1 -0
  86. package/dist/converters/interactions/tools.js +228 -0
  87. package/dist/converters/interactions/tools.js.map +1 -0
  88. package/dist/converters/shared/params.cjs +1 -2
  89. package/dist/converters/shared/params.js +1 -2
  90. package/dist/index.cjs +2 -0
  91. package/dist/index.d.cts +3 -2
  92. package/dist/index.d.ts +3 -2
  93. package/dist/index.js +2 -1
  94. package/dist/node.cjs +1 -1
  95. package/dist/node.d.cts +1 -1
  96. package/dist/node.d.ts +1 -1
  97. package/dist/node.js +1 -1
  98. package/dist/utils/errors.cjs +22 -4
  99. package/dist/utils/errors.cjs.map +1 -1
  100. package/dist/utils/errors.js +22 -6
  101. package/dist/utils/errors.js.map +1 -1
  102. package/dist/utils/gcp-auth.cjs +3 -4
  103. package/dist/utils/gcp-auth.d.cts +0 -2
  104. package/dist/utils/gcp-auth.d.ts +0 -2
  105. package/dist/utils/gcp-auth.js +3 -4
  106. package/dist/utils/handler.cjs +1 -2
  107. package/dist/utils/handler.d.cts +0 -2
  108. package/dist/utils/handler.d.ts +0 -2
  109. package/dist/utils/handler.js +1 -2
  110. package/dist/utils/metadata.cjs +1 -2
  111. package/dist/utils/metadata.js +1 -2
  112. package/dist/utils/misc.cjs +1 -2
  113. package/dist/utils/misc.d.cts +0 -2
  114. package/dist/utils/misc.d.ts +0 -2
  115. package/dist/utils/misc.js +1 -2
  116. package/dist/utils/stream.cjs +1 -2
  117. package/dist/utils/stream.js +1 -2
  118. package/package.json +11 -9
@@ -0,0 +1,766 @@
1
+ const require_errors = require("../../utils/errors.cjs");
2
+ const require_messages = require("../generate_content/messages.cjs");
3
+ let _langchain_core_messages = require("@langchain/core/messages");
4
+ // src/converters/interactions/messages.ts
5
+ /**
6
+ * Converts a single LangChain `BaseMessage` into `Gemini.Part[]` by delegating
7
+ * to the existing GenerateContent message converters.
8
+ *
9
+ * When `message` is a `ToolMessage`, `convertMessagesToGeminiContents` requires
10
+ * the preceding `AIMessage` that issued the matching `tool_call_id` to be present
11
+ * in the input array so it can resolve the function name (and avoid throwing
12
+ * `ToolCallNotFoundError`). Pass the full `messages` conversation array when
13
+ * converting a `ToolMessage` so the matching `AIMessage` can be included as
14
+ * context; if none is found (e.g., a standalone `ToolMessage`), a minimal
15
+ * synthetic `AIMessage` is provided as a fallback. Only the `Gemini.Part[]`
16
+ * produced for `message` itself is returned.
17
+ *
18
+ * @param message - The individual message to convert into `Gemini.Part[]`.
19
+ * @param messages - Optional full conversation history used to look up the
20
+ * matching `AIMessage` when `message` is a `ToolMessage`.
21
+ */
22
+ function convertSingleMessageToGeminiParts(message, messages) {
23
+ if (_langchain_core_messages.SystemMessage.isInstance(message)) return require_messages.convertMessagesToGeminiSystemInstruction([message])?.parts ?? [];
24
+ if (_langchain_core_messages.ToolMessage.isInstance(message)) {
25
+ const aiMsg = messages?.filter(_langchain_core_messages.AIMessage.isInstance).find((msg) => msg.tool_calls?.find((tc) => tc.id === message.tool_call_id));
26
+ const fallbackAiMsg = !aiMsg && message.tool_call_id ? new _langchain_core_messages.AIMessage({ tool_calls: [{
27
+ type: "tool_call",
28
+ id: message.tool_call_id,
29
+ name: message.name ?? "unknown",
30
+ args: {}
31
+ }] }) : void 0;
32
+ const contextMessages = [...aiMsg ? [aiMsg] : fallbackAiMsg ? [fallbackAiMsg] : [], message];
33
+ const contents = require_messages.convertMessagesToGeminiContents(contextMessages);
34
+ return contents[contents.length - 1]?.parts ?? [];
35
+ }
36
+ return require_messages.convertMessagesToGeminiContents([message]).flatMap((c) => c.parts ?? []);
37
+ }
38
+ /**
39
+ * Extracts and concatenates leading `SystemMessage` instances into the
40
+ * Interactions API `system_instruction` string.
41
+ */
42
+ function convertMessagesToInteractionsSystemInstruction(messages) {
43
+ const chunks = [];
44
+ for (let i = 0; i < messages.length; i++) {
45
+ const message = messages[i];
46
+ if (_langchain_core_messages.SystemMessage.isInstance(message)) {
47
+ const parts = convertSingleMessageToGeminiParts(message);
48
+ for (const part of parts) if (typeof part.text === "string" && part.text.length > 0) chunks.push(part.text);
49
+ } else break;
50
+ }
51
+ return chunks.length > 0 ? chunks.join("\n") : void 0;
52
+ }
53
+ /**
54
+ * Maps a GenerateContent `Part.MediaResolution` or string into an Interactions `MediaResolution`.
55
+ */
56
+ function mapPartResolutionToInteractions(partResolution, defaultResolution) {
57
+ const rawLevel = typeof partResolution === "string" ? partResolution : partResolution?.level;
58
+ if (rawLevel) {
59
+ const lower = rawLevel.toLowerCase();
60
+ if (lower === "media_resolution_low" || lower === "low") return "low";
61
+ if (lower === "media_resolution_medium" || lower === "medium") return "medium";
62
+ if (lower === "media_resolution_high" || lower === "high") return "high";
63
+ if (lower === "media_resolution_ultra_high" || lower === "ultra_high") return "ultra_high";
64
+ }
65
+ return defaultResolution;
66
+ }
67
+ /**
68
+ * Converts a single `Gemini.Part` (produced by `convertMessageToGeminiParts`) into an
69
+ * Interactions API `Content` item (`TextContent`, `ImageContent`, `AudioContent`,
70
+ * `DocumentContent`, or `VideoContent`).
71
+ */
72
+ function convertGeminiPartToInteractionsContent(part, defaultMediaResolution) {
73
+ if (typeof part.text === "string") return {
74
+ type: "text",
75
+ text: part.text
76
+ };
77
+ if (part.inlineData) {
78
+ const mimeType = part.inlineData.mimeType || "application/octet-stream";
79
+ const data = part.inlineData.data;
80
+ const resolution = mapPartResolutionToInteractions(part.mediaResolution, defaultMediaResolution);
81
+ if (mimeType.startsWith("image/")) return {
82
+ type: "image",
83
+ mime_type: mimeType,
84
+ data,
85
+ ...resolution ? { resolution } : {}
86
+ };
87
+ if (mimeType.startsWith("audio/")) return {
88
+ type: "audio",
89
+ mime_type: mimeType,
90
+ data
91
+ };
92
+ if (mimeType.startsWith("video/")) return {
93
+ type: "video",
94
+ mime_type: mimeType,
95
+ data,
96
+ ...resolution ? { resolution } : {}
97
+ };
98
+ return {
99
+ type: "document",
100
+ mime_type: mimeType,
101
+ data
102
+ };
103
+ }
104
+ if (part.fileData) {
105
+ const mimeType = part.fileData.mimeType || "application/octet-stream";
106
+ const uri = part.fileData.fileUri;
107
+ const resolution = mapPartResolutionToInteractions(part.mediaResolution, defaultMediaResolution);
108
+ if (mimeType.startsWith("image/")) return {
109
+ type: "image",
110
+ mime_type: mimeType,
111
+ uri,
112
+ ...resolution ? { resolution } : {}
113
+ };
114
+ if (mimeType.startsWith("audio/")) return {
115
+ type: "audio",
116
+ mime_type: mimeType,
117
+ uri
118
+ };
119
+ if (mimeType.startsWith("video/")) return {
120
+ type: "video",
121
+ mime_type: mimeType,
122
+ uri,
123
+ ...resolution ? { resolution } : {}
124
+ };
125
+ return {
126
+ type: "document",
127
+ mime_type: mimeType,
128
+ uri
129
+ };
130
+ }
131
+ }
132
+ /**
133
+ * Converts an Interactions API `Content` item into a `Gemini.Part` so that
134
+ * downstream `convertGeminiPartToContentBlock` produces identical LangChain
135
+ * `AIMessage` content blocks to `GenerateContentApiHandler`.
136
+ */
137
+ function convertInteractionsContentToGeminiPart(content, thoughtSignature) {
138
+ const sigSpread = thoughtSignature ? { thoughtSignature } : {};
139
+ switch (content.type) {
140
+ case "text": return {
141
+ text: content.text,
142
+ ...sigSpread
143
+ };
144
+ case "image":
145
+ case "audio":
146
+ case "video":
147
+ case "document": {
148
+ const mimeType = content.mime_type || `${content.type}/unknown`;
149
+ if (content.data) return {
150
+ inlineData: {
151
+ mimeType,
152
+ data: content.data
153
+ },
154
+ ...sigSpread
155
+ };
156
+ if (content.uri) return {
157
+ fileData: {
158
+ mimeType,
159
+ fileUri: content.uri
160
+ },
161
+ ...sigSpread
162
+ };
163
+ return;
164
+ }
165
+ default: return;
166
+ }
167
+ }
168
+ /**
169
+ * Converts an array of LangChain `BaseMessage`s into an array of Interactions API `Step`s.
170
+ *
171
+ * ### ThoughtStep & `thoughtSignature` Round-Trip Design
172
+ *
173
+ * In the Interactions API, model reasoning and cryptographic thought signatures are
174
+ * represented as standalone `{ type: "thought", signature, summary? }` steps in the
175
+ * step sequence, rather than inline `{ thought: true, thoughtSignature }` fields on
176
+ * text or tool-call parts.
177
+ *
178
+ * When converting an `AIMessage` back into Interactions `Step`s for multi-turn history:
179
+ * 1. If the `AIMessage` contains reasoning blocks (`v1` `{ type: "reasoning", reasoning }`
180
+ * or `v0` `{ type: "text", thought: true, text, thoughtSignature }`), each reasoning
181
+ * block is emitted as a `{ type: "thought", signature, summary: [{ type: "text", text }] }` step.
182
+ * 2. In the Interactions API, Gemini 2.5 / 3 models also emit signature-only `ThoughtStep`s
183
+ * (`{ type: "thought", signature }` with no `summary`) before `ModelOutputStep` or
184
+ * `FunctionCallStep` when `thinking_summaries` is not enabled. On inbound conversion
185
+ * (`convertInteractionToAIMessage`), we stash those signatures on:
186
+ * - `aiMessage.additional_kwargs.originalTextContentBlock.thoughtSignature` (or non-thought
187
+ * text content blocks), and
188
+ * - `aiMessage.tool_calls[i].thoughtSignature`.
189
+ * When converting that `AIMessage` back to `Step`s here, if a `thoughtSignature` is present
190
+ * on the text block or tool call and has NOT already been emitted by a preceding `ThoughtStep`,
191
+ * we emit `{ type: "thought", signature }` immediately before the corresponding
192
+ * `{ type: "model_output" }` or `{ type: "function_call" }` step.
193
+ */
194
+ function convertMessagesToInteractionsInput(messages, defaultMediaResolution) {
195
+ const steps = [];
196
+ let isLeadingSystemMessages = true;
197
+ for (let i = 0; i < messages.length; i++) {
198
+ const message = messages[i];
199
+ if (isLeadingSystemMessages && _langchain_core_messages.SystemMessage.isInstance(message)) continue;
200
+ isLeadingSystemMessages = false;
201
+ if (_langchain_core_messages.AIMessage.isInstance(message)) {
202
+ const parts = convertSingleMessageToGeminiParts(message);
203
+ const modelOutputContents = [];
204
+ const emittedSignatures = /* @__PURE__ */ new Set();
205
+ let pendingOutputSignature;
206
+ const flushModelOutput = () => {
207
+ if (modelOutputContents.length > 0) {
208
+ if (pendingOutputSignature && !emittedSignatures.has(pendingOutputSignature)) {
209
+ const lastStep = steps[steps.length - 1];
210
+ if (lastStep?.type === "thought" && !lastStep.signature) lastStep.signature = pendingOutputSignature;
211
+ else steps.push({
212
+ type: "thought",
213
+ signature: pendingOutputSignature
214
+ });
215
+ emittedSignatures.add(pendingOutputSignature);
216
+ }
217
+ steps.push({
218
+ type: "model_output",
219
+ content: [...modelOutputContents]
220
+ });
221
+ modelOutputContents.length = 0;
222
+ pendingOutputSignature = void 0;
223
+ }
224
+ };
225
+ let callIndex = 0;
226
+ for (const part of parts) if (part.thought) {
227
+ flushModelOutput();
228
+ const summaryContent = [];
229
+ if (typeof part.text === "string" && part.text.length > 0) summaryContent.push({
230
+ type: "text",
231
+ text: part.text
232
+ });
233
+ if (part.thoughtSignature) emittedSignatures.add(part.thoughtSignature);
234
+ steps.push({
235
+ type: "thought",
236
+ ...part.thoughtSignature ? { signature: part.thoughtSignature } : {},
237
+ ...summaryContent.length > 0 ? { summary: summaryContent } : {}
238
+ });
239
+ } else if (part.executableCode) {
240
+ flushModelOutput();
241
+ steps.push({
242
+ type: "code_execution_call",
243
+ id: `code_exec_${callIndex++}`,
244
+ arguments: {
245
+ code: part.executableCode.code,
246
+ language: "python"
247
+ },
248
+ ...part.thoughtSignature ? { signature: part.thoughtSignature } : {}
249
+ });
250
+ } else if (part.codeExecutionResult) {
251
+ flushModelOutput();
252
+ steps.push({
253
+ type: "code_execution_result",
254
+ call_id: `code_exec_${Math.max(0, callIndex - 1)}`,
255
+ result: part.codeExecutionResult.output ?? "",
256
+ is_error: part.codeExecutionResult.outcome !== "OUTCOME_OK" && part.codeExecutionResult.outcome !== "OUTCOME_UNSPECIFIED" && Boolean(part.codeExecutionResult.outcome),
257
+ ...part.thoughtSignature ? { signature: part.thoughtSignature } : {}
258
+ });
259
+ } else if (part.functionCall) {
260
+ flushModelOutput();
261
+ const originalToolCall = message.tool_calls?.[callIndex];
262
+ const id = part.functionCall.id || originalToolCall?.id || `call_${callIndex}`;
263
+ const sig = part.thoughtSignature || originalToolCall?.thoughtSignature;
264
+ if (sig && !emittedSignatures.has(sig)) {
265
+ const lastStep = steps[steps.length - 1];
266
+ if (lastStep?.type === "thought" && !lastStep.signature) lastStep.signature = sig;
267
+ else steps.push({
268
+ type: "thought",
269
+ signature: sig
270
+ });
271
+ emittedSignatures.add(sig);
272
+ }
273
+ steps.push({
274
+ type: "function_call",
275
+ id,
276
+ name: part.functionCall.name,
277
+ arguments: part.functionCall.args ?? {}
278
+ });
279
+ callIndex++;
280
+ } else {
281
+ if (part.thoughtSignature && !pendingOutputSignature) pendingOutputSignature = part.thoughtSignature;
282
+ const converted = convertGeminiPartToInteractionsContent(part, defaultMediaResolution);
283
+ if (converted) modelOutputContents.push(converted);
284
+ }
285
+ flushModelOutput();
286
+ if (pendingOutputSignature && !emittedSignatures.has(pendingOutputSignature)) {
287
+ steps.push({
288
+ type: "thought",
289
+ signature: pendingOutputSignature
290
+ });
291
+ emittedSignatures.add(pendingOutputSignature);
292
+ }
293
+ } else if (_langchain_core_messages.ToolMessage.isInstance(message)) {
294
+ const callId = message.tool_call_id || message.additional_kwargs?.tool_call_id || "unknown";
295
+ const name = message.name || message.additional_kwargs?.name || messages.filter(_langchain_core_messages.AIMessage.isInstance).flatMap((m) => m.tool_calls ?? []).find((tc) => tc.id === callId)?.name;
296
+ const isError = message.status === "error" ? true : void 0;
297
+ let result;
298
+ if (typeof message.content === "string") try {
299
+ const parsed = JSON.parse(message.content);
300
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) result = parsed;
301
+ else result = message.content;
302
+ } catch {
303
+ result = message.content;
304
+ }
305
+ else if (Array.isArray(message.content)) {
306
+ const parts = convertSingleMessageToGeminiParts(message, messages);
307
+ if (parts.some((p) => !("functionResponse" in p))) {
308
+ const contents = [];
309
+ for (const part of parts) if (part.functionResponse?.response) {
310
+ const resp = part.functionResponse.response;
311
+ if (typeof resp === "object" && resp !== null && "result" in resp && Object.keys(resp).length === 1) {
312
+ if (typeof resp.result === "string") contents.push({
313
+ type: "text",
314
+ text: resp.result
315
+ });
316
+ else if (Array.isArray(resp.result)) for (const item of resp.result) if (typeof item === "string") contents.push({
317
+ type: "text",
318
+ text: item
319
+ });
320
+ else if (typeof item === "object" && item !== null && "text" in item && typeof item.text === "string") contents.push({
321
+ type: "text",
322
+ text: item.text
323
+ });
324
+ else contents.push({
325
+ type: "text",
326
+ text: JSON.stringify(item)
327
+ });
328
+ else contents.push({
329
+ type: "text",
330
+ text: JSON.stringify(resp.result)
331
+ });
332
+ } else contents.push({
333
+ type: "text",
334
+ text: JSON.stringify(resp)
335
+ });
336
+ } else {
337
+ const converted = convertGeminiPartToInteractionsContent(part, defaultMediaResolution);
338
+ if (converted) contents.push(converted);
339
+ }
340
+ result = contents.length > 0 ? contents : "";
341
+ } else result = parts.find((p) => "functionResponse" in p && Boolean(p.functionResponse?.response))?.functionResponse?.response ?? { result: message.content };
342
+ } else if (typeof message.content === "object" && message.content !== null) result = { result: message.content };
343
+ else result = String(message.content ?? "");
344
+ steps.push({
345
+ type: "function_result",
346
+ call_id: callId,
347
+ ...name ? { name } : {},
348
+ ...typeof isError === "boolean" ? { is_error: isError } : {},
349
+ result
350
+ });
351
+ } else {
352
+ const parts = convertSingleMessageToGeminiParts(message);
353
+ const contents = [];
354
+ for (const part of parts) {
355
+ const converted = convertGeminiPartToInteractionsContent(part, defaultMediaResolution);
356
+ if (converted) contents.push(converted);
357
+ }
358
+ if (contents.length > 0) steps.push({
359
+ type: "user_input",
360
+ content: contents
361
+ });
362
+ }
363
+ }
364
+ return steps;
365
+ }
366
+ /**
367
+ * Converts Interactions API `ModalityTokens[]` into a modality record (`text`, `image`, `audio`, etc.)
368
+ * compatible with LangChain's `InputTokenDetails` / `OutputTokenDetails`.
369
+ */
370
+ function convertInteractionsModalityTokensToRecord(modalityTokens) {
371
+ const ret = {};
372
+ for (const item of modalityTokens ?? []) if (item.modality && typeof item.tokens === "number") {
373
+ const key = item.modality.toLowerCase();
374
+ ret[key] = (ret[key] ?? 0) + item.tokens;
375
+ }
376
+ return ret;
377
+ }
378
+ /**
379
+ * Converts an Interactions API `Usage` object into LangChain's `UsageMetadata`.
380
+ */
381
+ function convertInteractionsUsageToUsageMetadata(usage) {
382
+ if (!usage) return;
383
+ const inputMods = convertInteractionsModalityTokensToRecord(usage.input_tokens_by_modality);
384
+ const outputMods = convertInteractionsModalityTokensToRecord(usage.output_tokens_by_modality);
385
+ const cacheRead = usage.total_cached_tokens;
386
+ const toolUse = usage.total_tool_use_tokens;
387
+ const inputTokenDetails = {
388
+ ...typeof cacheRead === "number" ? { cache_read: cacheRead } : {},
389
+ ...typeof toolUse === "number" ? { tool_use: toolUse } : {},
390
+ ...inputMods
391
+ };
392
+ const reasoning = usage.total_thought_tokens;
393
+ const outputTokenDetails = {
394
+ ...typeof reasoning === "number" ? { reasoning } : {},
395
+ ...outputMods
396
+ };
397
+ const rawInput = usage.total_input_tokens ?? 0;
398
+ const rawOutput = usage.total_output_tokens ?? 0;
399
+ const rawThought = usage.total_thought_tokens ?? 0;
400
+ const rawToolUse = usage.total_tool_use_tokens ?? 0;
401
+ const inputTokens = typeof usage.total_tokens === "number" && rawInput + rawToolUse + rawOutput + rawThought === usage.total_tokens ? rawInput + rawToolUse : rawInput;
402
+ const outputTokens = typeof usage.total_tokens === "number" && rawInput + rawOutput === usage.total_tokens && rawThought > 0 ? rawOutput : rawOutput + rawThought;
403
+ return {
404
+ input_tokens: inputTokens,
405
+ output_tokens: outputTokens,
406
+ total_tokens: usage.total_tokens ?? inputTokens + outputTokens,
407
+ ...Object.keys(inputTokenDetails).length > 0 ? { input_token_details: inputTokenDetails } : {},
408
+ ...Object.keys(outputTokenDetails).length > 0 ? { output_token_details: outputTokenDetails } : {}
409
+ };
410
+ }
411
+ /**
412
+ * Normalizes an item from `interaction.steps` or legacy `interaction.outputs`
413
+ * into a `Gemini.Interactions.Step`.
414
+ */
415
+ function normalizeInteractionStep(item) {
416
+ if (item.type === "text" || item.type === "image" || item.type === "audio" || item.type === "document" || item.type === "video") return {
417
+ type: "model_output",
418
+ content: [item]
419
+ };
420
+ return item;
421
+ }
422
+ /**
423
+ * Synthesizes GenerateContent-compatible `groundingMetadata`, `citationMetadata`,
424
+ * `groundingSupport`, and `urlContextMetadata` from Interactions API annotations
425
+ * and built-in tool steps (`google_search_*`, `google_maps_*`, `file_search_*`, `url_context_*`).
426
+ */
427
+ function synthesizeMetadataFromInteractions(options) {
428
+ const { textSegments, builtInToolSteps } = options;
429
+ const webSearchQueries = [];
430
+ const groundingChunks = [];
431
+ const groundingSupports = [];
432
+ const citationSources = [];
433
+ const urlMetadataList = [];
434
+ let searchEntryPointRenderedContent;
435
+ const urlToChunkIndex = /* @__PURE__ */ new Map();
436
+ for (const step of builtInToolSteps) if (step.type === "google_search_call") {
437
+ for (const q of step.arguments?.queries ?? []) if (q && !webSearchQueries.includes(q)) webSearchQueries.push(q);
438
+ } else if (step.type === "google_search_result") for (const item of step.result ?? []) {
439
+ const rawItem = item;
440
+ if (rawItem.rendered_content && !searchEntryPointRenderedContent) searchEntryPointRenderedContent = rawItem.rendered_content;
441
+ if (rawItem.url || rawItem.title) {
442
+ const chunkIndex = groundingChunks.length;
443
+ groundingChunks.push({ web: {
444
+ ...rawItem.url ? { uri: rawItem.url } : {},
445
+ ...rawItem.title ? { title: rawItem.title } : {}
446
+ } });
447
+ if (rawItem.url && !urlToChunkIndex.has(rawItem.url)) urlToChunkIndex.set(rawItem.url, chunkIndex);
448
+ }
449
+ }
450
+ else if (step.type === "google_maps_call") {
451
+ for (const q of step.arguments?.queries ?? []) if (q && !webSearchQueries.includes(q)) webSearchQueries.push(q);
452
+ } else if (step.type === "google_maps_result") for (const item of step.result ?? []) for (const place of item.places ?? []) {
453
+ const chunkIndex = groundingChunks.length;
454
+ groundingChunks.push({ maps: {
455
+ ...place.url ? { uri: place.url } : {},
456
+ ...place.name ? { title: place.name } : {},
457
+ ...place.place_id ? { placeId: place.place_id } : {}
458
+ } });
459
+ if (place.url && !urlToChunkIndex.has(place.url)) urlToChunkIndex.set(place.url, chunkIndex);
460
+ }
461
+ else if (step.type === "file_search_result") for (const item of step.result ?? []) groundingChunks.push({ retrievedContext: {
462
+ ...item.title ? { title: item.title } : {},
463
+ ...item.text ? { text: item.text } : {},
464
+ ...item.file_search_store ? { fileSearchStore: item.file_search_store } : {}
465
+ } });
466
+ else if (step.type === "url_context_result") for (const item of step.result ?? []) {
467
+ let status = "URL_RETRIEVAL_STATUS_UNSPECIFIED";
468
+ switch (item.status) {
469
+ case "success":
470
+ status = "URL_RETRIEVAL_STATUS_SUCCESS";
471
+ break;
472
+ case "error":
473
+ status = "URL_RETRIEVAL_STATUS_ERROR";
474
+ break;
475
+ case "paywall":
476
+ status = "URL_RETRIEVAL_STATUS_PAYWALL";
477
+ break;
478
+ case "unsafe":
479
+ status = "URL_RETRIEVAL_STATUS_UNSAFE";
480
+ break;
481
+ default: status = "URL_RETRIEVAL_STATUS_UNSPECIFIED";
482
+ }
483
+ urlMetadataList.push({
484
+ ...item.url ? { retrievedUrl: item.url } : {},
485
+ urlRetrievalStatus: status
486
+ });
487
+ if (item.url && !urlToChunkIndex.has(item.url)) {
488
+ const chunkIndex = groundingChunks.length;
489
+ groundingChunks.push({ web: { uri: item.url } });
490
+ urlToChunkIndex.set(item.url, chunkIndex);
491
+ }
492
+ }
493
+ for (let partIndex = 0; partIndex < textSegments.length; partIndex++) {
494
+ const seg = textSegments[partIndex];
495
+ for (const annotation of seg.annotations ?? []) if (annotation.type === "url_citation") {
496
+ citationSources.push({
497
+ ...typeof annotation.start_index === "number" ? { startIndex: annotation.start_index } : {},
498
+ ...typeof annotation.end_index === "number" ? { endIndex: annotation.end_index } : {},
499
+ ...annotation.url ? { uri: annotation.url } : {}
500
+ });
501
+ let chunkIdx = annotation.url ? urlToChunkIndex.get(annotation.url) : void 0;
502
+ if (chunkIdx === void 0 && (annotation.url || annotation.title)) {
503
+ chunkIdx = groundingChunks.length;
504
+ groundingChunks.push({ web: {
505
+ ...annotation.url ? { uri: annotation.url } : {},
506
+ ...annotation.title ? { title: annotation.title } : {}
507
+ } });
508
+ if (annotation.url) urlToChunkIndex.set(annotation.url, chunkIdx);
509
+ }
510
+ groundingSupports.push({
511
+ segment: {
512
+ partIndex,
513
+ ...typeof annotation.start_index === "number" ? { startIndex: annotation.start_index } : {},
514
+ ...typeof annotation.end_index === "number" ? { endIndex: annotation.end_index } : {}
515
+ },
516
+ ...chunkIdx !== void 0 ? { groundingChunkIndices: [chunkIdx] } : {}
517
+ });
518
+ } else if (annotation.type === "place_citation") {
519
+ citationSources.push({
520
+ ...typeof annotation.start_index === "number" ? { startIndex: annotation.start_index } : {},
521
+ ...typeof annotation.end_index === "number" ? { endIndex: annotation.end_index } : {},
522
+ ...annotation.url ? { uri: annotation.url } : {}
523
+ });
524
+ let chunkIdx = annotation.url ? urlToChunkIndex.get(annotation.url) : void 0;
525
+ if (chunkIdx === void 0 && (annotation.url || annotation.name || annotation.place_id)) {
526
+ chunkIdx = groundingChunks.length;
527
+ groundingChunks.push({ maps: {
528
+ ...annotation.url ? { uri: annotation.url } : {},
529
+ ...annotation.name ? { title: annotation.name } : {},
530
+ ...annotation.place_id ? { placeId: annotation.place_id } : {}
531
+ } });
532
+ if (annotation.url) urlToChunkIndex.set(annotation.url, chunkIdx);
533
+ }
534
+ groundingSupports.push({
535
+ segment: {
536
+ partIndex,
537
+ ...typeof annotation.start_index === "number" ? { startIndex: annotation.start_index } : {},
538
+ ...typeof annotation.end_index === "number" ? { endIndex: annotation.end_index } : {}
539
+ },
540
+ ...chunkIdx !== void 0 ? { groundingChunkIndices: [chunkIdx] } : {}
541
+ });
542
+ } else if (annotation.type === "file_citation") citationSources.push({
543
+ ...typeof annotation.start_index === "number" ? { startIndex: annotation.start_index } : {},
544
+ ...typeof annotation.end_index === "number" ? { endIndex: annotation.end_index } : {},
545
+ ...annotation.document_uri ? { uri: annotation.document_uri } : {}
546
+ });
547
+ }
548
+ const groundingMetadata = webSearchQueries.length > 0 || groundingChunks.length > 0 || groundingSupports.length > 0 || Boolean(searchEntryPointRenderedContent) ? {
549
+ ...webSearchQueries.length > 0 ? { webSearchQueries } : {},
550
+ ...groundingChunks.length > 0 ? { groundingChunks } : {},
551
+ ...groundingSupports.length > 0 ? { groundingSupports } : {},
552
+ ...searchEntryPointRenderedContent ? { searchEntryPoint: { renderedContent: searchEntryPointRenderedContent } } : {}
553
+ } : void 0;
554
+ const groundingSupport = groundingMetadata?.groundingSupports?.filter((s) => (s?.segment?.partIndex ?? 0) === 0);
555
+ const citationMetadata = citationSources.length > 0 ? { citationSources } : void 0;
556
+ const urlContextMetadata = urlMetadataList.length > 0 ? { urlMetadata: urlMetadataList } : void 0;
557
+ return {
558
+ ...groundingMetadata ? {
559
+ groundingMetadata,
560
+ grounding_metadata: groundingMetadata,
561
+ groundingSupport: groundingSupport ?? []
562
+ } : {},
563
+ ...citationMetadata ? {
564
+ citationMetadata,
565
+ citation_metadata: citationMetadata
566
+ } : {},
567
+ ...urlContextMetadata ? {
568
+ urlContextMetadata,
569
+ url_context_metadata: urlContextMetadata
570
+ } : {}
571
+ };
572
+ }
573
+ /**
574
+ * Converts an Interactions API `Interaction` response into a LangChain `AIMessage`
575
+ * along with `finishReason` and `usageMetadata`.
576
+ *
577
+ * ### ThoughtStep & `thoughtSignature` Inbound Mapping
578
+ *
579
+ * In `convertGeminiCandidateToAIMessage` (GenerateContent):
580
+ * - A `Gemini.Part` with `thought: true` is treated as content-bearing (`!!p.thought === true`),
581
+ * which causes `AIMessage.content` to remain an array of content blocks (so the user can inspect
582
+ * the reasoning block alongside the text block).
583
+ * - A `Gemini.Part` with `{ text: "Hello", thoughtSignature: "sig..." }` (or a trailing
584
+ * metadata-only `{ thoughtSignature: "sig..." }` part with `thought` undefined) is NOT a
585
+ * reasoning block: if there is a single text part, `AIMessage.content` collapses to the plain
586
+ * string `"Hello"` and `additional_kwargs.originalTextContentBlock` preserves
587
+ * `{ type: "text", text: "Hello", thoughtSignature: "sig..." }`.
588
+ * - A `Gemini.Part` with `{ functionCall: {...}, thoughtSignature: "sig..." }` attaches
589
+ * `thoughtSignature` directly to `aiMessage.tool_calls[i].thoughtSignature`.
590
+ *
591
+ * In the Interactions API, the model emits `{ type: "thought", signature: "sig...", summary?: [...] }`:
592
+ * - Even when `thinking_summaries` is NOT enabled (`summary` is undefined or empty), Gemini 2.5 / 3
593
+ * still emits a `ThoughtStep` containing `signature` before a `ModelOutputStep` or `FunctionCallStep`.
594
+ * - Therefore:
595
+ * 1. If `step.summary` is non-empty, we emit `Gemini.Part`(s) with `thought: true` and
596
+ * `thoughtSignature: step.signature`. This produces a reasoning block in `AIMessage.content`
597
+ * and `AIMessage.contentBlocks`.
598
+ * 2. If `step.summary` is empty/undefined (signature-only), we do NOT emit a `{ thought: true }`
599
+ * part (which would prevent `AIMessage.content` from collapsing to a plain string!). Instead,
600
+ * we hold `pendingThoughtSignature = step.signature` and attach `thoughtSignature` to the
601
+ * next `FunctionCallStep` part or `ModelOutputStep` part (or as a metadata-only
602
+ * `{ thoughtSignature }` part if no step follows).
603
+ */
604
+ function convertInteractionToAIMessage(interaction) {
605
+ const steps = (interaction.steps ?? interaction.outputs ?? []).map(normalizeInteractionStep);
606
+ const geminiParts = [];
607
+ const textSegments = [];
608
+ const builtInToolSteps = [];
609
+ let pendingThoughtSignature;
610
+ let pendingFunctionCallSignature;
611
+ for (const step of steps) switch (step.type) {
612
+ case "thought": {
613
+ const textSummaries = (step.summary ?? []).filter((c) => c.type === "text" && typeof c.text === "string" && c.text.length > 0);
614
+ if (textSummaries.length > 0) {
615
+ for (let i = 0; i < textSummaries.length; i++) {
616
+ const isLast = i === textSummaries.length - 1;
617
+ geminiParts.push({
618
+ text: textSummaries[i].text,
619
+ thought: true,
620
+ ...isLast && step.signature ? { thoughtSignature: step.signature } : {}
621
+ });
622
+ }
623
+ pendingThoughtSignature = void 0;
624
+ pendingFunctionCallSignature = step.signature;
625
+ } else if (step.signature) {
626
+ pendingThoughtSignature = step.signature;
627
+ pendingFunctionCallSignature = step.signature;
628
+ }
629
+ break;
630
+ }
631
+ case "model_output": {
632
+ if (step.error?.message) throw new require_errors.GoogleError(`Google Interactions API model output error (${step.error.code ?? "unknown"}): ${step.error.message}`);
633
+ const contents = typeof step.content === "string" ? [{
634
+ type: "text",
635
+ text: step.content
636
+ }] : step.content ?? [];
637
+ for (const contentItem of contents) {
638
+ if (contentItem.type === "text") textSegments.push({
639
+ text: contentItem.text,
640
+ annotations: contentItem.annotations
641
+ });
642
+ const part = convertInteractionsContentToGeminiPart(contentItem, pendingThoughtSignature);
643
+ if (part) {
644
+ geminiParts.push(part);
645
+ pendingThoughtSignature = void 0;
646
+ pendingFunctionCallSignature = void 0;
647
+ }
648
+ }
649
+ break;
650
+ }
651
+ case "function_call": {
652
+ const fcSignature = pendingThoughtSignature ?? pendingFunctionCallSignature;
653
+ geminiParts.push({
654
+ functionCall: {
655
+ id: step.id,
656
+ name: step.name,
657
+ args: step.arguments ?? {}
658
+ },
659
+ ...fcSignature ? { thoughtSignature: fcSignature } : {}
660
+ });
661
+ pendingThoughtSignature = void 0;
662
+ pendingFunctionCallSignature = void 0;
663
+ break;
664
+ }
665
+ case "code_execution_call":
666
+ geminiParts.push({
667
+ executableCode: {
668
+ code: step.arguments?.code ?? "",
669
+ language: "PYTHON"
670
+ },
671
+ ...step.signature || pendingThoughtSignature ? { thoughtSignature: step.signature ?? pendingThoughtSignature } : {}
672
+ });
673
+ pendingThoughtSignature = void 0;
674
+ break;
675
+ case "code_execution_result":
676
+ geminiParts.push({
677
+ codeExecutionResult: {
678
+ output: step.result ?? "",
679
+ outcome: step.is_error ? "OUTCOME_FAILED" : "OUTCOME_OK"
680
+ },
681
+ ...step.signature || pendingThoughtSignature ? { thoughtSignature: step.signature ?? pendingThoughtSignature } : {}
682
+ });
683
+ pendingThoughtSignature = void 0;
684
+ break;
685
+ case "google_search_call":
686
+ case "google_search_result":
687
+ case "google_maps_call":
688
+ case "google_maps_result":
689
+ case "file_search_call":
690
+ case "file_search_result":
691
+ case "url_context_call":
692
+ case "url_context_result":
693
+ case "mcp_server_tool_call":
694
+ case "mcp_server_tool_result":
695
+ case "processing_call":
696
+ case "processing_result":
697
+ case "retrieval_call":
698
+ case "retrieval_result": builtInToolSteps.push(step);
699
+ }
700
+ if (pendingThoughtSignature) geminiParts.push({ thoughtSignature: pendingThoughtSignature });
701
+ const blockContent = geminiParts.map((part) => require_messages.convertGeminiPartToContentBlock(part)).filter((block) => block !== null);
702
+ let content = blockContent;
703
+ let originalTextContentBlock;
704
+ const hasContentPayload = (p) => p.type !== "text" || typeof p.text === "string" && p.text.length > 0 || !!p.thought;
705
+ const contentBearingParts = blockContent.filter(hasContentPayload);
706
+ if (blockContent.length === 1 && blockContent[0].type === "text") {
707
+ originalTextContentBlock = blockContent[0];
708
+ content = blockContent[0].text;
709
+ } else if (contentBearingParts.length === 1 && contentBearingParts[0].type === "text" && !contentBearingParts[0].thought) {
710
+ const textPart = contentBearingParts[0];
711
+ const sigSource = blockContent.find((p) => p !== textPart && p.thoughtSignature);
712
+ originalTextContentBlock = sigSource ? {
713
+ ...textPart,
714
+ thoughtSignature: sigSource.thoughtSignature
715
+ } : textPart;
716
+ content = textPart.text;
717
+ } else if (blockContent.length === 0) content = "";
718
+ const toolCalls = require_messages.convertGeminiPartsToToolCalls(geminiParts);
719
+ const usageMetadata = convertInteractionsUsageToUsageMetadata(interaction.usage);
720
+ const synthesized = synthesizeMetadataFromInteractions({
721
+ textSegments,
722
+ builtInToolSteps
723
+ });
724
+ let finishReason;
725
+ if (interaction.status === "completed") finishReason = "STOP";
726
+ else if (interaction.status === "requires_action") finishReason = "STOP";
727
+ else if (interaction.status) finishReason = interaction.status.toUpperCase();
728
+ const responseMetadata = {
729
+ model_provider: "google",
730
+ ...interaction.model ? { model_name: interaction.model } : {},
731
+ ...finishReason ? { finishReason } : {},
732
+ ...synthesized,
733
+ ...builtInToolSteps.length > 0 ? { interactionSteps: builtInToolSteps } : {},
734
+ interaction: {
735
+ ...interaction.id ? { id: interaction.id } : {},
736
+ ...interaction.status ? { status: interaction.status } : {},
737
+ ...interaction.role ? { role: interaction.role } : {},
738
+ ...interaction.previous_interaction_id ? { previous_interaction_id: interaction.previous_interaction_id } : {}
739
+ }
740
+ };
741
+ const additionalKwargs = {
742
+ ...finishReason ? { finishReason } : {},
743
+ ...originalTextContentBlock ? { originalTextContentBlock } : {}
744
+ };
745
+ return {
746
+ message: new _langchain_core_messages.AIMessage({
747
+ content,
748
+ tool_calls: toolCalls,
749
+ additional_kwargs: additionalKwargs,
750
+ usage_metadata: usageMetadata,
751
+ response_metadata: responseMetadata
752
+ }),
753
+ finishReason,
754
+ usageMetadata
755
+ };
756
+ }
757
+ exports.convertGeminiPartToInteractionsContent = convertGeminiPartToInteractionsContent;
758
+ exports.convertInteractionToAIMessage = convertInteractionToAIMessage;
759
+ exports.convertInteractionsContentToGeminiPart = convertInteractionsContentToGeminiPart;
760
+ exports.convertInteractionsUsageToUsageMetadata = convertInteractionsUsageToUsageMetadata;
761
+ exports.convertMessagesToInteractionsInput = convertMessagesToInteractionsInput;
762
+ exports.convertMessagesToInteractionsSystemInstruction = convertMessagesToInteractionsSystemInstruction;
763
+ exports.normalizeInteractionStep = normalizeInteractionStep;
764
+ exports.synthesizeMetadataFromInteractions = synthesizeMetadataFromInteractions;
765
+
766
+ //# sourceMappingURL=messages.cjs.map