@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.
Files changed (61) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +4 -0
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +21 -2
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/langfuseToolOutputTracing.cjs +228 -16
  6. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  7. package/dist/cjs/llm/init.cjs +1 -1
  8. package/dist/cjs/llm/invoke.cjs +14 -7
  9. package/dist/cjs/llm/invoke.cjs.map +1 -1
  10. package/dist/cjs/llm/openai/index.cjs +188 -11
  11. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  12. package/dist/cjs/main.cjs +4 -3
  13. package/dist/cjs/messages/core.cjs +592 -27
  14. package/dist/cjs/messages/core.cjs.map +1 -1
  15. package/dist/cjs/run.cjs +11 -1
  16. package/dist/cjs/run.cjs.map +1 -1
  17. package/dist/cjs/stream.cjs +2 -2
  18. package/dist/cjs/tools/ToolNode.cjs +1 -1
  19. package/dist/cjs/tools/search/tool.cjs +1 -1
  20. package/dist/cjs/utils/index.cjs +1 -1
  21. package/dist/esm/agents/AgentContext.mjs +4 -0
  22. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  23. package/dist/esm/graphs/Graph.mjs +21 -2
  24. package/dist/esm/graphs/Graph.mjs.map +1 -1
  25. package/dist/esm/langfuseToolOutputTracing.mjs +228 -16
  26. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  27. package/dist/esm/llm/init.mjs +1 -1
  28. package/dist/esm/llm/invoke.mjs +14 -7
  29. package/dist/esm/llm/invoke.mjs.map +1 -1
  30. package/dist/esm/llm/openai/index.mjs +190 -13
  31. package/dist/esm/llm/openai/index.mjs.map +1 -1
  32. package/dist/esm/main.mjs +5 -5
  33. package/dist/esm/messages/core.mjs +592 -28
  34. package/dist/esm/messages/core.mjs.map +1 -1
  35. package/dist/esm/run.mjs +11 -1
  36. package/dist/esm/run.mjs.map +1 -1
  37. package/dist/esm/stream.mjs +2 -2
  38. package/dist/esm/tools/ToolNode.mjs +1 -1
  39. package/dist/esm/tools/search/tool.mjs +1 -1
  40. package/dist/esm/utils/index.mjs +1 -1
  41. package/dist/types/agents/AgentContext.d.ts +2 -0
  42. package/dist/types/graphs/Graph.d.ts +5 -0
  43. package/dist/types/langfuseToolOutputTracing.d.ts +1 -0
  44. package/dist/types/llm/invoke.d.ts +1 -1
  45. package/dist/types/messages/core.d.ts +11 -6
  46. package/dist/types/run.d.ts +7 -0
  47. package/package.json +1 -1
  48. package/src/agents/AgentContext.ts +5 -0
  49. package/src/graphs/Graph.ts +39 -0
  50. package/src/langfuseToolOutputTracing.ts +410 -14
  51. package/src/llm/custom-chat-models.smoke.test.ts +747 -0
  52. package/src/llm/invoke.test.ts +98 -0
  53. package/src/llm/invoke.ts +34 -23
  54. package/src/llm/openai/index.ts +334 -25
  55. package/src/llm/openai/llm.spec.ts +107 -6
  56. package/src/messages/core.ts +1290 -42
  57. package/src/messages/formatAgentMessages.test.ts +2623 -0
  58. package/src/run.ts +15 -0
  59. package/src/specs/discovered-tools.test.ts +217 -0
  60. package/src/specs/langfuse-tool-output-tracing.test.ts +887 -0
  61. 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 collectToolCallNames(value, redactionContext) {
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) collectToolCallNames(item, redactionContext);
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
- for (const child of Object.values(value)) collectToolCallNames(child, redactionContext);
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 = { toolNamesByCallId: /* @__PURE__ */ new Map() };
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
- collectToolCallNames(value, redactionContext);
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
- collectToolCallNames(parsed, redactionContext);
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: false
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
- classifyLangfuseToolNodeSpan(span);
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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"}
@@ -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
@@ -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 providerInputMessages = require_core.projectToolStreamContentForProvider(messages);
61
- if (usesNativeOpenAIResponses(model, provider, callOptions)) return require_core.projectOpenAIResponsesToolMessageContent(require_cache.stripAnthropicCacheControl(require_cache.stripBedrockCacheControl(providerInputMessages)), maxToolResultChars);
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
- chunk.usage_metadata = {
177
+ const usageMetadata = {
177
178
  input_tokens: inputTokens,
178
179
  output_tokens: outputTokens,
179
180
  total_tokens: inputTokens + outputTokens
180
181
  };
181
- chunk.response_metadata = {
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
- finalChunk.response_metadata = {
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);