@langchain/google 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/chat_models/{api-types.d.ts → api_types/generate_content.d.cts} +198 -200
- package/dist/chat_models/{api-types.d.cts → api_types/generate_content.d.ts} +198 -200
- package/dist/chat_models/api_types/interactions.d.cts +1423 -0
- package/dist/chat_models/api_types/interactions.d.ts +1423 -0
- package/dist/chat_models/base.cjs +60 -403
- package/dist/chat_models/base.cjs.map +1 -1
- package/dist/chat_models/base.d.cts +13 -200
- package/dist/chat_models/base.d.ts +15 -202
- package/dist/chat_models/base.js +61 -402
- package/dist/chat_models/base.js.map +1 -1
- package/dist/chat_models/handlers/base.cjs +62 -0
- package/dist/chat_models/handlers/base.cjs.map +1 -0
- package/dist/chat_models/handlers/base.d.cts +46 -0
- package/dist/chat_models/handlers/base.d.ts +46 -0
- package/dist/chat_models/handlers/base.js +62 -0
- package/dist/chat_models/handlers/base.js.map +1 -0
- package/dist/chat_models/handlers/generate_content.cjs +344 -0
- package/dist/chat_models/handlers/generate_content.cjs.map +1 -0
- package/dist/chat_models/handlers/generate_content.d.cts +32 -0
- package/dist/chat_models/handlers/generate_content.d.ts +32 -0
- package/dist/chat_models/handlers/generate_content.js +344 -0
- package/dist/chat_models/handlers/generate_content.js.map +1 -0
- package/dist/chat_models/handlers/index.cjs +6 -0
- package/dist/chat_models/handlers/index.d.cts +4 -0
- package/dist/chat_models/handlers/index.d.ts +4 -0
- package/dist/chat_models/handlers/index.js +4 -0
- package/dist/chat_models/handlers/interactions.cjs +303 -0
- package/dist/chat_models/handlers/interactions.cjs.map +1 -0
- package/dist/chat_models/handlers/interactions.d.cts +33 -0
- package/dist/chat_models/handlers/interactions.d.ts +33 -0
- package/dist/chat_models/handlers/interactions.js +303 -0
- package/dist/chat_models/handlers/interactions.js.map +1 -0
- package/dist/chat_models/index.cjs +14 -6
- package/dist/chat_models/index.cjs.map +1 -1
- package/dist/chat_models/index.d.cts +7 -3
- package/dist/chat_models/index.d.ts +7 -3
- package/dist/chat_models/index.js +8 -4
- package/dist/chat_models/index.js.map +1 -1
- package/dist/chat_models/node.cjs +4 -5
- package/dist/chat_models/node.cjs.map +1 -1
- package/dist/chat_models/node.d.cts +1 -3
- package/dist/chat_models/node.d.ts +1 -3
- package/dist/chat_models/node.js +3 -4
- package/dist/chat_models/node.js.map +1 -1
- package/dist/chat_models/profiles.cjs +1 -2
- package/dist/chat_models/profiles.js +1 -2
- package/dist/chat_models/types.cjs +1 -2
- package/dist/chat_models/types.cjs.map +1 -1
- package/dist/chat_models/types.d.cts +776 -88
- package/dist/chat_models/types.d.ts +776 -88
- package/dist/chat_models/types.js +1 -2
- package/dist/chat_models/types.js.map +1 -1
- package/dist/clients/index.cjs +3 -4
- package/dist/clients/index.d.cts +0 -2
- package/dist/clients/index.d.ts +0 -2
- package/dist/clients/index.js +3 -4
- package/dist/clients/node.cjs +4 -5
- package/dist/clients/node.d.cts +0 -2
- package/dist/clients/node.d.ts +0 -2
- package/dist/clients/node.js +4 -5
- package/dist/const.cjs +1 -2
- package/dist/const.js +1 -2
- package/dist/converters/{messages.cjs → generate_content/messages.cjs} +4 -5
- package/dist/converters/generate_content/messages.cjs.map +1 -0
- package/dist/converters/{messages.js → generate_content/messages.js} +4 -5
- package/dist/converters/generate_content/messages.js.map +1 -0
- package/dist/converters/generate_content/params.cjs +135 -0
- package/dist/converters/generate_content/params.cjs.map +1 -0
- package/dist/converters/generate_content/params.js +131 -0
- package/dist/converters/generate_content/params.js.map +1 -0
- package/dist/{utils → converters/generate_content}/stream_events.cjs +2 -3
- package/dist/converters/generate_content/stream_events.cjs.map +1 -0
- package/dist/{utils → converters/generate_content}/stream_events.js +2 -3
- package/dist/converters/generate_content/stream_events.js.map +1 -0
- package/dist/converters/{tools.cjs → generate_content/tools.cjs} +4 -5
- package/dist/converters/generate_content/tools.cjs.map +1 -0
- package/dist/converters/{tools.js → generate_content/tools.js} +4 -5
- package/dist/converters/generate_content/tools.js.map +1 -0
- package/dist/converters/interactions/messages.cjs +766 -0
- package/dist/converters/interactions/messages.cjs.map +1 -0
- package/dist/converters/interactions/messages.js +759 -0
- package/dist/converters/interactions/messages.js.map +1 -0
- package/dist/converters/interactions/params.cjs +244 -0
- package/dist/converters/interactions/params.cjs.map +1 -0
- package/dist/converters/interactions/params.js +237 -0
- package/dist/converters/interactions/params.js.map +1 -0
- package/dist/converters/interactions/stream_events.cjs +769 -0
- package/dist/converters/interactions/stream_events.cjs.map +1 -0
- package/dist/converters/interactions/stream_events.js +768 -0
- package/dist/converters/interactions/stream_events.js.map +1 -0
- package/dist/converters/interactions/tools.cjs +229 -0
- package/dist/converters/interactions/tools.cjs.map +1 -0
- package/dist/converters/interactions/tools.js +228 -0
- package/dist/converters/interactions/tools.js.map +1 -0
- package/dist/converters/{params.cjs → shared/params.cjs} +8 -94
- package/dist/converters/shared/params.cjs.map +1 -0
- package/dist/converters/{params.js → shared/params.js} +8 -92
- package/dist/converters/shared/params.js.map +1 -0
- package/dist/index.cjs +8 -0
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/utils/errors.cjs +1 -2
- package/dist/utils/errors.js +1 -2
- package/dist/utils/gcp-auth.cjs +3 -4
- package/dist/utils/gcp-auth.d.cts +0 -2
- package/dist/utils/gcp-auth.d.ts +0 -2
- package/dist/utils/gcp-auth.js +3 -4
- package/dist/utils/handler.cjs +1 -2
- package/dist/utils/handler.d.cts +0 -2
- package/dist/utils/handler.d.ts +0 -2
- package/dist/utils/handler.js +1 -2
- package/dist/utils/metadata.cjs +9 -2
- package/dist/utils/metadata.cjs.map +1 -1
- package/dist/utils/metadata.js +9 -3
- package/dist/utils/metadata.js.map +1 -1
- package/dist/utils/misc.cjs +1 -2
- package/dist/utils/misc.cjs.map +1 -1
- package/dist/utils/misc.d.cts +2 -3
- package/dist/utils/misc.d.ts +2 -3
- package/dist/utils/misc.js +1 -2
- package/dist/utils/misc.js.map +1 -1
- package/dist/utils/stream.cjs +1 -2
- package/dist/utils/stream.js +1 -2
- package/package.json +12 -10
- package/dist/converters/messages.cjs.map +0 -1
- package/dist/converters/messages.js.map +0 -1
- package/dist/converters/params.cjs.map +0 -1
- package/dist/converters/params.js.map +0 -1
- package/dist/converters/tools.cjs.map +0 -1
- package/dist/converters/tools.js.map +0 -1
- package/dist/utils/stream_events.cjs.map +0 -1
- package/dist/utils/stream_events.js.map +0 -1
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
import { GoogleError } from "../../utils/errors.js";
|
|
2
|
+
import { convertGeminiPartToContentBlock, convertGeminiPartsToToolCalls, convertMessagesToGeminiContents, convertMessagesToGeminiSystemInstruction } from "../generate_content/messages.js";
|
|
3
|
+
import { AIMessage, SystemMessage, ToolMessage } from "@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 (SystemMessage.isInstance(message)) return convertMessagesToGeminiSystemInstruction([message])?.parts ?? [];
|
|
24
|
+
if (ToolMessage.isInstance(message)) {
|
|
25
|
+
const aiMsg = messages?.filter(AIMessage.isInstance).find((msg) => msg.tool_calls?.find((tc) => tc.id === message.tool_call_id));
|
|
26
|
+
const fallbackAiMsg = !aiMsg && message.tool_call_id ? new 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 = convertMessagesToGeminiContents(contextMessages);
|
|
34
|
+
return contents[contents.length - 1]?.parts ?? [];
|
|
35
|
+
}
|
|
36
|
+
return 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 (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 && SystemMessage.isInstance(message)) continue;
|
|
200
|
+
isLeadingSystemMessages = false;
|
|
201
|
+
if (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 (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(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 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) => 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 = 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 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
|
+
export { convertGeminiPartToInteractionsContent, convertInteractionToAIMessage, convertInteractionsContentToGeminiPart, convertInteractionsUsageToUsageMetadata, convertMessagesToInteractionsInput, convertMessagesToInteractionsSystemInstruction, normalizeInteractionStep, synthesizeMetadataFromInteractions };
|
|
758
|
+
|
|
759
|
+
//# sourceMappingURL=messages.js.map
|