@librechat/agents 3.3.9 → 3.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs +4 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +21 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +228 -16
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +14 -7
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +188 -11
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +4 -3
- package/dist/cjs/messages/core.cjs +592 -27
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/run.cjs +11 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +2 -2
- package/dist/cjs/tools/ToolNode.cjs +1 -1
- package/dist/cjs/tools/search/tool.cjs +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/esm/agents/AgentContext.mjs +4 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +21 -2
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +228 -16
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +14 -7
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +190 -13
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -5
- package/dist/esm/messages/core.mjs +592 -28
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/run.mjs +11 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +2 -2
- package/dist/esm/tools/ToolNode.mjs +1 -1
- package/dist/esm/tools/search/tool.mjs +1 -1
- package/dist/esm/utils/index.mjs +1 -1
- package/dist/types/agents/AgentContext.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +5 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +1 -0
- package/dist/types/llm/invoke.d.ts +1 -1
- package/dist/types/messages/core.d.ts +11 -6
- package/dist/types/run.d.ts +7 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +5 -0
- package/src/graphs/Graph.ts +39 -0
- package/src/langfuseToolOutputTracing.ts +410 -14
- package/src/llm/custom-chat-models.smoke.test.ts +747 -0
- package/src/llm/invoke.test.ts +98 -0
- package/src/llm/invoke.ts +34 -23
- package/src/llm/openai/index.ts +334 -25
- package/src/llm/openai/llm.spec.ts +107 -6
- package/src/messages/core.ts +1290 -42
- package/src/messages/formatAgentMessages.test.ts +2623 -0
- package/src/run.ts +15 -0
- package/src/specs/discovered-tools.test.ts +217 -0
- package/src/specs/langfuse-tool-output-tracing.test.ts +887 -0
- package/src/specs/preemptSeal.test.ts +374 -5
|
@@ -5,6 +5,8 @@ let _langfuse_tracing = require("@langfuse/tracing");
|
|
|
5
5
|
let _langfuse_otel = require("@langfuse/otel");
|
|
6
6
|
//#region src/langfuseToolOutputTracing.ts
|
|
7
7
|
const LANGGRAPH_TOOL_NODE_PREFIX = "tools=";
|
|
8
|
+
const SERVER_TOOL_RESULT_PREFIX = "{\"serverToolResult\":";
|
|
9
|
+
const SERVER_TOOL_RESULT_REPLAY_MARKER_KEY = "librechatResponsesReplay";
|
|
8
10
|
const CHAT_ROLES = new Set([
|
|
9
11
|
"assistant",
|
|
10
12
|
"developer",
|
|
@@ -13,6 +15,65 @@ const CHAT_ROLES = new Set([
|
|
|
13
15
|
"user"
|
|
14
16
|
]);
|
|
15
17
|
const TOOL_OUTPUT_FIELD_KEYS = ["content", "artifact"];
|
|
18
|
+
const RESPONSES_REPLAY_OUTPUT_DESCRIPTORS = {
|
|
19
|
+
local_shell_call_output: {
|
|
20
|
+
outputFields: ["output"],
|
|
21
|
+
toolName: "local_shell"
|
|
22
|
+
},
|
|
23
|
+
shell_call_output: {
|
|
24
|
+
outputFields: ["output"],
|
|
25
|
+
toolName: "shell"
|
|
26
|
+
},
|
|
27
|
+
apply_patch_call_output: {
|
|
28
|
+
outputFields: ["output"],
|
|
29
|
+
toolName: "apply_patch"
|
|
30
|
+
},
|
|
31
|
+
program_output: {
|
|
32
|
+
outputFields: ["result"],
|
|
33
|
+
toolName: "program"
|
|
34
|
+
},
|
|
35
|
+
code_interpreter_call: {
|
|
36
|
+
outputFields: ["outputs"],
|
|
37
|
+
toolName: "code_interpreter"
|
|
38
|
+
},
|
|
39
|
+
mcp_call: {
|
|
40
|
+
outputFields: ["output", "error"],
|
|
41
|
+
toolName: "mcp"
|
|
42
|
+
},
|
|
43
|
+
mcp_list_tools: {
|
|
44
|
+
outputFields: ["tools", "error"],
|
|
45
|
+
toolName: "mcp_list_tools"
|
|
46
|
+
},
|
|
47
|
+
image_generation_call: {
|
|
48
|
+
outputFields: ["result"],
|
|
49
|
+
toolName: "image_generation"
|
|
50
|
+
},
|
|
51
|
+
file_search_call: {
|
|
52
|
+
outputFields: ["results"],
|
|
53
|
+
toolName: "file_search"
|
|
54
|
+
},
|
|
55
|
+
web_search_call: {
|
|
56
|
+
nestedOutputFields: { action: ["sources"] },
|
|
57
|
+
outputFields: ["results"],
|
|
58
|
+
toolName: "web_search"
|
|
59
|
+
},
|
|
60
|
+
tool_search_output: {
|
|
61
|
+
outputFields: ["tools"],
|
|
62
|
+
toolName: "tool_search"
|
|
63
|
+
},
|
|
64
|
+
function_call_output: {
|
|
65
|
+
outputFields: ["output"],
|
|
66
|
+
resolveToolNameFromCallId: true
|
|
67
|
+
},
|
|
68
|
+
custom_tool_call_output: {
|
|
69
|
+
outputFields: ["output"],
|
|
70
|
+
resolveToolNameFromCallId: true
|
|
71
|
+
},
|
|
72
|
+
computer_call_output: {
|
|
73
|
+
outputFields: ["output"],
|
|
74
|
+
toolName: "computer_use"
|
|
75
|
+
}
|
|
76
|
+
};
|
|
16
77
|
function isRecord(value) {
|
|
17
78
|
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
18
79
|
}
|
|
@@ -47,7 +108,7 @@ function getNestedStringField(value, objectKey, fieldKey) {
|
|
|
47
108
|
return getStringField(nested, fieldKey);
|
|
48
109
|
}
|
|
49
110
|
function getSerializedToolCallId(value) {
|
|
50
|
-
return getStringField(value, "tool_call_id") ?? getNestedStringField(value, "kwargs", "tool_call_id") ?? getNestedStringField(value, "additional_kwargs", "tool_call_id") ?? getNestedStringField(value, "data", "tool_call_id") ?? (typeof value.id === "string" ? value.id : void 0);
|
|
111
|
+
return getStringField(value, "tool_call_id") ?? getStringField(value, "toolCallId") ?? getStringField(value, "call_id") ?? getNestedStringField(value, "kwargs", "tool_call_id") ?? getNestedStringField(value, "additional_kwargs", "tool_call_id") ?? getNestedStringField(value, "data", "tool_call_id") ?? (typeof value.id === "string" ? value.id : void 0);
|
|
51
112
|
}
|
|
52
113
|
function getSerializedToolName(value, redactionContext) {
|
|
53
114
|
const role = getStringField(value, "role");
|
|
@@ -65,6 +126,13 @@ function hasToolMessageIdentity(value) {
|
|
|
65
126
|
const role = getStringField(value, "role");
|
|
66
127
|
return role != null && !CHAT_ROLES.has(role.toLowerCase()) && ("content" in value || isRecord(value.kwargs) || isRecord(value.data));
|
|
67
128
|
}
|
|
129
|
+
function hasAssistantMessageIdentity(value) {
|
|
130
|
+
if (getStringField(value, "role")?.toLowerCase() === "assistant") return true;
|
|
131
|
+
const type = getStringField(value, "type") ?? getStringField(value, "_type");
|
|
132
|
+
if (type === "ai" || type === "assistant") return true;
|
|
133
|
+
const id = value.id;
|
|
134
|
+
return Array.isArray(id) && id.some((part) => typeof part === "string" && (part === "AIMessage" || part === "AIMessageChunk"));
|
|
135
|
+
}
|
|
68
136
|
function redactToolContentFields(value, config) {
|
|
69
137
|
const next = { ...value };
|
|
70
138
|
for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) if (outputKey in next) next[outputKey] = config.redactionText;
|
|
@@ -85,23 +153,152 @@ function redactToolContentFields(value, config) {
|
|
|
85
153
|
}
|
|
86
154
|
return next;
|
|
87
155
|
}
|
|
88
|
-
function
|
|
156
|
+
function redactResponsesReplayOutput(value, config, redactionContext) {
|
|
157
|
+
const type = getStringField(value, "type");
|
|
158
|
+
if (type == null || !Object.prototype.hasOwnProperty.call(RESPONSES_REPLAY_OUTPUT_DESCRIPTORS, type)) return;
|
|
159
|
+
const descriptor = RESPONSES_REPLAY_OUTPUT_DESCRIPTORS[type];
|
|
160
|
+
const hasDirectOutput = descriptor.outputFields.some((outputField) => outputField in value);
|
|
161
|
+
const hasNestedOutput = Object.entries(descriptor.nestedOutputFields ?? {}).some(([objectField, outputFields]) => {
|
|
162
|
+
const nested = value[objectField];
|
|
163
|
+
return isRecord(nested) && outputFields.some((outputField) => outputField in nested);
|
|
164
|
+
});
|
|
165
|
+
if (!hasDirectOutput && !hasNestedOutput) return;
|
|
166
|
+
let toolName = descriptor.toolName;
|
|
167
|
+
if (descriptor.resolveToolNameFromCallId === true) toolName = getSerializedToolName(value, redactionContext);
|
|
168
|
+
else if (type === "mcp_call") toolName = getStringField(value, "name") ?? descriptor.toolName;
|
|
169
|
+
if (!shouldRedactTool(toolName, config)) return;
|
|
170
|
+
const redacted = { ...value };
|
|
171
|
+
for (const outputField of descriptor.outputFields) if (outputField in redacted) redacted[outputField] = config.redactionText;
|
|
172
|
+
for (const [objectField, outputFields] of Object.entries(descriptor.nestedOutputFields ?? {})) {
|
|
173
|
+
const nested = redacted[objectField];
|
|
174
|
+
if (!isRecord(nested)) continue;
|
|
175
|
+
const redactedNested = { ...nested };
|
|
176
|
+
for (const outputField of outputFields) if (outputField in redactedNested) redactedNested[outputField] = config.redactionText;
|
|
177
|
+
redacted[objectField] = redactedNested;
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
value: redacted,
|
|
181
|
+
changed: true
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function redactStandardServerToolResult(value, config, redactionContext) {
|
|
185
|
+
if (getStringField(value, "type") !== "server_tool_call_result" || !("output" in value)) return;
|
|
186
|
+
if (!shouldRedactTool(getNestedStringField(value, "extras", "name") ?? getSerializedToolName(value, redactionContext), config)) return;
|
|
187
|
+
return {
|
|
188
|
+
value: {
|
|
189
|
+
...value,
|
|
190
|
+
output: config.redactionText
|
|
191
|
+
},
|
|
192
|
+
changed: true
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function parseSerializedServerToolResultMarker(text) {
|
|
196
|
+
if (!text.startsWith(SERVER_TOOL_RESULT_PREFIX)) return;
|
|
197
|
+
try {
|
|
198
|
+
const parsed = JSON.parse(text);
|
|
199
|
+
if (!isRecord(parsed) || !isRecord(parsed.serverToolResult)) return;
|
|
200
|
+
const result = parsed.serverToolResult;
|
|
201
|
+
if (result[SERVER_TOOL_RESULT_REPLAY_MARKER_KEY] !== true) return;
|
|
202
|
+
const status = getStringField(result, "status");
|
|
203
|
+
if (status !== "error" && status !== "success" || !Object.prototype.hasOwnProperty.call(result, "output")) return;
|
|
204
|
+
const toolName = getStringField(result, "toolName");
|
|
205
|
+
return toolName != null ? { toolName } : {};
|
|
206
|
+
} catch {
|
|
207
|
+
const match = /^\{"serverToolResult":\{"librechatResponsesReplay":true,(?:"toolName":("(?:\\.|[^"\\])*"),)?"status":"(?:error|success)","output":/.exec(text.slice(0, 512));
|
|
208
|
+
if (match == null) return;
|
|
209
|
+
const encodedToolName = match.at(1);
|
|
210
|
+
if (encodedToolName == null) return {};
|
|
211
|
+
try {
|
|
212
|
+
const toolName = JSON.parse(encodedToolName);
|
|
213
|
+
return typeof toolName === "string" ? { toolName } : {};
|
|
214
|
+
} catch {
|
|
215
|
+
return {};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function redactMarkedServerToolResult(value, config, allowSerializedMarker) {
|
|
220
|
+
const type = getStringField(value, "type");
|
|
221
|
+
if (type !== "text" && type !== "image") return;
|
|
222
|
+
const text = type === "text" ? getStringField(value, "text") : void 0;
|
|
223
|
+
const extras = value.extras;
|
|
224
|
+
const marker = isRecord(extras) ? extras.librechatServerToolResult : void 0;
|
|
225
|
+
const serializedMarker = allowSerializedMarker && text != null ? parseSerializedServerToolResultMarker(text) : void 0;
|
|
226
|
+
if (!isRecord(marker) && serializedMarker == null) return;
|
|
227
|
+
if (!shouldRedactTool(isRecord(marker) ? getStringField(marker, "toolName") : serializedMarker?.toolName, config)) return;
|
|
228
|
+
if (type === "image") {
|
|
229
|
+
const redacted = { ...value };
|
|
230
|
+
let changed = false;
|
|
231
|
+
for (const outputField of [
|
|
232
|
+
"data",
|
|
233
|
+
"url",
|
|
234
|
+
"fileId"
|
|
235
|
+
]) if (outputField in redacted) {
|
|
236
|
+
redacted[outputField] = config.redactionText;
|
|
237
|
+
changed = true;
|
|
238
|
+
}
|
|
239
|
+
return changed ? {
|
|
240
|
+
value: redacted,
|
|
241
|
+
changed: true
|
|
242
|
+
} : void 0;
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
value: {
|
|
246
|
+
...value,
|
|
247
|
+
text: config.redactionText
|
|
248
|
+
},
|
|
249
|
+
changed: true
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function redactGeneratedImageBlock(value, config, redactionContext, isAssistantContent) {
|
|
253
|
+
if (getStringField(value, "type") !== "image" || !shouldRedactTool("image_generation", config)) return;
|
|
254
|
+
const extras = value.extras;
|
|
255
|
+
const explicitMarker = isRecord(extras) ? extras.librechatServerToolResult : void 0;
|
|
256
|
+
const explicitToolName = isRecord(explicitMarker) ? getStringField(explicitMarker, "toolName") : void 0;
|
|
257
|
+
if (explicitToolName != null && require_langfuseConfig.normalizeToolName(explicitToolName) !== "image_generation") return;
|
|
258
|
+
const id = getStringField(value, "id");
|
|
259
|
+
const data = getStringField(value, "data");
|
|
260
|
+
const metadata = value.metadata;
|
|
261
|
+
const normalizedStatus = isRecord(metadata) ? getStringField(metadata, "status") : void 0;
|
|
262
|
+
const hasNormalizedGeneratedImageIdentity = isAssistantContent && id?.startsWith("ig_") === true && isPresent(normalizedStatus);
|
|
263
|
+
if (!(explicitToolName != null || hasNormalizedGeneratedImageIdentity || (id != null ? redactionContext.generatedImageIds.has(id) : data != null && redactionContext.generatedImageData.has(data)))) return;
|
|
264
|
+
const redacted = { ...value };
|
|
265
|
+
let changed = false;
|
|
266
|
+
for (const outputField of [
|
|
267
|
+
"data",
|
|
268
|
+
"url",
|
|
269
|
+
"fileId"
|
|
270
|
+
]) if (outputField in redacted) {
|
|
271
|
+
redacted[outputField] = config.redactionText;
|
|
272
|
+
changed = true;
|
|
273
|
+
}
|
|
274
|
+
return changed ? {
|
|
275
|
+
value: redacted,
|
|
276
|
+
changed: true
|
|
277
|
+
} : void 0;
|
|
278
|
+
}
|
|
279
|
+
function collectRedactionContext(value, redactionContext) {
|
|
89
280
|
if (Array.isArray(value)) {
|
|
90
|
-
for (const item of value)
|
|
281
|
+
for (const item of value) collectRedactionContext(item, redactionContext);
|
|
91
282
|
return;
|
|
92
283
|
}
|
|
93
284
|
if (!isRecord(value)) return;
|
|
94
285
|
const toolCallId = getSerializedToolCallId(value);
|
|
95
286
|
const toolName = getSerializedToolName(value);
|
|
96
287
|
if (toolCallId != null && toolName != null) redactionContext.toolNamesByCallId.set(toolCallId, toolName);
|
|
97
|
-
|
|
288
|
+
if (getStringField(value, "type") === "image_generation_call") {
|
|
289
|
+
const id = getStringField(value, "id");
|
|
290
|
+
const result = getStringField(value, "result");
|
|
291
|
+
if (id != null) redactionContext.generatedImageIds.add(id);
|
|
292
|
+
if (result != null) redactionContext.generatedImageData.add(result);
|
|
293
|
+
}
|
|
294
|
+
for (const child of Object.values(value)) collectRedactionContext(child, redactionContext);
|
|
98
295
|
}
|
|
99
|
-
function redactValue(value, config, redactionContext) {
|
|
296
|
+
function redactValue(value, config, redactionContext, allowSerializedServerToolResult = false) {
|
|
100
297
|
if (Array.isArray(value)) {
|
|
101
298
|
let changed = false;
|
|
102
299
|
const next = [];
|
|
103
300
|
for (const item of value) {
|
|
104
|
-
const result = redactValue(item, config, redactionContext);
|
|
301
|
+
const result = redactValue(item, config, redactionContext, allowSerializedServerToolResult);
|
|
105
302
|
if (result.changed) changed = true;
|
|
106
303
|
next.push(result.value);
|
|
107
304
|
}
|
|
@@ -117,6 +314,15 @@ function redactValue(value, config, redactionContext) {
|
|
|
117
314
|
value,
|
|
118
315
|
changed: false
|
|
119
316
|
};
|
|
317
|
+
const allowSerializedMarker = allowSerializedServerToolResult || hasAssistantMessageIdentity(value);
|
|
318
|
+
const replayOutput = redactResponsesReplayOutput(value, config, redactionContext);
|
|
319
|
+
if (replayOutput != null) return replayOutput;
|
|
320
|
+
const standardServerToolResult = redactStandardServerToolResult(value, config, redactionContext);
|
|
321
|
+
if (standardServerToolResult != null) return standardServerToolResult;
|
|
322
|
+
const markedServerToolResult = redactMarkedServerToolResult(value, config, allowSerializedMarker);
|
|
323
|
+
if (markedServerToolResult != null) return markedServerToolResult;
|
|
324
|
+
const generatedImageBlock = redactGeneratedImageBlock(value, config, redactionContext, allowSerializedMarker);
|
|
325
|
+
if (generatedImageBlock != null) return generatedImageBlock;
|
|
120
326
|
const toolName = getSerializedToolName(value, redactionContext);
|
|
121
327
|
if (hasToolMessageIdentity(value) && shouldRedactTool(toolName, config)) return {
|
|
122
328
|
value: redactToolContentFields(value, config),
|
|
@@ -125,7 +331,7 @@ function redactValue(value, config, redactionContext) {
|
|
|
125
331
|
let changed = false;
|
|
126
332
|
const next = {};
|
|
127
333
|
for (const [key, child] of Object.entries(value)) {
|
|
128
|
-
const result = redactValue(child, config, redactionContext);
|
|
334
|
+
const result = redactValue(child, config, redactionContext, allowSerializedMarker);
|
|
129
335
|
if (result.changed) changed = true;
|
|
130
336
|
next[key] = result.value;
|
|
131
337
|
}
|
|
@@ -138,9 +344,13 @@ function redactValue(value, config, redactionContext) {
|
|
|
138
344
|
};
|
|
139
345
|
}
|
|
140
346
|
function redactSerializedValue(value, config) {
|
|
141
|
-
const redactionContext = {
|
|
347
|
+
const redactionContext = {
|
|
348
|
+
generatedImageData: /* @__PURE__ */ new Set(),
|
|
349
|
+
generatedImageIds: /* @__PURE__ */ new Set(),
|
|
350
|
+
toolNamesByCallId: /* @__PURE__ */ new Map()
|
|
351
|
+
};
|
|
142
352
|
if (typeof value !== "string") {
|
|
143
|
-
|
|
353
|
+
collectRedactionContext(value, redactionContext);
|
|
144
354
|
return redactValue(value, config, redactionContext);
|
|
145
355
|
}
|
|
146
356
|
const trimmed = value.trim();
|
|
@@ -150,7 +360,7 @@ function redactSerializedValue(value, config) {
|
|
|
150
360
|
};
|
|
151
361
|
try {
|
|
152
362
|
const parsed = JSON.parse(value);
|
|
153
|
-
|
|
363
|
+
collectRedactionContext(parsed, redactionContext);
|
|
154
364
|
const result = redactValue(parsed, config, redactionContext);
|
|
155
365
|
return result.changed ? {
|
|
156
366
|
value: JSON.stringify(result.value),
|
|
@@ -161,8 +371,8 @@ function redactSerializedValue(value, config) {
|
|
|
161
371
|
};
|
|
162
372
|
} catch {
|
|
163
373
|
return {
|
|
164
|
-
value,
|
|
165
|
-
changed:
|
|
374
|
+
value: config.redactionText,
|
|
375
|
+
changed: true
|
|
166
376
|
};
|
|
167
377
|
}
|
|
168
378
|
}
|
|
@@ -200,6 +410,11 @@ function redactLangfuseSpanToolOutputs(span, config) {
|
|
|
200
410
|
_langfuse_tracing.LangfuseOtelSpanAttributes.TRACE_OUTPUT
|
|
201
411
|
]) redactAttribute(attributes, key, config);
|
|
202
412
|
}
|
|
413
|
+
function prepareLangfuseSpanForExport(span, config) {
|
|
414
|
+
classifyLangfuseToolNodeSpan(span);
|
|
415
|
+
if (config != null) redactLangfuseSpanToolOutputs(span, config);
|
|
416
|
+
require_langfuseTraceShaping.shapeLangfuseSpan(span);
|
|
417
|
+
}
|
|
203
418
|
var ToolOutputRedactingLangfuseSpanProcessor = class {
|
|
204
419
|
processor;
|
|
205
420
|
fallbackConfig;
|
|
@@ -216,10 +431,7 @@ var ToolOutputRedactingLangfuseSpanProcessor = class {
|
|
|
216
431
|
}
|
|
217
432
|
onEnd(span) {
|
|
218
433
|
if (require_langfuseTraceShaping.shouldDropLangfuseSpan(span.name)) return;
|
|
219
|
-
|
|
220
|
-
require_langfuseTraceShaping.shapeLangfuseSpan(span);
|
|
221
|
-
const config = this.spanConfigs.get(span) ?? this.fallbackConfig;
|
|
222
|
-
if (config != null) redactLangfuseSpanToolOutputs(span, config);
|
|
434
|
+
prepareLangfuseSpanForExport(span, this.spanConfigs.get(span) ?? this.fallbackConfig);
|
|
223
435
|
this.processor.onEnd(span);
|
|
224
436
|
}
|
|
225
437
|
forceFlush() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langfuseToolOutputTracing.cjs","names":["normalizeToolName","LangfuseOtelSpanAttributes","LangfuseSpanProcessor","shouldDropLangfuseSpan","resolveToolOutputTracingConfigForSpan","hasToolOutputTracingConfig","resolveToolOutputTracingConfig","resolveLangfuseConfig"],"sources":["../../src/langfuseToolOutputTracing.ts"],"sourcesContent":["import { LangfuseSpanProcessor } from '@langfuse/otel';\nimport { LangfuseOtelSpanAttributes } from '@langfuse/tracing';\nimport type {\n ReadableSpan,\n Span,\n SpanProcessor,\n} from '@opentelemetry/sdk-trace-base';\nimport type { LangfuseSpanProcessorParams } from '@langfuse/otel';\nimport type { Context } from '@opentelemetry/api';\nimport type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\nimport type * as t from '@/types';\nimport {\n LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,\n hasToolOutputTracingConfig,\n normalizeToolName,\n resolveLangfuseConfig,\n resolveToolOutputTracingConfig,\n} from '@/langfuseConfig';\nimport {\n shapeLangfuseSpan,\n shouldDropLangfuseSpan,\n} from '@/langfuseTraceShaping';\nimport { resolveToolOutputTracingConfigForSpan } from '@/langfuseRuntimeScope';\n\nexport { LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT, resolveLangfuseConfig };\n\nconst LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';\n\nconst CHAT_ROLES = new Set([\n 'assistant',\n 'developer',\n 'human',\n 'system',\n 'user',\n]);\n\ntype SpanWithAttributes = ReadableSpan & {\n attributes: Record<string, unknown>;\n};\n\ntype RedactionResult = {\n value: unknown;\n changed: boolean;\n};\n\ntype RedactionContext = {\n toolNamesByCallId: Map<string, string>;\n};\n\nconst TOOL_OUTPUT_FIELD_KEYS = ['content', 'artifact'];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isPresent(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction shouldApplyToolOutputRedaction(\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || config.redactedToolNames.size > 0;\n}\n\nfunction toolNameMatches(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n if (!isPresent(toolName)) {\n return false;\n }\n\n const normalizedToolName = normalizeToolName(toolName);\n if (config.redactedToolNameMatchMode === 'partial') {\n for (const redactedToolName of config.redactedToolNames) {\n if (normalizedToolName.includes(redactedToolName)) {\n return true;\n }\n }\n return false;\n }\n\n return config.redactedToolNames.has(normalizedToolName);\n}\n\n/** Whether a tool's outputs are excluded from tracing (global disable or\n * `redactedToolNames` match). Exported for the activity-label prompt\n * builder, whose prompt becomes Langfuse generation input. */\nexport function shouldRedactTool(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || toolNameMatches(toolName, config);\n}\n\nfunction getStringField(\n value: Record<string, unknown>,\n key: string\n): string | undefined {\n const field = value[key];\n return typeof field === 'string' ? field : undefined;\n}\n\nfunction getNestedStringField(\n value: Record<string, unknown>,\n objectKey: string,\n fieldKey: string\n): string | undefined {\n const nested = value[objectKey];\n if (!isRecord(nested)) {\n return undefined;\n }\n return getStringField(nested, fieldKey);\n}\n\nfunction getSerializedToolCallId(\n value: Record<string, unknown>\n): string | undefined {\n return (\n getStringField(value, 'tool_call_id') ??\n getNestedStringField(value, 'kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'data', 'tool_call_id') ??\n (typeof value.id === 'string' ? value.id : undefined)\n );\n}\n\nfunction getSerializedToolName(\n value: Record<string, unknown>,\n redactionContext?: RedactionContext\n): string | undefined {\n const role = getStringField(value, 'role');\n const explicitName =\n getStringField(value, 'name') ??\n getStringField(value, 'tool_name') ??\n getNestedStringField(value, 'function', 'name') ??\n getNestedStringField(value, 'kwargs', 'name') ??\n getNestedStringField(value, 'additional_kwargs', 'name') ??\n getNestedStringField(value, 'data', 'name') ??\n (role != null && role.toLowerCase() !== 'tool' ? role : undefined);\n\n if (explicitName != null) {\n return explicitName;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n return toolCallId != null\n ? redactionContext?.toolNamesByCallId.get(toolCallId)\n : undefined;\n}\n\nfunction hasToolMessageIdentity(value: Record<string, unknown>): boolean {\n const type = getStringField(value, 'type') ?? getStringField(value, '_type');\n if (type === 'tool' || type === 'tool_message') {\n return true;\n }\n\n const id = value.id;\n if (\n Array.isArray(id) &&\n id.some((part) => typeof part === 'string' && part.includes('ToolMessage'))\n ) {\n return true;\n }\n\n if (\n 'tool_call_id' in value ||\n getNestedStringField(value, 'kwargs', 'tool_call_id') != null ||\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') != null\n ) {\n return true;\n }\n\n const role = getStringField(value, 'role');\n return (\n role != null &&\n !CHAT_ROLES.has(role.toLowerCase()) &&\n ('content' in value || isRecord(value.kwargs) || isRecord(value.data))\n );\n}\n\nfunction redactToolContentFields(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): Record<string, unknown> {\n const next = { ...value };\n\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in next) {\n next[outputKey] = config.redactionText;\n }\n }\n\n for (const nestedKey of ['kwargs', 'data', 'additional_kwargs']) {\n const nested = next[nestedKey];\n if (!isRecord(nested)) {\n continue;\n }\n const nextNested = { ...nested };\n let changed = false;\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in nextNested) {\n nextNested[outputKey] = config.redactionText;\n changed = true;\n }\n }\n if (changed) {\n next[nestedKey] = nextNested;\n }\n }\n\n return next;\n}\n\nfunction collectToolCallNames(\n value: unknown,\n redactionContext: RedactionContext\n): void {\n if (Array.isArray(value)) {\n for (const item of value) {\n collectToolCallNames(item, redactionContext);\n }\n return;\n }\n\n if (!isRecord(value)) {\n return;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n const toolName = getSerializedToolName(value);\n if (toolCallId != null && toolName != null) {\n redactionContext.toolNamesByCallId.set(toolCallId, toolName);\n }\n\n for (const child of Object.values(value)) {\n collectToolCallNames(child, redactionContext);\n }\n}\n\nfunction redactValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig,\n redactionContext: RedactionContext\n): RedactionResult {\n if (Array.isArray(value)) {\n let changed = false;\n const next: unknown[] = [];\n for (const item of value) {\n const result = redactValue(item, config, redactionContext);\n if (result.changed) {\n changed = true;\n }\n next.push(result.value);\n }\n return changed ? { value: next, changed } : { value, changed };\n }\n\n if (!isRecord(value)) {\n return { value, changed: false };\n }\n\n const toolName = getSerializedToolName(value, redactionContext);\n if (hasToolMessageIdentity(value) && shouldRedactTool(toolName, config)) {\n return {\n value: redactToolContentFields(value, config),\n changed: true,\n };\n }\n\n let changed = false;\n const next: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n const result = redactValue(child, config, redactionContext);\n if (result.changed) {\n changed = true;\n }\n next[key] = result.value;\n }\n\n return changed ? { value: next, changed } : { value, changed };\n}\n\nfunction redactSerializedValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig\n): RedactionResult {\n const redactionContext: RedactionContext = {\n toolNamesByCallId: new Map(),\n };\n if (typeof value !== 'string') {\n collectToolCallNames(value, redactionContext);\n return redactValue(value, config, redactionContext);\n }\n\n const trimmed = value.trim();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return { value, changed: false };\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n collectToolCallNames(parsed, redactionContext);\n const result = redactValue(parsed, config, redactionContext);\n return result.changed\n ? { value: JSON.stringify(result.value), changed: true }\n : { value, changed: false };\n } catch {\n return { value, changed: false };\n }\n}\n\nfunction redactAttribute(\n attributes: Record<string, unknown>,\n key: string,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (!(key in attributes)) {\n return;\n }\n\n const result = redactSerializedValue(attributes[key], config);\n if (result.changed) {\n attributes[key] = result.value;\n }\n}\n\nfunction isToolObservation(attributes: Record<string, unknown>): boolean {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n return typeof type === 'string' && type.toLowerCase() === 'tool';\n}\n\nfunction classifyLangGraphToolNodeSpan(\n attributes: Record<string, unknown>\n): void {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n if (typeof type !== 'string' || type.toLowerCase() !== 'span') {\n return;\n }\n\n const langGraphNode =\n attributes[\n `${LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.langgraph_node`\n ];\n if (\n typeof langGraphNode === 'string' &&\n langGraphNode.startsWith(LANGGRAPH_TOOL_NODE_PREFIX)\n ) {\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE] = 'tool';\n }\n}\n\nexport function classifyLangfuseToolNodeSpan(span: ReadableSpan): void {\n classifyLangGraphToolNodeSpan((span as SpanWithAttributes).attributes);\n}\n\nfunction redactToolObservationOutput(\n span: ReadableSpan,\n attributes: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (\n !(\n isToolObservation(attributes) &&\n shouldRedactTool(span.name, config) &&\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT in attributes\n )\n ) {\n return;\n }\n\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT] =\n config.redactionText;\n}\n\nexport function redactLangfuseSpanToolOutputs(\n span: ReadableSpan,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n const attributes = (span as SpanWithAttributes).attributes;\n classifyLangfuseToolNodeSpan(span);\n\n if (!shouldApplyToolOutputRedaction(config)) {\n return;\n }\n\n redactToolObservationOutput(span, attributes, config);\n\n for (const key of [\n LangfuseOtelSpanAttributes.OBSERVATION_INPUT,\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT,\n LangfuseOtelSpanAttributes.TRACE_INPUT,\n LangfuseOtelSpanAttributes.TRACE_OUTPUT,\n ]) {\n redactAttribute(attributes, key, config);\n }\n}\n\nclass ToolOutputRedactingLangfuseSpanProcessor implements SpanProcessor {\n private readonly processor: LangfuseSpanProcessor;\n private readonly fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig;\n private readonly spanConfigs = new WeakMap<\n object,\n ResolvedLangfuseToolOutputTracingConfig\n >();\n\n constructor(\n params?: LangfuseSpanProcessorParams,\n fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig\n ) {\n this.processor = new LangfuseSpanProcessor(params);\n this.fallbackConfig = fallbackConfig;\n }\n\n onStart(span: Span, parentContext: Context): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n const config =\n resolveToolOutputTracingConfigForSpan(parentContext) ??\n this.fallbackConfig;\n if (config != null) {\n this.spanConfigs.set(span, config);\n }\n this.processor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n classifyLangfuseToolNodeSpan(span);\n shapeLangfuseSpan(span);\n const config = this.spanConfigs.get(span) ?? this.fallbackConfig;\n if (config != null) {\n redactLangfuseSpanToolOutputs(span, config);\n }\n this.processor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.processor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.processor.shutdown();\n }\n}\n\nexport function createLangfuseSpanProcessor(\n params?: LangfuseSpanProcessorParams,\n runLangfuse?: t.LangfuseConfig,\n agentLangfuse?: t.LangfuseConfig\n): SpanProcessor {\n const fallbackConfig = hasToolOutputTracingConfig(runLangfuse, agentLangfuse)\n ? resolveToolOutputTracingConfig(runLangfuse, agentLangfuse)\n : undefined;\n return new ToolOutputRedactingLangfuseSpanProcessor(params, fallbackConfig);\n}\n\nfunction hasLangfuseEnvKeys(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nfunction hasLangfuseConfigKeys(langfuse?: t.LangfuseConfig): boolean {\n if (langfuse == null) {\n return false;\n }\n return isPresent(langfuse.secretKey) && isPresent(langfuse.publicKey);\n}\n\nexport function shouldTraceToolNodeForLangfuse({\n runLangfuse,\n agentLangfuse,\n}: {\n runLangfuse?: t.LangfuseConfig;\n agentLangfuse?: t.LangfuseConfig;\n}): boolean {\n const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);\n if (langfuse?.enabled === false) {\n return false;\n }\n\n const explicit = langfuse?.toolNodeTracing?.enabled;\n if (explicit !== true) {\n return false;\n }\n\n return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"langfuseToolOutputTracing.cjs","names":["normalizeToolName","LangfuseOtelSpanAttributes","LangfuseSpanProcessor","shouldDropLangfuseSpan","resolveToolOutputTracingConfigForSpan","hasToolOutputTracingConfig","resolveToolOutputTracingConfig","resolveLangfuseConfig"],"sources":["../../src/langfuseToolOutputTracing.ts"],"sourcesContent":["import { LangfuseSpanProcessor } from '@langfuse/otel';\nimport { LangfuseOtelSpanAttributes } from '@langfuse/tracing';\nimport type {\n ReadableSpan,\n Span,\n SpanProcessor,\n} from '@opentelemetry/sdk-trace-base';\nimport type { LangfuseSpanProcessorParams } from '@langfuse/otel';\nimport type { Context } from '@opentelemetry/api';\nimport type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\nimport type * as t from '@/types';\nimport {\n LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,\n hasToolOutputTracingConfig,\n normalizeToolName,\n resolveLangfuseConfig,\n resolveToolOutputTracingConfig,\n} from '@/langfuseConfig';\nimport {\n shapeLangfuseSpan,\n shouldDropLangfuseSpan,\n} from '@/langfuseTraceShaping';\nimport { resolveToolOutputTracingConfigForSpan } from '@/langfuseRuntimeScope';\n\nexport { LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT, resolveLangfuseConfig };\n\nconst LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';\nconst SERVER_TOOL_RESULT_PREFIX = '{\"serverToolResult\":';\nconst SERVER_TOOL_RESULT_REPLAY_MARKER_KEY = 'librechatResponsesReplay';\n\nconst CHAT_ROLES = new Set([\n 'assistant',\n 'developer',\n 'human',\n 'system',\n 'user',\n]);\n\ntype SpanWithAttributes = ReadableSpan & {\n attributes: Record<string, unknown>;\n};\n\ntype RedactionResult = {\n value: unknown;\n changed: boolean;\n};\n\ntype RedactionContext = {\n generatedImageData: Set<string>;\n generatedImageIds: Set<string>;\n toolNamesByCallId: Map<string, string>;\n};\n\nconst TOOL_OUTPUT_FIELD_KEYS = ['content', 'artifact'];\n\ntype ResponsesReplayOutputDescriptor = {\n nestedOutputFields?: Readonly<Record<string, readonly string[]>>;\n outputFields: readonly string[];\n resolveToolNameFromCallId?: boolean;\n toolName?: string;\n};\n\nconst RESPONSES_REPLAY_OUTPUT_DESCRIPTORS: Readonly<\n Record<string, ResponsesReplayOutputDescriptor>\n> = {\n local_shell_call_output: {\n outputFields: ['output'],\n toolName: 'local_shell',\n },\n shell_call_output: { outputFields: ['output'], toolName: 'shell' },\n apply_patch_call_output: {\n outputFields: ['output'],\n toolName: 'apply_patch',\n },\n program_output: { outputFields: ['result'], toolName: 'program' },\n code_interpreter_call: {\n outputFields: ['outputs'],\n toolName: 'code_interpreter',\n },\n mcp_call: { outputFields: ['output', 'error'], toolName: 'mcp' },\n mcp_list_tools: {\n outputFields: ['tools', 'error'],\n toolName: 'mcp_list_tools',\n },\n image_generation_call: {\n outputFields: ['result'],\n toolName: 'image_generation',\n },\n file_search_call: {\n outputFields: ['results'],\n toolName: 'file_search',\n },\n web_search_call: {\n nestedOutputFields: { action: ['sources'] },\n outputFields: ['results'],\n toolName: 'web_search',\n },\n tool_search_output: {\n outputFields: ['tools'],\n toolName: 'tool_search',\n },\n function_call_output: {\n outputFields: ['output'],\n resolveToolNameFromCallId: true,\n },\n custom_tool_call_output: {\n outputFields: ['output'],\n resolveToolNameFromCallId: true,\n },\n computer_call_output: {\n outputFields: ['output'],\n toolName: 'computer_use',\n },\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isPresent(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction shouldApplyToolOutputRedaction(\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || config.redactedToolNames.size > 0;\n}\n\nfunction toolNameMatches(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n if (!isPresent(toolName)) {\n return false;\n }\n\n const normalizedToolName = normalizeToolName(toolName);\n if (config.redactedToolNameMatchMode === 'partial') {\n for (const redactedToolName of config.redactedToolNames) {\n if (normalizedToolName.includes(redactedToolName)) {\n return true;\n }\n }\n return false;\n }\n\n return config.redactedToolNames.has(normalizedToolName);\n}\n\n/** Whether a tool's outputs are excluded from tracing (global disable or\n * `redactedToolNames` match). Exported for the activity-label prompt\n * builder, whose prompt becomes Langfuse generation input. */\nexport function shouldRedactTool(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || toolNameMatches(toolName, config);\n}\n\nfunction getStringField(\n value: Record<string, unknown>,\n key: string\n): string | undefined {\n const field = value[key];\n return typeof field === 'string' ? field : undefined;\n}\n\nfunction getNestedStringField(\n value: Record<string, unknown>,\n objectKey: string,\n fieldKey: string\n): string | undefined {\n const nested = value[objectKey];\n if (!isRecord(nested)) {\n return undefined;\n }\n return getStringField(nested, fieldKey);\n}\n\nfunction getSerializedToolCallId(\n value: Record<string, unknown>\n): string | undefined {\n return (\n getStringField(value, 'tool_call_id') ??\n getStringField(value, 'toolCallId') ??\n getStringField(value, 'call_id') ??\n getNestedStringField(value, 'kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'data', 'tool_call_id') ??\n (typeof value.id === 'string' ? value.id : undefined)\n );\n}\n\nfunction getSerializedToolName(\n value: Record<string, unknown>,\n redactionContext?: RedactionContext\n): string | undefined {\n const role = getStringField(value, 'role');\n const explicitName =\n getStringField(value, 'name') ??\n getStringField(value, 'tool_name') ??\n getNestedStringField(value, 'function', 'name') ??\n getNestedStringField(value, 'kwargs', 'name') ??\n getNestedStringField(value, 'additional_kwargs', 'name') ??\n getNestedStringField(value, 'data', 'name') ??\n (role != null && role.toLowerCase() !== 'tool' ? role : undefined);\n\n if (explicitName != null) {\n return explicitName;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n return toolCallId != null\n ? redactionContext?.toolNamesByCallId.get(toolCallId)\n : undefined;\n}\n\nfunction hasToolMessageIdentity(value: Record<string, unknown>): boolean {\n const type = getStringField(value, 'type') ?? getStringField(value, '_type');\n if (type === 'tool' || type === 'tool_message') {\n return true;\n }\n\n const id = value.id;\n if (\n Array.isArray(id) &&\n id.some((part) => typeof part === 'string' && part.includes('ToolMessage'))\n ) {\n return true;\n }\n\n if (\n 'tool_call_id' in value ||\n getNestedStringField(value, 'kwargs', 'tool_call_id') != null ||\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') != null\n ) {\n return true;\n }\n\n const role = getStringField(value, 'role');\n return (\n role != null &&\n !CHAT_ROLES.has(role.toLowerCase()) &&\n ('content' in value || isRecord(value.kwargs) || isRecord(value.data))\n );\n}\n\nfunction hasAssistantMessageIdentity(value: Record<string, unknown>): boolean {\n const role = getStringField(value, 'role');\n if (role?.toLowerCase() === 'assistant') {\n return true;\n }\n const type = getStringField(value, 'type') ?? getStringField(value, '_type');\n if (type === 'ai' || type === 'assistant') {\n return true;\n }\n const id = value.id;\n return (\n Array.isArray(id) &&\n id.some(\n (part) =>\n typeof part === 'string' &&\n (part === 'AIMessage' || part === 'AIMessageChunk')\n )\n );\n}\n\nfunction redactToolContentFields(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): Record<string, unknown> {\n const next = { ...value };\n\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in next) {\n next[outputKey] = config.redactionText;\n }\n }\n\n for (const nestedKey of ['kwargs', 'data', 'additional_kwargs']) {\n const nested = next[nestedKey];\n if (!isRecord(nested)) {\n continue;\n }\n const nextNested = { ...nested };\n let changed = false;\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in nextNested) {\n nextNested[outputKey] = config.redactionText;\n changed = true;\n }\n }\n if (changed) {\n next[nestedKey] = nextNested;\n }\n }\n\n return next;\n}\n\nfunction redactResponsesReplayOutput(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig,\n redactionContext: RedactionContext\n): RedactionResult | undefined {\n const type = getStringField(value, 'type');\n if (\n type == null ||\n !Object.prototype.hasOwnProperty.call(\n RESPONSES_REPLAY_OUTPUT_DESCRIPTORS,\n type\n )\n ) {\n return undefined;\n }\n const descriptor =\n RESPONSES_REPLAY_OUTPUT_DESCRIPTORS[\n type as keyof typeof RESPONSES_REPLAY_OUTPUT_DESCRIPTORS\n ];\n const hasDirectOutput = descriptor.outputFields.some(\n (outputField) => outputField in value\n );\n const hasNestedOutput = Object.entries(\n descriptor.nestedOutputFields ?? {}\n ).some(([objectField, outputFields]) => {\n const nested = value[objectField];\n return (\n isRecord(nested) &&\n outputFields.some((outputField) => outputField in nested)\n );\n });\n if (!hasDirectOutput && !hasNestedOutput) {\n return undefined;\n }\n let toolName = descriptor.toolName;\n if (descriptor.resolveToolNameFromCallId === true) {\n toolName = getSerializedToolName(value, redactionContext);\n } else if (type === 'mcp_call') {\n toolName = getStringField(value, 'name') ?? descriptor.toolName;\n }\n if (!shouldRedactTool(toolName, config)) {\n return undefined;\n }\n const redacted = { ...value };\n for (const outputField of descriptor.outputFields) {\n if (outputField in redacted) {\n redacted[outputField] = config.redactionText;\n }\n }\n for (const [objectField, outputFields] of Object.entries(\n descriptor.nestedOutputFields ?? {}\n )) {\n const nested = redacted[objectField];\n if (!isRecord(nested)) {\n continue;\n }\n const redactedNested = { ...nested };\n for (const outputField of outputFields) {\n if (outputField in redactedNested) {\n redactedNested[outputField] = config.redactionText;\n }\n }\n redacted[objectField] = redactedNested;\n }\n return {\n value: redacted,\n changed: true,\n };\n}\n\nfunction redactStandardServerToolResult(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig,\n redactionContext: RedactionContext\n): RedactionResult | undefined {\n if (\n getStringField(value, 'type') !== 'server_tool_call_result' ||\n !('output' in value)\n ) {\n return undefined;\n }\n const toolName =\n getNestedStringField(value, 'extras', 'name') ??\n getSerializedToolName(value, redactionContext);\n if (!shouldRedactTool(toolName, config)) {\n return undefined;\n }\n return {\n value: { ...value, output: config.redactionText },\n changed: true,\n };\n}\n\nfunction parseSerializedServerToolResultMarker(\n text: string\n): { toolName?: string } | undefined {\n if (!text.startsWith(SERVER_TOOL_RESULT_PREFIX)) {\n return undefined;\n }\n try {\n const parsed = JSON.parse(text) as unknown;\n if (!isRecord(parsed) || !isRecord(parsed.serverToolResult)) {\n return undefined;\n }\n const result = parsed.serverToolResult;\n if (result[SERVER_TOOL_RESULT_REPLAY_MARKER_KEY] !== true) {\n return undefined;\n }\n const status = getStringField(result, 'status');\n if (\n (status !== 'error' && status !== 'success') ||\n !Object.prototype.hasOwnProperty.call(result, 'output')\n ) {\n return undefined;\n }\n const toolName = getStringField(result, 'toolName');\n return toolName != null ? { toolName } : {};\n } catch {\n const match =\n /^\\{\"serverToolResult\":\\{\"librechatResponsesReplay\":true,(?:\"toolName\":(\"(?:\\\\.|[^\"\\\\])*\"),)?\"status\":\"(?:error|success)\",\"output\":/.exec(\n text.slice(0, 512)\n );\n if (match == null) {\n return undefined;\n }\n const encodedToolName = match.at(1);\n if (encodedToolName == null) {\n return {};\n }\n try {\n const toolName = JSON.parse(encodedToolName) as unknown;\n return typeof toolName === 'string' ? { toolName } : {};\n } catch {\n return {};\n }\n }\n}\n\nfunction redactMarkedServerToolResult(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig,\n allowSerializedMarker: boolean\n): RedactionResult | undefined {\n const type = getStringField(value, 'type');\n if (type !== 'text' && type !== 'image') {\n return undefined;\n }\n const text = type === 'text' ? getStringField(value, 'text') : undefined;\n const extras = value.extras;\n const marker = isRecord(extras)\n ? extras.librechatServerToolResult\n : undefined;\n const serializedMarker =\n allowSerializedMarker && text != null\n ? parseSerializedServerToolResultMarker(text)\n : undefined;\n if (!isRecord(marker) && serializedMarker == null) {\n return undefined;\n }\n const toolName = isRecord(marker)\n ? getStringField(marker, 'toolName')\n : serializedMarker?.toolName;\n if (!shouldRedactTool(toolName, config)) {\n return undefined;\n }\n if (type === 'image') {\n const redacted = { ...value };\n let changed = false;\n for (const outputField of ['data', 'url', 'fileId']) {\n if (outputField in redacted) {\n redacted[outputField] = config.redactionText;\n changed = true;\n }\n }\n return changed ? { value: redacted, changed: true } : undefined;\n }\n return {\n value: { ...value, text: config.redactionText },\n changed: true,\n };\n}\n\nfunction redactGeneratedImageBlock(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig,\n redactionContext: RedactionContext,\n isAssistantContent: boolean\n): RedactionResult | undefined {\n if (\n getStringField(value, 'type') !== 'image' ||\n !shouldRedactTool('image_generation', config)\n ) {\n return undefined;\n }\n const extras = value.extras;\n const explicitMarker = isRecord(extras)\n ? extras.librechatServerToolResult\n : undefined;\n const explicitToolName = isRecord(explicitMarker)\n ? getStringField(explicitMarker, 'toolName')\n : undefined;\n if (\n explicitToolName != null &&\n normalizeToolName(explicitToolName) !== 'image_generation'\n ) {\n return undefined;\n }\n const id = getStringField(value, 'id');\n const data = getStringField(value, 'data');\n const metadata = value.metadata;\n const normalizedStatus = isRecord(metadata)\n ? getStringField(metadata, 'status')\n : undefined;\n const hasNormalizedGeneratedImageIdentity =\n isAssistantContent &&\n id?.startsWith('ig_') === true &&\n isPresent(normalizedStatus);\n const isGeneratedImage =\n explicitToolName != null ||\n hasNormalizedGeneratedImageIdentity ||\n (id != null\n ? redactionContext.generatedImageIds.has(id)\n : data != null && redactionContext.generatedImageData.has(data));\n if (!isGeneratedImage) {\n return undefined;\n }\n const redacted = { ...value };\n let changed = false;\n for (const outputField of ['data', 'url', 'fileId']) {\n if (outputField in redacted) {\n redacted[outputField] = config.redactionText;\n changed = true;\n }\n }\n return changed ? { value: redacted, changed: true } : undefined;\n}\n\nfunction collectRedactionContext(\n value: unknown,\n redactionContext: RedactionContext\n): void {\n if (Array.isArray(value)) {\n for (const item of value) {\n collectRedactionContext(item, redactionContext);\n }\n return;\n }\n\n if (!isRecord(value)) {\n return;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n const toolName = getSerializedToolName(value);\n if (toolCallId != null && toolName != null) {\n redactionContext.toolNamesByCallId.set(toolCallId, toolName);\n }\n\n if (getStringField(value, 'type') === 'image_generation_call') {\n const id = getStringField(value, 'id');\n const result = getStringField(value, 'result');\n if (id != null) {\n redactionContext.generatedImageIds.add(id);\n }\n if (result != null) {\n redactionContext.generatedImageData.add(result);\n }\n }\n\n for (const child of Object.values(value)) {\n collectRedactionContext(child, redactionContext);\n }\n}\n\nfunction redactValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig,\n redactionContext: RedactionContext,\n allowSerializedServerToolResult = false\n): RedactionResult {\n if (Array.isArray(value)) {\n let changed = false;\n const next: unknown[] = [];\n for (const item of value) {\n const result = redactValue(\n item,\n config,\n redactionContext,\n allowSerializedServerToolResult\n );\n if (result.changed) {\n changed = true;\n }\n next.push(result.value);\n }\n return changed ? { value: next, changed } : { value, changed };\n }\n\n if (!isRecord(value)) {\n return { value, changed: false };\n }\n\n const allowSerializedMarker =\n allowSerializedServerToolResult || hasAssistantMessageIdentity(value);\n\n const replayOutput = redactResponsesReplayOutput(\n value,\n config,\n redactionContext\n );\n if (replayOutput != null) {\n return replayOutput;\n }\n const standardServerToolResult = redactStandardServerToolResult(\n value,\n config,\n redactionContext\n );\n if (standardServerToolResult != null) {\n return standardServerToolResult;\n }\n const markedServerToolResult = redactMarkedServerToolResult(\n value,\n config,\n allowSerializedMarker\n );\n if (markedServerToolResult != null) {\n return markedServerToolResult;\n }\n const generatedImageBlock = redactGeneratedImageBlock(\n value,\n config,\n redactionContext,\n allowSerializedMarker\n );\n if (generatedImageBlock != null) {\n return generatedImageBlock;\n }\n\n const toolName = getSerializedToolName(value, redactionContext);\n if (hasToolMessageIdentity(value) && shouldRedactTool(toolName, config)) {\n return {\n value: redactToolContentFields(value, config),\n changed: true,\n };\n }\n\n let changed = false;\n const next: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n const result = redactValue(\n child,\n config,\n redactionContext,\n allowSerializedMarker\n );\n if (result.changed) {\n changed = true;\n }\n next[key] = result.value;\n }\n\n return changed ? { value: next, changed } : { value, changed };\n}\n\nfunction redactSerializedValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig\n): RedactionResult {\n const redactionContext: RedactionContext = {\n generatedImageData: new Set(),\n generatedImageIds: new Set(),\n toolNamesByCallId: new Map(),\n };\n if (typeof value !== 'string') {\n collectRedactionContext(value, redactionContext);\n return redactValue(value, config, redactionContext);\n }\n\n const trimmed = value.trim();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return { value, changed: false };\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n collectRedactionContext(parsed, redactionContext);\n const result = redactValue(parsed, config, redactionContext);\n return result.changed\n ? { value: JSON.stringify(result.value), changed: true }\n : { value, changed: false };\n } catch {\n // OpenTelemetry truncates string attributes before span processors run.\n // Langfuse serializes structured message values to JSON first, so a\n // truncated attribute can still contain tool output even though it is no\n // longer parseable. Fail closed for JSON-shaped attributes whenever tool\n // output redaction is active instead of exporting a partial secret.\n return { value: config.redactionText, changed: true };\n }\n}\n\nfunction redactAttribute(\n attributes: Record<string, unknown>,\n key: string,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (!(key in attributes)) {\n return;\n }\n\n const result = redactSerializedValue(attributes[key], config);\n if (result.changed) {\n attributes[key] = result.value;\n }\n}\n\nfunction isToolObservation(attributes: Record<string, unknown>): boolean {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n return typeof type === 'string' && type.toLowerCase() === 'tool';\n}\n\nfunction classifyLangGraphToolNodeSpan(\n attributes: Record<string, unknown>\n): void {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n if (typeof type !== 'string' || type.toLowerCase() !== 'span') {\n return;\n }\n\n const langGraphNode =\n attributes[\n `${LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.langgraph_node`\n ];\n if (\n typeof langGraphNode === 'string' &&\n langGraphNode.startsWith(LANGGRAPH_TOOL_NODE_PREFIX)\n ) {\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE] = 'tool';\n }\n}\n\nexport function classifyLangfuseToolNodeSpan(span: ReadableSpan): void {\n classifyLangGraphToolNodeSpan((span as SpanWithAttributes).attributes);\n}\n\nfunction redactToolObservationOutput(\n span: ReadableSpan,\n attributes: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (\n !(\n isToolObservation(attributes) &&\n shouldRedactTool(span.name, config) &&\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT in attributes\n )\n ) {\n return;\n }\n\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT] =\n config.redactionText;\n}\n\nexport function redactLangfuseSpanToolOutputs(\n span: ReadableSpan,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n const attributes = (span as SpanWithAttributes).attributes;\n classifyLangfuseToolNodeSpan(span);\n\n if (!shouldApplyToolOutputRedaction(config)) {\n return;\n }\n\n redactToolObservationOutput(span, attributes, config);\n\n for (const key of [\n LangfuseOtelSpanAttributes.OBSERVATION_INPUT,\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT,\n LangfuseOtelSpanAttributes.TRACE_INPUT,\n LangfuseOtelSpanAttributes.TRACE_OUTPUT,\n ]) {\n redactAttribute(attributes, key, config);\n }\n}\n\nexport function prepareLangfuseSpanForExport(\n span: ReadableSpan,\n config?: ResolvedLangfuseToolOutputTracingConfig\n): void {\n classifyLangfuseToolNodeSpan(span);\n if (config != null) {\n redactLangfuseSpanToolOutputs(span, config);\n }\n shapeLangfuseSpan(span);\n}\n\nclass ToolOutputRedactingLangfuseSpanProcessor implements SpanProcessor {\n private readonly processor: LangfuseSpanProcessor;\n private readonly fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig;\n private readonly spanConfigs = new WeakMap<\n object,\n ResolvedLangfuseToolOutputTracingConfig\n >();\n\n constructor(\n params?: LangfuseSpanProcessorParams,\n fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig\n ) {\n this.processor = new LangfuseSpanProcessor(params);\n this.fallbackConfig = fallbackConfig;\n }\n\n onStart(span: Span, parentContext: Context): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n const config =\n resolveToolOutputTracingConfigForSpan(parentContext) ??\n this.fallbackConfig;\n if (config != null) {\n this.spanConfigs.set(span, config);\n }\n this.processor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n const config = this.spanConfigs.get(span) ?? this.fallbackConfig;\n prepareLangfuseSpanForExport(span, config);\n this.processor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.processor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.processor.shutdown();\n }\n}\n\nexport function createLangfuseSpanProcessor(\n params?: LangfuseSpanProcessorParams,\n runLangfuse?: t.LangfuseConfig,\n agentLangfuse?: t.LangfuseConfig\n): SpanProcessor {\n const fallbackConfig = hasToolOutputTracingConfig(runLangfuse, agentLangfuse)\n ? resolveToolOutputTracingConfig(runLangfuse, agentLangfuse)\n : undefined;\n return new ToolOutputRedactingLangfuseSpanProcessor(params, fallbackConfig);\n}\n\nfunction hasLangfuseEnvKeys(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nfunction hasLangfuseConfigKeys(langfuse?: t.LangfuseConfig): boolean {\n if (langfuse == null) {\n return false;\n }\n return isPresent(langfuse.secretKey) && isPresent(langfuse.publicKey);\n}\n\nexport function shouldTraceToolNodeForLangfuse({\n runLangfuse,\n agentLangfuse,\n}: {\n runLangfuse?: t.LangfuseConfig;\n agentLangfuse?: t.LangfuseConfig;\n}): boolean {\n const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);\n if (langfuse?.enabled === false) {\n return false;\n }\n\n const explicit = langfuse?.toolNodeTracing?.enabled;\n if (explicit !== true) {\n return false;\n }\n\n return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();\n}\n"],"mappings":"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"}
|
package/dist/cjs/llm/init.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require("../common/enum.cjs");
|
|
2
2
|
require("../common/index.cjs");
|
|
3
|
+
const require_llm = require("../utils/llm.cjs");
|
|
3
4
|
const require_index$1 = require("./openai/index.cjs");
|
|
4
5
|
const require_providers = require("./providers.cjs");
|
|
5
|
-
const require_llm = require("../utils/llm.cjs");
|
|
6
6
|
require("../utils/index.cjs");
|
|
7
7
|
let _langchain_google_vertexai = require("@langchain/google-vertexai");
|
|
8
8
|
//#region src/llm/init.ts
|
package/dist/cjs/llm/invoke.cjs
CHANGED
|
@@ -8,12 +8,12 @@ const require_alternation = require("../messages/alternation.cjs");
|
|
|
8
8
|
const require_handoffCue = require("../messages/handoffCue.cjs");
|
|
9
9
|
require("../messages/index.cjs");
|
|
10
10
|
const require_toolOutputReferences = require("../tools/toolOutputReferences.cjs");
|
|
11
|
+
const require_llm = require("../utils/llm.cjs");
|
|
12
|
+
const require_stream = require("../stream.cjs");
|
|
11
13
|
const require_truncation = require("./truncation.cjs");
|
|
12
14
|
const require_providers = require("./providers.cjs");
|
|
13
15
|
const require_errors = require("../utils/errors.cjs");
|
|
14
16
|
const require_preempt = require("./preempt.cjs");
|
|
15
|
-
const require_llm = require("../utils/llm.cjs");
|
|
16
|
-
const require_stream = require("../stream.cjs");
|
|
17
17
|
const require_init = require("./init.cjs");
|
|
18
18
|
let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
19
19
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
@@ -57,8 +57,9 @@ function usesNativeOpenAIResponses(model, provider, callOptions) {
|
|
|
57
57
|
* guard so structured tool output cannot grow after the payload was measured.
|
|
58
58
|
*/
|
|
59
59
|
function projectMessagesForProvider({ model, messages, provider, maxToolResultChars, callOptions }) {
|
|
60
|
-
const
|
|
61
|
-
|
|
60
|
+
const nativeOpenAIResponses = usesNativeOpenAIResponses(model, provider, callOptions);
|
|
61
|
+
const providerInputMessages = require_core.projectToolStreamContentForProvider(messages, nativeOpenAIResponses ? "native" : "fallback", maxToolResultChars);
|
|
62
|
+
if (nativeOpenAIResponses) return require_core.projectOpenAIResponsesToolMessageContent(require_cache.stripAnthropicCacheControl(require_cache.stripBedrockCacheControl(providerInputMessages)), maxToolResultChars);
|
|
62
63
|
if (provider === "openrouter") return require_core.projectComputerCallOutputsToText(require_core.projectOpenRouterToolMessageContent(require_cache.stripBedrockCacheControl(providerInputMessages), maxToolResultChars));
|
|
63
64
|
if (require_llm.isOpenAILike(provider)) return require_core.projectComputerCallOutputsToText(require_core.projectOpenAIChatToolMessageContent(require_cache.stripAnthropicCacheControl(require_cache.stripBedrockCacheControl(providerInputMessages)), maxToolResultChars));
|
|
64
65
|
if (provider === "anthropic") return require_core.projectComputerCallOutputsToText(require_core.projectSingleTextToolOutputsToText(require_cache.stripBedrockCacheControl(providerInputMessages), maxToolResultChars));
|
|
@@ -173,15 +174,19 @@ function synthesizeSealedUsage(context, chunk, prompt, metadata) {
|
|
|
173
174
|
const outputTokens = countSealedTokens(context, metadata, [chunk]);
|
|
174
175
|
if (outputTokens == null) return;
|
|
175
176
|
const inputTokens = (countSealedTokens(context, metadata, prompt) ?? 0) + sealedInstructionOverhead(context, metadata);
|
|
176
|
-
|
|
177
|
+
const usageMetadata = {
|
|
177
178
|
input_tokens: inputTokens,
|
|
178
179
|
output_tokens: outputTokens,
|
|
179
180
|
total_tokens: inputTokens + outputTokens
|
|
180
181
|
};
|
|
181
|
-
chunk.
|
|
182
|
+
chunk.usage_metadata = usageMetadata;
|
|
183
|
+
chunk.lc_kwargs.usage_metadata = usageMetadata;
|
|
184
|
+
const responseMetadata = {
|
|
182
185
|
...chunk.response_metadata,
|
|
183
186
|
estimated_usage: true
|
|
184
187
|
};
|
|
188
|
+
chunk.response_metadata = responseMetadata;
|
|
189
|
+
chunk.lc_kwargs.response_metadata = responseMetadata;
|
|
185
190
|
}
|
|
186
191
|
function getMessageText(chunk) {
|
|
187
192
|
if (typeof chunk.content === "string") return chunk.content;
|
|
@@ -449,10 +454,12 @@ async function attemptInvoke({ model, messages, provider, context, onChunk }, co
|
|
|
449
454
|
}
|
|
450
455
|
if (require_providers.manualToolStreamProviders.has(provider)) finalChunk = require_core.modifyDeltaProperties(provider, finalChunk);
|
|
451
456
|
if (preempted && finalChunk != null) {
|
|
452
|
-
|
|
457
|
+
const responseMetadata = {
|
|
453
458
|
...finalChunk.response_metadata,
|
|
454
459
|
preempted: true
|
|
455
460
|
};
|
|
461
|
+
finalChunk.response_metadata = responseMetadata;
|
|
462
|
+
finalChunk.lc_kwargs.response_metadata = responseMetadata;
|
|
456
463
|
await endSealedModelRun(context, finalChunk, messagesForProvider, sealedRunId, config, model);
|
|
457
464
|
}
|
|
458
465
|
if ((finalChunk?.tool_calls?.length ?? 0) > 0) finalChunk.tool_calls = finalChunk.tool_calls?.filter((tool_call) => !!tool_call.name);
|