@librechat/agents 3.2.43 → 3.2.45
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/graphs/Graph.cjs +17 -8
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +20 -21
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +65 -13
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseConfig.cjs +106 -0
- package/dist/cjs/langfuseConfig.cjs.map +1 -0
- package/dist/cjs/langfuseRuntimeContext.cjs +48 -0
- package/dist/cjs/langfuseRuntimeContext.cjs.map +1 -0
- package/dist/cjs/langfuseRuntimeScope.cjs +57 -0
- package/dist/cjs/langfuseRuntimeScope.cjs.map +1 -0
- package/dist/cjs/langfuseToolOutputTracing.cjs +14 -119
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/main.cjs +1 -0
- package/dist/cjs/messages/reducer.cjs +15 -2
- package/dist/cjs/messages/reducer.cjs.map +1 -1
- package/dist/cjs/run.cjs +25 -12
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +5 -2
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/utils/misc.cjs +17 -0
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +16 -7
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +22 -22
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +65 -14
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseConfig.mjs +102 -0
- package/dist/esm/langfuseConfig.mjs.map +1 -0
- package/dist/esm/langfuseRuntimeContext.mjs +44 -0
- package/dist/esm/langfuseRuntimeContext.mjs.map +1 -0
- package/dist/esm/langfuseRuntimeScope.mjs +53 -0
- package/dist/esm/langfuseRuntimeScope.mjs.map +1 -0
- package/dist/esm/langfuseToolOutputTracing.mjs +13 -115
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/messages/reducer.mjs +15 -2
- package/dist/esm/messages/reducer.mjs.map +1 -1
- package/dist/esm/run.mjs +21 -8
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +5 -2
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/utils/misc.mjs +17 -1
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/types/instrumentation.d.ts +0 -1
- package/dist/types/langfuse.d.ts +5 -1
- package/dist/types/langfuseConfig.d.ts +7 -0
- package/dist/types/langfuseRuntimeContext.d.ts +26 -0
- package/dist/types/langfuseRuntimeScope.d.ts +17 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +4 -11
- package/dist/types/types/graph.d.ts +6 -0
- package/dist/types/utils/misc.d.ts +1 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +20 -13
- package/src/instrumentation.ts +33 -29
- package/src/langfuse.ts +114 -7
- package/src/langfuseConfig.ts +214 -0
- package/src/langfuseRuntimeContext.ts +93 -0
- package/src/langfuseRuntimeScope.ts +135 -0
- package/src/langfuseToolOutputTracing.ts +25 -263
- package/src/messages/reducer.spec.ts +32 -0
- package/src/messages/reducer.ts +23 -7
- package/src/run.ts +44 -34
- package/src/specs/deterministic-trace-id.test.ts +4 -11
- package/src/specs/langfuse-callbacks.test.ts +278 -22
- package/src/specs/langfuse-config.test.ts +10 -3
- package/src/specs/langfuse-instrumentation.test.ts +32 -12
- package/src/specs/langfuse-routing.integration.test.ts +543 -0
- package/src/specs/langfuse-runtime-context.test.ts +92 -0
- package/src/specs/langfuse-tool-output-tracing.test.ts +353 -5
- package/src/tools/ToolNode.ts +10 -5
- package/src/tools/__tests__/ToolNode.langfuse.test.ts +21 -11
- package/src/types/graph.ts +9 -0
- package/src/utils/misc.ts +18 -0
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
const require_langfuseConfig = require("./langfuseConfig.cjs");
|
|
2
|
+
const require_langfuseRuntimeScope = require("./langfuseRuntimeScope.cjs");
|
|
1
3
|
let _langfuse_tracing = require("@langfuse/tracing");
|
|
2
|
-
let node_async_hooks = require("node:async_hooks");
|
|
3
4
|
let _langfuse_otel = require("@langfuse/otel");
|
|
4
|
-
|
|
5
|
-
const langfuseToolOutputTracingConfigKey = (0, _opentelemetry_api.createContextKey)("librechat.langfuse.tool-output-tracing");
|
|
6
|
-
const langfuseConfigKey = (0, _opentelemetry_api.createContextKey)("librechat.langfuse.config");
|
|
7
|
-
const toolOutputTracingStorage = new node_async_hooks.AsyncLocalStorage();
|
|
8
|
-
const langfuseConfigStorage = new node_async_hooks.AsyncLocalStorage();
|
|
5
|
+
//#region src/langfuseToolOutputTracing.ts
|
|
9
6
|
const LANGGRAPH_TOOL_NODE_PREFIX = "tools=";
|
|
10
7
|
const CHAT_ROLES = new Set([
|
|
11
8
|
"assistant",
|
|
@@ -21,67 +18,12 @@ function isRecord(value) {
|
|
|
21
18
|
function isPresent(value) {
|
|
22
19
|
return typeof value === "string" && value.trim() !== "";
|
|
23
20
|
}
|
|
24
|
-
function parseBoolean(value) {
|
|
25
|
-
if (value == null) return;
|
|
26
|
-
const normalized = value.trim().toLowerCase();
|
|
27
|
-
if ([
|
|
28
|
-
"1",
|
|
29
|
-
"true",
|
|
30
|
-
"yes",
|
|
31
|
-
"on"
|
|
32
|
-
].includes(normalized)) return true;
|
|
33
|
-
if ([
|
|
34
|
-
"0",
|
|
35
|
-
"false",
|
|
36
|
-
"no",
|
|
37
|
-
"off"
|
|
38
|
-
].includes(normalized)) return false;
|
|
39
|
-
}
|
|
40
|
-
function normalizeToolName(name) {
|
|
41
|
-
return name.trim().toLowerCase();
|
|
42
|
-
}
|
|
43
|
-
function normalizeToolNames(names) {
|
|
44
|
-
const normalized = /* @__PURE__ */ new Set();
|
|
45
|
-
for (const name of names ?? []) if (isPresent(name)) normalized.add(normalizeToolName(name));
|
|
46
|
-
return normalized;
|
|
47
|
-
}
|
|
48
|
-
function parseToolNames(value) {
|
|
49
|
-
if (!isPresent(value)) return;
|
|
50
|
-
return value.split(",").map((name) => name.trim()).filter((name) => name !== "");
|
|
51
|
-
}
|
|
52
|
-
function getEnvToolOutputTracingEnabled() {
|
|
53
|
-
const traceToolOutputs = parseBoolean(process.env.LANGFUSE_TRACE_TOOL_OUTPUTS);
|
|
54
|
-
if (traceToolOutputs != null) return traceToolOutputs;
|
|
55
|
-
const redactToolOutputs = parseBoolean(process.env.LANGFUSE_REDACT_TOOL_OUTPUTS);
|
|
56
|
-
if (redactToolOutputs != null) return !redactToolOutputs;
|
|
57
|
-
return parseBoolean(process.env.LANGFUSE_TOOL_OUTPUT_TRACING_ENABLED);
|
|
58
|
-
}
|
|
59
|
-
function getEnvRedactedToolNames() {
|
|
60
|
-
return parseToolNames(process.env.LANGFUSE_REDACT_TOOL_OUTPUT_NAMES) ?? parseToolNames(process.env.LANGFUSE_REDACT_TOOL_NAMES);
|
|
61
|
-
}
|
|
62
|
-
function getEnvRedactionText() {
|
|
63
|
-
return isPresent(process.env.LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT) ? process.env.LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT : void 0;
|
|
64
|
-
}
|
|
65
|
-
function getEnvToolNameMatchMode() {
|
|
66
|
-
const mode = (process.env.LANGFUSE_REDACT_TOOL_OUTPUT_NAME_MATCH_MODE ?? process.env.LANGFUSE_REDACT_TOOL_NAME_MATCH_MODE)?.trim().toLowerCase();
|
|
67
|
-
if (mode === "exact" || mode === "partial") return mode;
|
|
68
|
-
}
|
|
69
|
-
function resolveToolOutputTracingConfig(runLangfuse, agentLangfuse) {
|
|
70
|
-
const runConfig = runLangfuse?.toolOutputTracing;
|
|
71
|
-
const agentConfig = agentLangfuse?.toolOutputTracing;
|
|
72
|
-
return {
|
|
73
|
-
enabled: agentConfig?.enabled ?? runConfig?.enabled ?? getEnvToolOutputTracingEnabled() ?? true,
|
|
74
|
-
redactedToolNames: normalizeToolNames(agentConfig?.redactedToolNames ?? runConfig?.redactedToolNames ?? getEnvRedactedToolNames()),
|
|
75
|
-
redactedToolNameMatchMode: agentConfig?.redactedToolNameMatchMode ?? runConfig?.redactedToolNameMatchMode ?? getEnvToolNameMatchMode() ?? "exact",
|
|
76
|
-
redactionText: agentConfig?.redactionText ?? runConfig?.redactionText ?? getEnvRedactionText() ?? "[tool output redacted]"
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
21
|
function shouldApplyToolOutputRedaction(config) {
|
|
80
22
|
return config.enabled === false || config.redactedToolNames.size > 0;
|
|
81
23
|
}
|
|
82
24
|
function toolNameMatches(toolName, config) {
|
|
83
25
|
if (!isPresent(toolName)) return false;
|
|
84
|
-
const normalizedToolName = normalizeToolName(toolName);
|
|
26
|
+
const normalizedToolName = require_langfuseConfig.normalizeToolName(toolName);
|
|
85
27
|
if (config.redactedToolNameMatchMode === "partial") {
|
|
86
28
|
for (const redactedToolName of config.redactedToolNames) if (normalizedToolName.includes(redactedToolName)) return true;
|
|
87
29
|
return false;
|
|
@@ -235,13 +177,16 @@ function classifyLangGraphToolNodeSpan(attributes) {
|
|
|
235
177
|
const langGraphNode = attributes[`${_langfuse_tracing.LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.langgraph_node`];
|
|
236
178
|
if (typeof langGraphNode === "string" && langGraphNode.startsWith(LANGGRAPH_TOOL_NODE_PREFIX)) attributes[_langfuse_tracing.LangfuseOtelSpanAttributes.OBSERVATION_TYPE] = "tool";
|
|
237
179
|
}
|
|
180
|
+
function classifyLangfuseToolNodeSpan(span) {
|
|
181
|
+
classifyLangGraphToolNodeSpan(span.attributes);
|
|
182
|
+
}
|
|
238
183
|
function redactToolObservationOutput(span, attributes, config) {
|
|
239
184
|
if (!(isToolObservation(attributes) && shouldRedactTool(span.name, config) && _langfuse_tracing.LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT in attributes)) return;
|
|
240
185
|
attributes[_langfuse_tracing.LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT] = config.redactionText;
|
|
241
186
|
}
|
|
242
187
|
function redactLangfuseSpanToolOutputs(span, config) {
|
|
243
188
|
const attributes = span.attributes;
|
|
244
|
-
|
|
189
|
+
classifyLangfuseToolNodeSpan(span);
|
|
245
190
|
if (!shouldApplyToolOutputRedaction(config)) return;
|
|
246
191
|
redactToolObservationOutput(span, attributes, config);
|
|
247
192
|
for (const key of [
|
|
@@ -251,18 +196,6 @@ function redactLangfuseSpanToolOutputs(span, config) {
|
|
|
251
196
|
_langfuse_tracing.LangfuseOtelSpanAttributes.TRACE_OUTPUT
|
|
252
197
|
]) redactAttribute(attributes, key, config);
|
|
253
198
|
}
|
|
254
|
-
function getContextToolOutputTracingConfig(activeContext) {
|
|
255
|
-
const asyncConfig = toolOutputTracingStorage.getStore();
|
|
256
|
-
if (asyncConfig != null) return asyncConfig;
|
|
257
|
-
const value = activeContext.getValue(langfuseToolOutputTracingConfigKey);
|
|
258
|
-
return isRecord(value) ? value : void 0;
|
|
259
|
-
}
|
|
260
|
-
function getContextLangfuseConfig(activeContext) {
|
|
261
|
-
const asyncConfig = langfuseConfigStorage.getStore();
|
|
262
|
-
if (asyncConfig != null) return asyncConfig;
|
|
263
|
-
const value = activeContext.getValue(langfuseConfigKey);
|
|
264
|
-
return isRecord(value) ? value : void 0;
|
|
265
|
-
}
|
|
266
199
|
var ToolOutputRedactingLangfuseSpanProcessor = class {
|
|
267
200
|
processor;
|
|
268
201
|
fallbackConfig;
|
|
@@ -272,12 +205,14 @@ var ToolOutputRedactingLangfuseSpanProcessor = class {
|
|
|
272
205
|
this.fallbackConfig = fallbackConfig;
|
|
273
206
|
}
|
|
274
207
|
onStart(span, parentContext) {
|
|
275
|
-
const config =
|
|
208
|
+
const config = require_langfuseRuntimeScope.resolveToolOutputTracingConfigForSpan(parentContext) ?? this.fallbackConfig;
|
|
276
209
|
if (config != null) this.spanConfigs.set(span, config);
|
|
277
210
|
this.processor.onStart(span, parentContext);
|
|
278
211
|
}
|
|
279
212
|
onEnd(span) {
|
|
280
|
-
|
|
213
|
+
classifyLangfuseToolNodeSpan(span);
|
|
214
|
+
const config = this.spanConfigs.get(span) ?? this.fallbackConfig;
|
|
215
|
+
if (config != null) redactLangfuseSpanToolOutputs(span, config);
|
|
281
216
|
this.processor.onEnd(span);
|
|
282
217
|
}
|
|
283
218
|
forceFlush() {
|
|
@@ -288,19 +223,7 @@ var ToolOutputRedactingLangfuseSpanProcessor = class {
|
|
|
288
223
|
}
|
|
289
224
|
};
|
|
290
225
|
function createLangfuseSpanProcessor(params, runLangfuse, agentLangfuse) {
|
|
291
|
-
return new ToolOutputRedactingLangfuseSpanProcessor(params, runLangfuse
|
|
292
|
-
}
|
|
293
|
-
function withLangfuseToolOutputTracingConfig(runLangfuse, action, agentLangfuse) {
|
|
294
|
-
const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);
|
|
295
|
-
const hasNoToolOutputConfig = runLangfuse?.toolOutputTracing == null && agentLangfuse?.toolOutputTracing == null;
|
|
296
|
-
if (langfuse == null && hasNoToolOutputConfig) return action();
|
|
297
|
-
const config = hasNoToolOutputConfig ? void 0 : resolveToolOutputTracingConfig(runLangfuse, agentLangfuse);
|
|
298
|
-
let activeContext = _opentelemetry_api.context.active();
|
|
299
|
-
if (langfuse != null) activeContext = activeContext.setValue(langfuseConfigKey, langfuse);
|
|
300
|
-
if (config != null) activeContext = activeContext.setValue(langfuseToolOutputTracingConfigKey, config);
|
|
301
|
-
const runWithContext = () => _opentelemetry_api.context.with(activeContext, action);
|
|
302
|
-
const runWithToolOutputConfig = () => config != null ? toolOutputTracingStorage.run(config, runWithContext) : runWithContext();
|
|
303
|
-
return langfuse != null ? langfuseConfigStorage.run(langfuse, runWithToolOutputConfig) : runWithToolOutputConfig();
|
|
226
|
+
return new ToolOutputRedactingLangfuseSpanProcessor(params, require_langfuseConfig.hasToolOutputTracingConfig(runLangfuse, agentLangfuse) ? require_langfuseConfig.resolveToolOutputTracingConfig(runLangfuse, agentLangfuse) : void 0);
|
|
304
227
|
}
|
|
305
228
|
function hasLangfuseEnvKeys() {
|
|
306
229
|
return isPresent(process.env.LANGFUSE_SECRET_KEY) && isPresent(process.env.LANGFUSE_PUBLIC_KEY);
|
|
@@ -310,42 +233,14 @@ function hasLangfuseConfigKeys(langfuse) {
|
|
|
310
233
|
return isPresent(langfuse.secretKey) && isPresent(langfuse.publicKey);
|
|
311
234
|
}
|
|
312
235
|
function shouldTraceToolNodeForLangfuse({ runLangfuse, agentLangfuse }) {
|
|
313
|
-
const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);
|
|
236
|
+
const langfuse = require_langfuseConfig.resolveLangfuseConfig(runLangfuse, agentLangfuse);
|
|
314
237
|
if (langfuse?.enabled === false) return false;
|
|
315
238
|
const explicit = langfuse?.toolNodeTracing?.enabled;
|
|
316
239
|
if (explicit != null) return explicit && (hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys());
|
|
317
240
|
return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();
|
|
318
241
|
}
|
|
319
|
-
function resolveLangfuseConfig(runLangfuse, agentLangfuse) {
|
|
320
|
-
if (runLangfuse == null) return agentLangfuse;
|
|
321
|
-
if (agentLangfuse == null) return runLangfuse;
|
|
322
|
-
const toolNodeTracing = runLangfuse.toolNodeTracing != null || agentLangfuse.toolNodeTracing != null ? {
|
|
323
|
-
...runLangfuse.toolNodeTracing,
|
|
324
|
-
...agentLangfuse.toolNodeTracing
|
|
325
|
-
} : void 0;
|
|
326
|
-
const toolOutputTracing = runLangfuse.toolOutputTracing != null || agentLangfuse.toolOutputTracing != null ? {
|
|
327
|
-
...runLangfuse.toolOutputTracing,
|
|
328
|
-
...agentLangfuse.toolOutputTracing
|
|
329
|
-
} : void 0;
|
|
330
|
-
const metadata = runLangfuse.metadata != null || agentLangfuse.metadata != null ? {
|
|
331
|
-
...runLangfuse.metadata,
|
|
332
|
-
...agentLangfuse.metadata
|
|
333
|
-
} : void 0;
|
|
334
|
-
const tags = runLangfuse.tags != null || agentLangfuse.tags != null ? [...new Set([...runLangfuse.tags ?? [], ...agentLangfuse.tags ?? []])] : void 0;
|
|
335
|
-
return {
|
|
336
|
-
...runLangfuse,
|
|
337
|
-
...agentLangfuse,
|
|
338
|
-
...metadata != null ? { metadata } : {},
|
|
339
|
-
...tags != null ? { tags } : {},
|
|
340
|
-
...toolNodeTracing != null ? { toolNodeTracing } : {},
|
|
341
|
-
...toolOutputTracing != null ? { toolOutputTracing } : {}
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
242
|
//#endregion
|
|
345
243
|
exports.createLangfuseSpanProcessor = createLangfuseSpanProcessor;
|
|
346
|
-
exports.getContextLangfuseConfig = getContextLangfuseConfig;
|
|
347
|
-
exports.resolveLangfuseConfig = resolveLangfuseConfig;
|
|
348
244
|
exports.shouldTraceToolNodeForLangfuse = shouldTraceToolNodeForLangfuse;
|
|
349
|
-
exports.withLangfuseToolOutputTracingConfig = withLangfuseToolOutputTracingConfig;
|
|
350
245
|
|
|
351
246
|
//# sourceMappingURL=langfuseToolOutputTracing.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langfuseToolOutputTracing.cjs","names":["AsyncLocalStorage","LangfuseOtelSpanAttributes","LangfuseSpanProcessor","context"],"sources":["../../src/langfuseToolOutputTracing.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { LangfuseSpanProcessor } from '@langfuse/otel';\nimport { context, createContextKey } from '@opentelemetry/api';\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 * as t from '@/types';\n\nexport const LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT = '[tool output redacted]';\n\nconst langfuseToolOutputTracingConfigKey = createContextKey(\n 'librechat.langfuse.tool-output-tracing'\n);\nconst langfuseConfigKey = createContextKey('librechat.langfuse.config');\nconst toolOutputTracingStorage =\n new AsyncLocalStorage<ResolvedLangfuseToolOutputTracingConfig>();\nconst langfuseConfigStorage = new AsyncLocalStorage<t.LangfuseConfig>();\nconst LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';\n\nconst CHAT_ROLES = new Set([\n 'assistant',\n 'developer',\n 'human',\n 'system',\n 'user',\n]);\n\nexport type ResolvedLangfuseToolOutputTracingConfig = {\n enabled: boolean;\n redactedToolNames: Set<string>;\n redactedToolNameMatchMode: 'exact' | 'partial';\n redactionText: string;\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 parseBoolean(value: string | undefined): boolean | undefined {\n if (value == null) {\n return undefined;\n }\n\n const normalized = value.trim().toLowerCase();\n if (['1', 'true', 'yes', 'on'].includes(normalized)) {\n return true;\n }\n if (['0', 'false', 'no', 'off'].includes(normalized)) {\n return false;\n }\n\n return undefined;\n}\n\nfunction normalizeToolName(name: string): string {\n return name.trim().toLowerCase();\n}\n\nfunction normalizeToolNames(names: string[] | undefined): Set<string> {\n const normalized = new Set<string>();\n for (const name of names ?? []) {\n if (isPresent(name)) {\n normalized.add(normalizeToolName(name));\n }\n }\n return normalized;\n}\n\nfunction parseToolNames(value: string | undefined): string[] | undefined {\n if (!isPresent(value)) {\n return undefined;\n }\n\n return value\n .split(',')\n .map((name) => name.trim())\n .filter((name) => name !== '');\n}\n\nfunction getEnvToolOutputTracingEnabled(): boolean | undefined {\n const traceToolOutputs = parseBoolean(\n process.env.LANGFUSE_TRACE_TOOL_OUTPUTS\n );\n if (traceToolOutputs != null) {\n return traceToolOutputs;\n }\n\n const redactToolOutputs = parseBoolean(\n process.env.LANGFUSE_REDACT_TOOL_OUTPUTS\n );\n if (redactToolOutputs != null) {\n return !redactToolOutputs;\n }\n\n return parseBoolean(process.env.LANGFUSE_TOOL_OUTPUT_TRACING_ENABLED);\n}\n\nfunction getEnvRedactedToolNames(): string[] | undefined {\n return (\n parseToolNames(process.env.LANGFUSE_REDACT_TOOL_OUTPUT_NAMES) ??\n parseToolNames(process.env.LANGFUSE_REDACT_TOOL_NAMES)\n );\n}\n\nfunction getEnvRedactionText(): string | undefined {\n return isPresent(process.env.LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT)\n ? process.env.LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT\n : undefined;\n}\n\nfunction getEnvToolNameMatchMode(): 'exact' | 'partial' | undefined {\n const mode = (\n process.env.LANGFUSE_REDACT_TOOL_OUTPUT_NAME_MATCH_MODE ??\n process.env.LANGFUSE_REDACT_TOOL_NAME_MATCH_MODE\n )\n ?.trim()\n .toLowerCase();\n if (mode === 'exact' || mode === 'partial') {\n return mode;\n }\n return undefined;\n}\n\nfunction resolveToolOutputTracingConfig(\n runLangfuse?: t.LangfuseConfig,\n agentLangfuse?: t.LangfuseConfig\n): ResolvedLangfuseToolOutputTracingConfig {\n const runConfig = runLangfuse?.toolOutputTracing;\n const agentConfig = agentLangfuse?.toolOutputTracing;\n\n return {\n enabled:\n agentConfig?.enabled ??\n runConfig?.enabled ??\n getEnvToolOutputTracingEnabled() ??\n true,\n redactedToolNames: normalizeToolNames(\n agentConfig?.redactedToolNames ??\n runConfig?.redactedToolNames ??\n getEnvRedactedToolNames()\n ),\n redactedToolNameMatchMode:\n agentConfig?.redactedToolNameMatchMode ??\n runConfig?.redactedToolNameMatchMode ??\n getEnvToolNameMatchMode() ??\n 'exact',\n redactionText:\n agentConfig?.redactionText ??\n runConfig?.redactionText ??\n getEnvRedactionText() ??\n LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,\n };\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\nfunction 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\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 classifyLangGraphToolNodeSpan(attributes);\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\nfunction getContextToolOutputTracingConfig(\n activeContext: Context\n): ResolvedLangfuseToolOutputTracingConfig | undefined {\n const asyncConfig = toolOutputTracingStorage.getStore();\n if (asyncConfig != null) {\n return asyncConfig;\n }\n\n const value = activeContext.getValue(langfuseToolOutputTracingConfigKey);\n return isRecord(value)\n ? (value as ResolvedLangfuseToolOutputTracingConfig)\n : undefined;\n}\n\nexport function getContextLangfuseConfig(\n activeContext: Context\n): t.LangfuseConfig | undefined {\n const asyncConfig = langfuseConfigStorage.getStore();\n if (asyncConfig != null) {\n return asyncConfig;\n }\n\n const value = activeContext.getValue(langfuseConfigKey);\n return isRecord(value) ? (value as t.LangfuseConfig) : undefined;\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 const config =\n getContextToolOutputTracingConfig(parentContext) ?? 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 const config =\n this.spanConfigs.get(span) ??\n toolOutputTracingStorage.getStore() ??\n this.fallbackConfig ??\n resolveToolOutputTracingConfig();\n redactLangfuseSpanToolOutputs(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 =\n runLangfuse != null || agentLangfuse != null\n ? resolveToolOutputTracingConfig(runLangfuse, agentLangfuse)\n : undefined;\n return new ToolOutputRedactingLangfuseSpanProcessor(params, fallbackConfig);\n}\n\nexport function withLangfuseToolOutputTracingConfig<T>(\n runLangfuse: t.LangfuseConfig | undefined,\n action: () => T,\n agentLangfuse?: t.LangfuseConfig\n): T {\n const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);\n const hasNoToolOutputConfig =\n runLangfuse?.toolOutputTracing == null &&\n agentLangfuse?.toolOutputTracing == null;\n\n if (langfuse == null && hasNoToolOutputConfig) {\n return action();\n }\n\n const config = hasNoToolOutputConfig\n ? undefined\n : resolveToolOutputTracingConfig(runLangfuse, agentLangfuse);\n let activeContext = context.active();\n if (langfuse != null) {\n activeContext = activeContext.setValue(langfuseConfigKey, langfuse);\n }\n if (config != null) {\n activeContext = activeContext.setValue(\n langfuseToolOutputTracingConfigKey,\n config\n );\n }\n\n const runWithContext = (): T => context.with(activeContext, action);\n const runWithToolOutputConfig = (): T =>\n config != null\n ? toolOutputTracingStorage.run(config, runWithContext)\n : runWithContext();\n\n return langfuse != null\n ? langfuseConfigStorage.run(langfuse, runWithToolOutputConfig)\n : runWithToolOutputConfig();\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 != null) {\n return (\n explicit && (hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys())\n );\n }\n\n return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();\n}\n\nexport function resolveLangfuseConfig(\n runLangfuse?: t.LangfuseConfig,\n agentLangfuse?: t.LangfuseConfig\n): t.LangfuseConfig | undefined {\n if (runLangfuse == null) {\n return agentLangfuse;\n }\n if (agentLangfuse == null) {\n return runLangfuse;\n }\n\n const toolNodeTracing =\n runLangfuse.toolNodeTracing != null || agentLangfuse.toolNodeTracing != null\n ? {\n ...runLangfuse.toolNodeTracing,\n ...agentLangfuse.toolNodeTracing,\n }\n : undefined;\n const toolOutputTracing =\n runLangfuse.toolOutputTracing != null ||\n agentLangfuse.toolOutputTracing != null\n ? {\n ...runLangfuse.toolOutputTracing,\n ...agentLangfuse.toolOutputTracing,\n }\n : undefined;\n const metadata =\n runLangfuse.metadata != null || agentLangfuse.metadata != null\n ? {\n ...runLangfuse.metadata,\n ...agentLangfuse.metadata,\n }\n : undefined;\n const tags =\n runLangfuse.tags != null || agentLangfuse.tags != null\n ? [\n ...new Set([\n ...(runLangfuse.tags ?? []),\n ...(agentLangfuse.tags ?? []),\n ]),\n ]\n : undefined;\n\n return {\n ...runLangfuse,\n ...agentLangfuse,\n ...(metadata != null ? { metadata } : {}),\n ...(tags != null ? { tags } : {}),\n ...(toolNodeTracing != null ? { toolNodeTracing } : {}),\n ...(toolOutputTracing != null ? { toolOutputTracing } : {}),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"langfuseToolOutputTracing.cjs","names":["normalizeToolName","LangfuseOtelSpanAttributes","LangfuseSpanProcessor","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 { 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\nfunction 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 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 classifyLangfuseToolNodeSpan(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 != null) {\n return (\n explicit && (hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys())\n );\n }\n\n return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();\n}\n"],"mappings":";;;;;AAsBA,MAAM,6BAA6B;AAEnC,MAAM,aAAa,IAAI,IAAI;CACzB;CACA;CACA;CACA;CACA;AACF,CAAC;AAeD,MAAM,yBAAyB,CAAC,WAAW,UAAU;AAErD,SAAS,SAAS,OAAkD;CAClE,OAAO,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC3E;AAEA,SAAS,UAAU,OAAiC;CAClD,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM;AACvD;AAEA,SAAS,+BACP,QACS;CACT,OAAO,OAAO,YAAY,SAAS,OAAO,kBAAkB,OAAO;AACrE;AAEA,SAAS,gBACP,UACA,QACS;CACT,IAAI,CAAC,UAAU,QAAQ,GACrB,OAAO;CAGT,MAAM,qBAAqBA,uBAAAA,kBAAkB,QAAQ;CACrD,IAAI,OAAO,8BAA8B,WAAW;EAClD,KAAK,MAAM,oBAAoB,OAAO,mBACpC,IAAI,mBAAmB,SAAS,gBAAgB,GAC9C,OAAO;EAGX,OAAO;CACT;CAEA,OAAO,OAAO,kBAAkB,IAAI,kBAAkB;AACxD;AAEA,SAAS,iBACP,UACA,QACS;CACT,OAAO,OAAO,YAAY,SAAS,gBAAgB,UAAU,MAAM;AACrE;AAEA,SAAS,eACP,OACA,KACoB;CACpB,MAAM,QAAQ,MAAM;CACpB,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,qBACP,OACA,WACA,UACoB;CACpB,MAAM,SAAS,MAAM;CACrB,IAAI,CAAC,SAAS,MAAM,GAClB;CAEF,OAAO,eAAe,QAAQ,QAAQ;AACxC;AAEA,SAAS,wBACP,OACoB;CACpB,OACE,eAAe,OAAO,cAAc,KACpC,qBAAqB,OAAO,UAAU,cAAc,KACpD,qBAAqB,OAAO,qBAAqB,cAAc,KAC/D,qBAAqB,OAAO,QAAQ,cAAc,MACjD,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,KAAA;AAE/C;AAEA,SAAS,sBACP,OACA,kBACoB;CACpB,MAAM,OAAO,eAAe,OAAO,MAAM;CACzC,MAAM,eACJ,eAAe,OAAO,MAAM,KAC5B,eAAe,OAAO,WAAW,KACjC,qBAAqB,OAAO,YAAY,MAAM,KAC9C,qBAAqB,OAAO,UAAU,MAAM,KAC5C,qBAAqB,OAAO,qBAAqB,MAAM,KACvD,qBAAqB,OAAO,QAAQ,MAAM,MACzC,QAAQ,QAAQ,KAAK,YAAY,MAAM,SAAS,OAAO,KAAA;CAE1D,IAAI,gBAAgB,MAClB,OAAO;CAGT,MAAM,aAAa,wBAAwB,KAAK;CAChD,OAAO,cAAc,OACjB,kBAAkB,kBAAkB,IAAI,UAAU,IAClD,KAAA;AACN;AAEA,SAAS,uBAAuB,OAAyC;CACvE,MAAM,OAAO,eAAe,OAAO,MAAM,KAAK,eAAe,OAAO,OAAO;CAC3E,IAAI,SAAS,UAAU,SAAS,gBAC9B,OAAO;CAGT,MAAM,KAAK,MAAM;CACjB,IACE,MAAM,QAAQ,EAAE,KAChB,GAAG,MAAM,SAAS,OAAO,SAAS,YAAY,KAAK,SAAS,aAAa,CAAC,GAE1E,OAAO;CAGT,IACE,kBAAkB,SAClB,qBAAqB,OAAO,UAAU,cAAc,KAAK,QACzD,qBAAqB,OAAO,qBAAqB,cAAc,KAAK,MAEpE,OAAO;CAGT,MAAM,OAAO,eAAe,OAAO,MAAM;CACzC,OACE,QAAQ,QACR,CAAC,WAAW,IAAI,KAAK,YAAY,CAAC,MACjC,aAAa,SAAS,SAAS,MAAM,MAAM,KAAK,SAAS,MAAM,IAAI;AAExE;AAEA,SAAS,wBACP,OACA,QACyB;CACzB,MAAM,OAAO,EAAE,GAAG,MAAM;CAExB,KAAK,MAAM,aAAa,wBACtB,IAAI,aAAa,MACf,KAAK,aAAa,OAAO;CAI7B,KAAK,MAAM,aAAa;EAAC;EAAU;EAAQ;CAAmB,GAAG;EAC/D,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,SAAS,MAAM,GAClB;EAEF,MAAM,aAAa,EAAE,GAAG,OAAO;EAC/B,IAAI,UAAU;EACd,KAAK,MAAM,aAAa,wBACtB,IAAI,aAAa,YAAY;GAC3B,WAAW,aAAa,OAAO;GAC/B,UAAU;EACZ;EAEF,IAAI,SACF,KAAK,aAAa;CAEtB;CAEA,OAAO;AACT;AAEA,SAAS,qBACP,OACA,kBACM;CACN,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,KAAK,MAAM,QAAQ,OACjB,qBAAqB,MAAM,gBAAgB;EAE7C;CACF;CAEA,IAAI,CAAC,SAAS,KAAK,GACjB;CAGF,MAAM,aAAa,wBAAwB,KAAK;CAChD,MAAM,WAAW,sBAAsB,KAAK;CAC5C,IAAI,cAAc,QAAQ,YAAY,MACpC,iBAAiB,kBAAkB,IAAI,YAAY,QAAQ;CAG7D,KAAK,MAAM,SAAS,OAAO,OAAO,KAAK,GACrC,qBAAqB,OAAO,gBAAgB;AAEhD;AAEA,SAAS,YACP,OACA,QACA,kBACiB;CACjB,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,IAAI,UAAU;EACd,MAAM,OAAkB,CAAC;EACzB,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,SAAS,YAAY,MAAM,QAAQ,gBAAgB;GACzD,IAAI,OAAO,SACT,UAAU;GAEZ,KAAK,KAAK,OAAO,KAAK;EACxB;EACA,OAAO,UAAU;GAAE,OAAO;GAAM;EAAQ,IAAI;GAAE;GAAO;EAAQ;CAC/D;CAEA,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO;EAAE;EAAO,SAAS;CAAM;CAGjC,MAAM,WAAW,sBAAsB,OAAO,gBAAgB;CAC9D,IAAI,uBAAuB,KAAK,KAAK,iBAAiB,UAAU,MAAM,GACpE,OAAO;EACL,OAAO,wBAAwB,OAAO,MAAM;EAC5C,SAAS;CACX;CAGF,IAAI,UAAU;CACd,MAAM,OAAgC,CAAC;CACvC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAAG;EAChD,MAAM,SAAS,YAAY,OAAO,QAAQ,gBAAgB;EAC1D,IAAI,OAAO,SACT,UAAU;EAEZ,KAAK,OAAO,OAAO;CACrB;CAEA,OAAO,UAAU;EAAE,OAAO;EAAM;CAAQ,IAAI;EAAE;EAAO;CAAQ;AAC/D;AAEA,SAAS,sBACP,OACA,QACiB;CACjB,MAAM,mBAAqC,EACzC,mCAAmB,IAAI,IAAI,EAC7B;CACA,IAAI,OAAO,UAAU,UAAU;EAC7B,qBAAqB,OAAO,gBAAgB;EAC5C,OAAO,YAAY,OAAO,QAAQ,gBAAgB;CACpD;CAEA,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,WAAW,GAAG,GACrD,OAAO;EAAE;EAAO,SAAS;CAAM;CAGjC,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,KAAK;EAC/B,qBAAqB,QAAQ,gBAAgB;EAC7C,MAAM,SAAS,YAAY,QAAQ,QAAQ,gBAAgB;EAC3D,OAAO,OAAO,UACV;GAAE,OAAO,KAAK,UAAU,OAAO,KAAK;GAAG,SAAS;EAAK,IACrD;GAAE;GAAO,SAAS;EAAM;CAC9B,QAAQ;EACN,OAAO;GAAE;GAAO,SAAS;EAAM;CACjC;AACF;AAEA,SAAS,gBACP,YACA,KACA,QACM;CACN,IAAI,EAAE,OAAO,aACX;CAGF,MAAM,SAAS,sBAAsB,WAAW,MAAM,MAAM;CAC5D,IAAI,OAAO,SACT,WAAW,OAAO,OAAO;AAE7B;AAEA,SAAS,kBAAkB,YAA8C;CACvE,MAAM,OAAO,WAAWC,kBAAAA,2BAA2B;CACnD,OAAO,OAAO,SAAS,YAAY,KAAK,YAAY,MAAM;AAC5D;AAEA,SAAS,8BACP,YACM;CACN,MAAM,OAAO,WAAWA,kBAAAA,2BAA2B;CACnD,IAAI,OAAO,SAAS,YAAY,KAAK,YAAY,MAAM,QACrD;CAGF,MAAM,gBACJ,WACE,GAAGA,kBAAAA,2BAA2B,qBAAqB;CAEvD,IACE,OAAO,kBAAkB,YACzB,cAAc,WAAW,0BAA0B,GAEnD,WAAWA,kBAAAA,2BAA2B,oBAAoB;AAE9D;AAEA,SAAgB,6BAA6B,MAA0B;CACrE,8BAA+B,KAA4B,UAAU;AACvE;AAEA,SAAS,4BACP,MACA,YACA,QACM;CACN,IACE,EACE,kBAAkB,UAAU,KAC5B,iBAAiB,KAAK,MAAM,MAAM,KAClCA,kBAAAA,2BAA2B,sBAAsB,aAGnD;CAGF,WAAWA,kBAAAA,2BAA2B,sBACpC,OAAO;AACX;AAEA,SAAgB,8BACd,MACA,QACM;CACN,MAAM,aAAc,KAA4B;CAChD,6BAA6B,IAAI;CAEjC,IAAI,CAAC,+BAA+B,MAAM,GACxC;CAGF,4BAA4B,MAAM,YAAY,MAAM;CAEpD,KAAK,MAAM,OAAO;EAChBA,kBAAAA,2BAA2B;EAC3BA,kBAAAA,2BAA2B;EAC3BA,kBAAAA,2BAA2B;EAC3BA,kBAAAA,2BAA2B;CAC7B,GACE,gBAAgB,YAAY,KAAK,MAAM;AAE3C;AAEA,IAAM,2CAAN,MAAwE;CACtE;CACA;CACA,8BAA+B,IAAI,QAGjC;CAEF,YACE,QACA,gBACA;EACA,KAAK,YAAY,IAAIC,eAAAA,sBAAsB,MAAM;EACjD,KAAK,iBAAiB;CACxB;CAEA,QAAQ,MAAY,eAA8B;EAChD,MAAM,SACJC,6BAAAA,sCAAsC,aAAa,KACnD,KAAK;EACP,IAAI,UAAU,MACZ,KAAK,YAAY,IAAI,MAAM,MAAM;EAEnC,KAAK,UAAU,QAAQ,MAAM,aAAa;CAC5C;CAEA,MAAM,MAA0B;EAC9B,6BAA6B,IAAI;EACjC,MAAM,SAAS,KAAK,YAAY,IAAI,IAAI,KAAK,KAAK;EAClD,IAAI,UAAU,MACZ,8BAA8B,MAAM,MAAM;EAE5C,KAAK,UAAU,MAAM,IAAI;CAC3B;CAEA,aAA4B;EAC1B,OAAO,KAAK,UAAU,WAAW;CACnC;CAEA,WAA0B;EACxB,OAAO,KAAK,UAAU,SAAS;CACjC;AACF;AAEA,SAAgB,4BACd,QACA,aACA,eACe;CAIf,OAAO,IAAI,yCAAyC,QAH7BC,uBAAAA,2BAA2B,aAAa,aAAa,IACxEC,uBAAAA,+BAA+B,aAAa,aAAa,IACzD,KAAA,CACsE;AAC5E;AAEA,SAAS,qBAA8B;CACrC,OACE,UAAU,QAAQ,IAAI,mBAAmB,KACzC,UAAU,QAAQ,IAAI,mBAAmB;AAE7C;AAEA,SAAS,sBAAsB,UAAsC;CACnE,IAAI,YAAY,MACd,OAAO;CAET,OAAO,UAAU,SAAS,SAAS,KAAK,UAAU,SAAS,SAAS;AACtE;AAEA,SAAgB,+BAA+B,EAC7C,aACA,iBAIU;CACV,MAAM,WAAWC,uBAAAA,sBAAsB,aAAa,aAAa;CACjE,IAAI,UAAU,YAAY,OACxB,OAAO;CAGT,MAAM,WAAW,UAAU,iBAAiB;CAC5C,IAAI,YAAY,MACd,OACE,aAAa,sBAAsB,QAAQ,KAAK,mBAAmB;CAIvE,OAAO,sBAAsB,QAAQ,KAAK,mBAAmB;AAC/D"}
|
package/dist/cjs/main.cjs
CHANGED
|
@@ -552,6 +552,7 @@ exports.normalizeBashToolResultsForReplay = require_BashProgrammaticToolCalling.
|
|
|
552
552
|
exports.normalizeServerFilter = require_ToolSearch.normalizeServerFilter;
|
|
553
553
|
exports.normalizeToBashIdentifier = require_BashProgrammaticToolCalling.normalizeToBashIdentifier;
|
|
554
554
|
exports.normalizeToPythonIdentifier = require_ProgrammaticToolCalling.normalizeToPythonIdentifier;
|
|
555
|
+
exports.parseBooleanEnv = require_misc.parseBooleanEnv;
|
|
555
556
|
exports.partitionAndMarkAnthropicToolCache = require_anthropicToolCache.partitionAndMarkAnthropicToolCache;
|
|
556
557
|
exports.performLocalSearch = require_ToolSearch.performLocalSearch;
|
|
557
558
|
exports.preFlightTruncateToolCallInputs = require_prune.preFlightTruncateToolCallInputs;
|
|
@@ -23,6 +23,19 @@ function createRemoveAllMessage() {
|
|
|
23
23
|
return new _langchain_core_messages.RemoveMessage({ id: REMOVE_ALL_MESSAGES });
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
+
* Coerce each entry to a {@link BaseMessage} in a single pass, skipping
|
|
27
|
+
* null/undefined entries. Providers can emit empty/partial stream chunks that
|
|
28
|
+
* arrive as `undefined`; passing those to `coerceMessageLikeToMessage` throws
|
|
29
|
+
* "Cannot read properties of undefined (reading 'role')" and crashes the run.
|
|
30
|
+
* Folding the null check into the coercion loop avoids a second pass over the
|
|
31
|
+
* array. Refs LibreChat Discussion #12284.
|
|
32
|
+
*/
|
|
33
|
+
function coerceMessages(messages) {
|
|
34
|
+
const coerced = [];
|
|
35
|
+
for (const message of messages) if (message != null) coerced.push((0, _langchain_core_messages.coerceMessageLikeToMessage)(message));
|
|
36
|
+
return coerced;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
26
39
|
* Prebuilt reducer that combines returned messages.
|
|
27
40
|
* Can handle standard messages and special modifiers like {@link RemoveMessage}
|
|
28
41
|
* instances.
|
|
@@ -30,8 +43,8 @@ function createRemoveAllMessage() {
|
|
|
30
43
|
function messagesStateReducer(left, right) {
|
|
31
44
|
const leftArray = Array.isArray(left) ? left : [left];
|
|
32
45
|
const rightArray = Array.isArray(right) ? right : [right];
|
|
33
|
-
const leftMessages = leftArray
|
|
34
|
-
const rightMessages = rightArray
|
|
46
|
+
const leftMessages = coerceMessages(leftArray);
|
|
47
|
+
const rightMessages = coerceMessages(rightArray);
|
|
35
48
|
for (const m of leftMessages) if (m.id === null || m.id === void 0) {
|
|
36
49
|
m.id = (0, uuid.v4)();
|
|
37
50
|
m.lc_kwargs.id = m.id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.cjs","names":["RemoveMessage"
|
|
1
|
+
{"version":3,"file":"reducer.cjs","names":["RemoveMessage"],"sources":["../../../src/messages/reducer.ts"],"sourcesContent":["import { v4 } from 'uuid';\nimport {\n BaseMessage,\n RemoveMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n} from '@langchain/core/messages';\n\nexport const REMOVE_ALL_MESSAGES = '__remove_all__';\n\n/**\n * Creates a message that instructs messagesStateReducer to remove ALL\n * existing messages from state. Messages appearing after this one in\n * the array become the new state.\n *\n * Usage (in a node return value):\n * ```ts\n * return { messages: [createRemoveAllMessage(), ...survivingMessages] };\n * ```\n *\n * This works because the reducer checks for `getType() === 'remove'`\n * with `id === REMOVE_ALL_MESSAGES` and discards everything before it.\n *\n * NOTE: Uses RemoveMessage from @langchain/core with a sentinel id so\n * the reducer can distinguish a \"remove-all\" marker from a single-message\n * removal.\n */\nexport function createRemoveAllMessage(): BaseMessage {\n return new RemoveMessage({ id: REMOVE_ALL_MESSAGES });\n}\n\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Coerce each entry to a {@link BaseMessage} in a single pass, skipping\n * null/undefined entries. Providers can emit empty/partial stream chunks that\n * arrive as `undefined`; passing those to `coerceMessageLikeToMessage` throws\n * \"Cannot read properties of undefined (reading 'role')\" and crashes the run.\n * Folding the null check into the coercion loop avoids a second pass over the\n * array. Refs LibreChat Discussion #12284.\n */\nfunction coerceMessages(\n messages: ReadonlyArray<BaseMessageLike | null | undefined>\n): BaseMessage[] {\n const coerced: BaseMessage[] = [];\n for (const message of messages) {\n if (message != null) {\n coerced.push(coerceMessageLikeToMessage(message));\n }\n }\n return coerced;\n}\n\n/**\n * Prebuilt reducer that combines returned messages.\n * Can handle standard messages and special modifiers like {@link RemoveMessage}\n * instances.\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n // coerce to message, skipping null/undefined entries in the same pass\n const leftMessages = coerceMessages(leftArray as BaseMessageLike[]);\n const rightMessages = coerceMessages(rightArray as BaseMessageLike[]);\n // assign missing ids\n for (const m of leftMessages) {\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n }\n\n let removeAllIdx: number | undefined;\n for (let i = 0; i < rightMessages.length; i += 1) {\n const m = rightMessages[i];\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n\n if (m.getType() === 'remove' && m.id === REMOVE_ALL_MESSAGES) {\n removeAllIdx = i;\n }\n }\n\n if (removeAllIdx != null) return rightMessages.slice(removeAllIdx + 1);\n\n // merge\n const merged = [...leftMessages];\n const mergedById = new Map(merged.map((m, i) => [m.id, i]));\n const idsToRemove = new Set();\n for (const m of rightMessages) {\n const existingIdx = mergedById.get(m.id);\n if (existingIdx !== undefined) {\n if (m.getType() === 'remove') {\n idsToRemove.add(m.id);\n } else {\n idsToRemove.delete(m.id);\n merged[existingIdx] = m;\n }\n } else {\n if (m.getType() === 'remove') {\n throw new Error(\n `Attempting to delete a message with an ID that doesn't exist ('${m.id}')`\n );\n }\n mergedById.set(m.id, merged.length);\n merged.push(m);\n }\n }\n return merged.filter((m) => !idsToRemove.has(m.id));\n}\n"],"mappings":";;;AAQA,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;AAmBnC,SAAgB,yBAAsC;CACpD,OAAO,IAAIA,yBAAAA,cAAc,EAAE,IAAI,oBAAoB,CAAC;AACtD;;;;;;;;;AAeA,SAAS,eACP,UACe;CACf,MAAM,UAAyB,CAAC;CAChC,KAAK,MAAM,WAAW,UACpB,IAAI,WAAW,MACb,QAAQ,MAAA,GAAA,yBAAA,2BAAA,CAAgC,OAAO,CAAC;CAGpD,OAAO;AACT;;;;;;AAOA,SAAgB,qBACd,MACA,OACe;CACf,MAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CACpD,MAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;CAExD,MAAM,eAAe,eAAe,SAA8B;CAClE,MAAM,gBAAgB,eAAe,UAA+B;CAEpE,KAAK,MAAM,KAAK,cACd,IAAI,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAA,GAAW;EACvC,EAAE,MAAA,GAAA,KAAA,GAAA,CAAQ;EACV,EAAE,UAAU,KAAK,EAAE;CACrB;CAGF,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK,GAAG;EAChD,MAAM,IAAI,cAAc;EACxB,IAAI,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAA,GAAW;GACvC,EAAE,MAAA,GAAA,KAAA,GAAA,CAAQ;GACV,EAAE,UAAU,KAAK,EAAE;EACrB;EAEA,IAAI,EAAE,QAAQ,MAAM,YAAY,EAAE,OAAA,kBAChC,eAAe;CAEnB;CAEA,IAAI,gBAAgB,MAAM,OAAO,cAAc,MAAM,eAAe,CAAC;CAGrE,MAAM,SAAS,CAAC,GAAG,YAAY;CAC/B,MAAM,aAAa,IAAI,IAAI,OAAO,KAAK,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAC1D,MAAM,8BAAc,IAAI,IAAI;CAC5B,KAAK,MAAM,KAAK,eAAe;EAC7B,MAAM,cAAc,WAAW,IAAI,EAAE,EAAE;EACvC,IAAI,gBAAgB,KAAA,GAClB,IAAI,EAAE,QAAQ,MAAM,UAClB,YAAY,IAAI,EAAE,EAAE;OACf;GACL,YAAY,OAAO,EAAE,EAAE;GACvB,OAAO,eAAe;EACxB;OACK;GACL,IAAI,EAAE,QAAQ,MAAM,UAClB,MAAM,IAAI,MACR,kEAAkE,EAAE,GAAG,GACzE;GAEF,WAAW,IAAI,EAAE,IAAI,OAAO,MAAM;GAClC,OAAO,KAAK,CAAC;EACf;CACF;CACA,OAAO,OAAO,QAAQ,MAAM,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;AACpD"}
|
package/dist/cjs/run.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
const require_langfuseConfig = require("./langfuseConfig.cjs");
|
|
2
|
+
const require_langfuseRuntimeScope = require("./langfuseRuntimeScope.cjs");
|
|
1
3
|
const require_langfuse = require("./langfuse.cjs");
|
|
2
|
-
const require_langfuseToolOutputTracing = require("./langfuseToolOutputTracing.cjs");
|
|
3
4
|
const require_callbacks = require("./utils/callbacks.cjs");
|
|
4
5
|
require("./common/enum.cjs");
|
|
5
6
|
require("./common/index.cjs");
|
|
6
7
|
const require_title = require("./utils/title.cjs");
|
|
7
|
-
const require_instrumentation = require("./instrumentation.cjs");
|
|
8
8
|
const require_tokens = require("./utils/tokens.cjs");
|
|
9
|
+
const require_instrumentation = require("./instrumentation.cjs");
|
|
9
10
|
const require_llm = require("./utils/llm.cjs");
|
|
10
11
|
const require_events = require("./events.cjs");
|
|
11
12
|
const require_executeHooks = require("./hooks/executeHooks.cjs");
|
|
@@ -380,16 +381,16 @@ var Run = class Run {
|
|
|
380
381
|
}
|
|
381
382
|
getStreamLangfuseConfig(graph) {
|
|
382
383
|
const primaryContext = graph.agentContexts.get(graph.defaultAgentId);
|
|
383
|
-
if (primaryContext != null) return
|
|
384
|
+
if (primaryContext != null) return require_langfuseConfig.resolveLangfuseConfig(this.langfuse, primaryContext.langfuse);
|
|
384
385
|
for (const context of graph.agentContexts.values()) {
|
|
385
|
-
const langfuse =
|
|
386
|
+
const langfuse = require_langfuseConfig.resolveLangfuseConfig(this.langfuse, context.langfuse);
|
|
386
387
|
if (langfuse != null) return langfuse;
|
|
387
388
|
}
|
|
388
389
|
return this.langfuse;
|
|
389
390
|
}
|
|
390
391
|
getStreamToolOutputTracingLangfuseConfig(graph) {
|
|
391
392
|
const toolOutputTracingConfigs = Array.from(graph.agentContexts.values()).map((context) => {
|
|
392
|
-
return
|
|
393
|
+
return require_langfuseConfig.resolveLangfuseConfig(this.langfuse, context.langfuse)?.toolOutputTracing;
|
|
393
394
|
}).filter((config) => {
|
|
394
395
|
return config != null;
|
|
395
396
|
});
|
|
@@ -471,7 +472,8 @@ var Run = class Run {
|
|
|
471
472
|
userId,
|
|
472
473
|
sessionId,
|
|
473
474
|
traceMetadata,
|
|
474
|
-
tags: ["librechat", "agent"]
|
|
475
|
+
tags: ["librechat", "agent"],
|
|
476
|
+
traceIdSeed: streamLangfuseConfig?.deterministicTraceId === true ? this.id : void 0
|
|
475
477
|
});
|
|
476
478
|
if (langfuseHandler != null) {
|
|
477
479
|
config.runName = traceName;
|
|
@@ -594,14 +596,18 @@ var Run = class Run {
|
|
|
594
596
|
}).catch(() => {});
|
|
595
597
|
};
|
|
596
598
|
try {
|
|
597
|
-
await
|
|
599
|
+
await require_langfuseRuntimeScope.withLangfuseRuntimeScope(require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
|
|
600
|
+
runLangfuse: streamLangfuseConfig,
|
|
601
|
+
langfuseOverlay: this.getStreamToolOutputTracingLangfuseConfig(graph),
|
|
602
|
+
traceIdSeed: streamLangfuseConfig?.deterministicTraceId === true ? this.id : void 0
|
|
603
|
+
}), () => require_langfuse.withLangfuseAttributes({
|
|
598
604
|
langfuse: streamLangfuseConfig,
|
|
599
605
|
userId,
|
|
600
606
|
sessionId,
|
|
601
607
|
traceName,
|
|
602
608
|
traceMetadata,
|
|
603
609
|
tags: ["librechat", "agent"]
|
|
604
|
-
}, consumeStream)
|
|
610
|
+
}, consumeStream));
|
|
605
611
|
} catch (err) {
|
|
606
612
|
streamThrew = true;
|
|
607
613
|
if (this.hookRegistry?.hasHookFor("StopFailure", this.id) === true) {
|
|
@@ -841,14 +847,15 @@ var Run = class Run {
|
|
|
841
847
|
if (chainOptions != null) {
|
|
842
848
|
titleUserId = typeof chainOptions.configurable?.user_id === "string" ? chainOptions.configurable.user_id : void 0;
|
|
843
849
|
titleSessionId = typeof chainOptions.configurable?.thread_id === "string" ? chainOptions.configurable.thread_id : void 0;
|
|
844
|
-
titleLangfuseConfig =
|
|
850
|
+
titleLangfuseConfig = require_langfuseConfig.resolveLangfuseConfig(this.langfuse, titleContext?.langfuse);
|
|
845
851
|
require_instrumentation.initializeLangfuseTracing(titleLangfuseConfig);
|
|
846
852
|
titleLangfuseHandler = require_langfuse.createLangfuseHandler({
|
|
847
853
|
langfuse: titleLangfuseConfig,
|
|
848
854
|
userId: titleUserId,
|
|
849
855
|
sessionId: titleSessionId,
|
|
850
856
|
traceMetadata,
|
|
851
|
-
tags: ["librechat", "title"]
|
|
857
|
+
tags: ["librechat", "title"],
|
|
858
|
+
traceIdSeed: titleLangfuseConfig?.deterministicTraceId === true ? "title-" + this.id : void 0
|
|
852
859
|
});
|
|
853
860
|
if (titleLangfuseHandler != null) chainOptions.callbacks = require_callbacks.appendCallbacks(chainOptions.callbacks, [titleLangfuseHandler]);
|
|
854
861
|
}
|
|
@@ -894,12 +901,18 @@ var Run = class Run {
|
|
|
894
901
|
}, runtimeConfig));
|
|
895
902
|
try {
|
|
896
903
|
try {
|
|
897
|
-
return await
|
|
904
|
+
return await require_langfuseRuntimeScope.withLangfuseRuntimeScope(require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
|
|
905
|
+
runLangfuse: this.langfuse,
|
|
906
|
+
langfuseOverlay: titleContext?.langfuse
|
|
907
|
+
}), () => invokeTitleChain(invokeConfig));
|
|
898
908
|
} catch (_e) {
|
|
899
909
|
const langfuseHandler = require_callbacks.findCallback(invokeConfig.callbacks, require_langfuse.isLangfuseCallbackHandler);
|
|
900
910
|
const { callbacks: _cb, ...rest } = invokeConfig;
|
|
901
911
|
const safeConfig = Object.assign({}, rest, { callbacks: langfuseHandler ? [langfuseHandler] : [] });
|
|
902
|
-
return await
|
|
912
|
+
return await require_langfuseRuntimeScope.withLangfuseRuntimeScope(require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
|
|
913
|
+
runLangfuse: this.langfuse,
|
|
914
|
+
langfuseOverlay: titleContext?.langfuse
|
|
915
|
+
}), () => invokeTitleChain(safeConfig));
|
|
903
916
|
}
|
|
904
917
|
} finally {
|
|
905
918
|
await require_langfuse.disposeLangfuseHandler(titleLangfuseHandler);
|