@librechat/agents 3.3.8 → 3.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs +4 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +66 -13
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +35 -2
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/hitl/askUserQuestion.cjs +3 -2
- package/dist/cjs/hitl/askUserQuestion.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +18 -48
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +174 -29
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseConfig.cjs +12 -0
- package/dist/cjs/langfuseConfig.cjs.map +1 -1
- package/dist/cjs/langfuseRuntimeContext.cjs +23 -2
- package/dist/cjs/langfuseRuntimeContext.cjs.map +1 -1
- package/dist/cjs/langfuseRuntimeScope.cjs +39 -8
- package/dist/cjs/langfuseRuntimeScope.cjs.map +1 -1
- package/dist/cjs/langfuseSpanRegistry.cjs +95 -0
- package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -0
- package/dist/cjs/langfuseTraceShaping.cjs +121 -4
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +39 -15
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +25 -5
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +3 -3
- package/dist/cjs/llm/invoke.cjs +5 -5
- package/dist/cjs/llm/openai/index.cjs +1 -1
- package/dist/cjs/main.cjs +10 -10
- package/dist/cjs/messages/prune.cjs +13 -1
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/prompts/activityLabel.cjs +24 -12
- package/dist/cjs/prompts/activityLabel.cjs.map +1 -1
- package/dist/cjs/run.cjs +57 -22
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/messageSerialization.cjs +6 -0
- package/dist/cjs/session/messageSerialization.cjs.map +1 -1
- package/dist/cjs/stream.cjs +21 -10
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +5 -0
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +253 -24
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/search/tool.cjs +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
- package/dist/cjs/utils/index.cjs +2 -2
- package/dist/esm/agents/AgentContext.mjs +4 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +67 -14
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +35 -2
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/hitl/askUserQuestion.mjs +3 -2
- package/dist/esm/hitl/askUserQuestion.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +18 -48
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +176 -28
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseConfig.mjs +10 -1
- package/dist/esm/langfuseConfig.mjs.map +1 -1
- package/dist/esm/langfuseRuntimeContext.mjs +21 -3
- package/dist/esm/langfuseRuntimeContext.mjs.map +1 -1
- package/dist/esm/langfuseRuntimeScope.mjs +39 -10
- package/dist/esm/langfuseRuntimeScope.mjs.map +1 -1
- package/dist/esm/langfuseSpanRegistry.mjs +91 -0
- package/dist/esm/langfuseSpanRegistry.mjs.map +1 -0
- package/dist/esm/langfuseTraceShaping.mjs +121 -4
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +39 -15
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +25 -5
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +2 -2
- package/dist/esm/llm/invoke.mjs +5 -5
- package/dist/esm/llm/openai/index.mjs +1 -1
- package/dist/esm/main.mjs +8 -8
- package/dist/esm/messages/prune.mjs +13 -1
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/prompts/activityLabel.mjs +24 -12
- package/dist/esm/prompts/activityLabel.mjs.map +1 -1
- package/dist/esm/run.mjs +57 -22
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/messageSerialization.mjs +6 -0
- package/dist/esm/session/messageSerialization.mjs.map +1 -1
- package/dist/esm/stream.mjs +21 -10
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +5 -0
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +254 -25
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/search/tool.mjs +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
- package/dist/esm/utils/index.mjs +2 -2
- package/dist/types/agents/AgentContext.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +24 -0
- package/dist/types/hitl/askUserQuestion.d.ts +11 -1
- package/dist/types/langfuse.d.ts +16 -8
- package/dist/types/langfuseConfig.d.ts +6 -0
- package/dist/types/langfuseRuntimeContext.d.ts +27 -1
- package/dist/types/langfuseRuntimeScope.d.ts +17 -2
- package/dist/types/langfuseSpanRegistry.d.ts +17 -0
- package/dist/types/langfuseTraceShaping.d.ts +2 -1
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -0
- package/dist/types/run.d.ts +7 -0
- package/dist/types/session/types.d.ts +1 -0
- package/dist/types/tools/ToolNode.d.ts +7 -1
- package/dist/types/types/hitl.d.ts +8 -0
- package/dist/types/types/tools.d.ts +30 -0
- package/package.json +7 -4
- package/src/__tests__/stream.eagerArgsDivergence.test.ts +753 -0
- package/src/agents/AgentContext.ts +5 -0
- package/src/graphs/Graph.ts +108 -20
- package/src/graphs/MultiAgentGraph.ts +56 -2
- package/src/graphs/__tests__/composition.smoke.test.ts +4 -0
- package/src/hitl/askUserQuestion.ts +14 -1
- package/src/instrumentation.ts +35 -77
- package/src/langfuse.ts +320 -43
- package/src/langfuseConfig.ts +24 -0
- package/src/langfuseRuntimeContext.ts +43 -1
- package/src/langfuseRuntimeScope.ts +94 -21
- package/src/langfuseSpanRegistry.ts +131 -0
- package/src/langfuseTraceShaping.ts +194 -7
- package/src/llm/anthropic/utils/message_inputs.ts +70 -19
- package/src/llm/anthropic/utils/streaming-tool-input.test.ts +186 -11
- package/src/llm/bedrock/utils/message_inputs.test.ts +120 -4
- package/src/llm/bedrock/utils/message_inputs.ts +32 -7
- package/src/messages/prune.ts +12 -1
- package/src/prompts/activityLabel.ts +23 -6
- package/src/run.ts +91 -45
- package/src/scripts/activity-labels/captured.json +56 -0
- package/src/scripts/activity-labels/checks.cjs +205 -0
- package/src/scripts/activity-labels/corpus.cjs +473 -0
- package/src/scripts/activity-labels/report.cjs +203 -0
- package/src/scripts/activity-labels/rescore.cjs +102 -0
- package/src/scripts/activity-labels/run.ts +705 -0
- package/src/scripts/activity-labels/variants.ts +71 -0
- package/src/session/messageSerialization.ts +12 -1
- package/src/session/types.ts +1 -0
- package/src/specs/activity-label-prompt.test.ts +26 -10
- package/src/specs/agent-handoffs.test.ts +306 -0
- package/src/specs/discovered-tools.test.ts +217 -0
- package/src/specs/langfuse-callbacks.test.ts +456 -0
- package/src/specs/langfuse-routing.integration.test.ts +138 -1
- package/src/specs/langfuse-span-registry.test.ts +70 -0
- package/src/specs/langfuse-trace-shaping.test.ts +294 -0
- package/src/specs/prune.test.ts +38 -1
- package/src/stream.ts +70 -6
- package/src/summarization/node.ts +5 -0
- package/src/tools/ToolNode.ts +400 -9
- package/src/tools/__tests__/ToolNode.invalidToolCalls.test.ts +757 -0
- package/src/tools/__tests__/hitl.test.ts +58 -0
- package/src/types/hitl.ts +8 -0
- package/src/types/tools.ts +35 -1
|
@@ -31,8 +31,8 @@ function truncateForLabel(value, maxLength) {
|
|
|
31
31
|
* Reduces a committed label to bounded single-line data.
|
|
32
32
|
*
|
|
33
33
|
* Sections in this prompt are delimited by blank lines, so a label carrying
|
|
34
|
-
* embedded newlines could otherwise forge an apparent
|
|
35
|
-
* `
|
|
34
|
+
* embedded newlines could otherwise forge an apparent entries section or
|
|
35
|
+
* `Header:` cue. Unlike every other input here, previous labels re-enter
|
|
36
36
|
* the prompt on EVERY later batch, so one malformed result — plain model
|
|
37
37
|
* noncompliance, or injection surfacing through a tool result — would
|
|
38
38
|
* persistently steer unrelated later labels rather than affecting one. The
|
|
@@ -109,17 +109,29 @@ function buildActivityLabelPrompt({ entries, charLimit, thinkingExcerpts, lastAs
|
|
|
109
109
|
if (entries.length > 0) {
|
|
110
110
|
const shown = entries.slice(0, MAX_PROMPT_ENTRIES);
|
|
111
111
|
const omitted = entries.length - shown.length;
|
|
112
|
-
sections.push(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
sections.push(
|
|
113
|
+
/** Frames the list as reference material, not the thing to
|
|
114
|
+
* transcribe. Ported from LibreChat's fallback builder (its
|
|
115
|
+
* runtime.ts documents that without this the model "hands back a
|
|
116
|
+
* transcription" of the list) after the eval harness measured it
|
|
117
|
+
* across three independent sweeps: fewer template-redundancy and
|
|
118
|
+
* length violations than a bare `Tool calls:` heading, with no
|
|
119
|
+
* per-case regressions (agents #360). */
|
|
120
|
+
"What it called, and what came back (do not restate these):\n" + shown.map((entry) => {
|
|
121
|
+
const input = clip(serializeForLabel(entry.toolInput, charLimit), charLimit);
|
|
122
|
+
const redacted = redaction != null && require_langfuseToolOutputTracing.shouldRedactTool(entry.toolName, redaction);
|
|
123
|
+
let outcome;
|
|
124
|
+
if (redacted) outcome = redaction.redactionText;
|
|
125
|
+
else if (entry.status === "error") outcome = `ERROR: ${clip(entry.error ?? "unknown error", charLimit)}`;
|
|
126
|
+
else outcome = clip(serializeForLabel(entry.toolOutput, charLimit), charLimit);
|
|
127
|
+
return `- ${entry.toolName}(${input}) → ${outcome}`;
|
|
128
|
+
}).join("\n") + (omitted > 0 ? `\n- …and ${omitted} more tool ${omitted === 1 ? "call" : "calls"}` : "")
|
|
129
|
+
);
|
|
121
130
|
}
|
|
122
|
-
|
|
131
|
+
/** The fallback builder's terminal cue, measured alongside the heading
|
|
132
|
+
* (same sweeps). The default system prompt already describes the
|
|
133
|
+
* output as "the header of a collapsed activity group". */
|
|
134
|
+
sections.push("Header:");
|
|
123
135
|
return sections.join("\n\n");
|
|
124
136
|
}
|
|
125
137
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activityLabel.cjs","names":["shouldRedactTool"],"sources":["../../../src/prompts/activityLabel.ts"],"sourcesContent":["import type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\nimport type { ActivityLabelToolEntry } from '@/types/activityLabel';\nimport { shouldRedactTool } from '@/langfuseToolOutputTracing';\n\n/**\n * Default system prompt for fast-model activity labeling.\n *\n * Style synthesized from Claude Code's tool-use summary prompt (git-subject\n * register, past tense, distinctive nouns) and claude.ai's observed group\n * headers (5–9 words describing a mixed reasoning + tool block, e.g.\n * \"Synthesized version data and curated comparative framework\").\n */\nexport const ACTIVITY_LABEL_PROMPT = `Write a short label describing what this block of agent activity accomplished. It appears as the header of a collapsed activity group in a chat UI.\n\nRules:\n- 5 to 9 words, past-tense verb first\n- Name the most distinctive subject (file, API, topic); drop articles and filler\n- Describe outcomes, not mechanics; if something failed, say so plainly\n- Output only the label — no quotes, no punctuation at the end, no preamble\n\nExamples:\n- Searched Node.js release notes and changelogs\n- Compared runtime versions across official sources\n- Fixed failing auth middleware tests\n- Read project config and dependency manifests\n- Attempted database migration, hit permission errors`;\n\n/** Truncates a serialized value for the label prompt. */\nexport function truncateForLabel(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n return value.slice(0, Math.max(0, maxLength - 1)) + '…';\n}\n\n/**\n * Reduces a committed label to bounded single-line data.\n *\n * Sections in this prompt are delimited by blank lines, so a label carrying\n * embedded newlines could otherwise forge an apparent `Tool calls:` or\n * `Label:` section. Unlike every other input here, previous labels re-enter\n * the prompt on EVERY later batch, so one malformed result — plain model\n * noncompliance, or injection surfacing through a tool result — would\n * persistently steer unrelated later labels rather than affecting one. The\n * clip bounds the same way `lastAssistantText` and reasoning excerpts are\n * bounded: oversized headers must not inflate later requests past the fast\n * model's window and starve the run of labels entirely.\n */\nfunction sanitizePreviousLabel(label: string): string {\n return truncateForLabel(label.replace(/\\s+/g, ' ').trim(), PREVIOUS_LABEL_LIMIT);\n}\n\nconst ABORT_SERIALIZATION = Symbol('abort-label-serialization');\n\n/**\n * Serializes a tool value for the prompt WITHOUT materializing huge JSON:\n * the output is clipped to a few hundred characters anyway, so a multi-\n * megabyte tool result must not be stringified in full on the label path.\n * Strings clip immediately; structured values serialize under a character\n * budget and degrade to a shape summary once it is exhausted.\n */\nfunction serializeForLabel(value: unknown, limit: number): string {\n if (value == null) {\n return '';\n }\n if (typeof value === 'string') {\n return value.length > limit ? value.slice(0, limit + 1) : value;\n }\n let budget = limit * 4;\n try {\n return (\n JSON.stringify(value, (_key, nested: unknown) => {\n if (budget <= 0) {\n throw ABORT_SERIALIZATION;\n }\n if (typeof nested === 'string') {\n const clipped =\n nested.length > limit ? nested.slice(0, limit) : nested;\n budget -= clipped.length;\n return clipped;\n }\n budget -= 8;\n return nested;\n }) ?? ''\n );\n } catch (error) {\n if (error === ABORT_SERIALIZATION) {\n return Array.isArray(value) ? `[Array(${value.length})]` : '[Object]';\n }\n return String(value);\n }\n}\n\nconst INPUT_CONTEXT_LIMIT = 200;\nconst MAX_THINKING_EXCERPTS = 4;\nconst MAX_PREVIOUS_LABELS = 3;\n/** Per-label bound. A header is 5-9 words; anything past this is\n * noncompliance or payload, and previous labels are the one input that\n * RE-ENTERS the prompt on every later batch of the run. */\nconst PREVIOUS_LABEL_LIMIT = 200;\n/** A label is 5-9 words; no batch needs more than this many entries to\n * produce one, and the cap keeps a 200-call programmatic batch from\n * building an enormous prompt out of per-field-bounded pieces. */\nconst MAX_PROMPT_ENTRIES = 12;\n\nexport type BuildActivityLabelPromptParams = {\n entries: ActivityLabelToolEntry[];\n charLimit: number;\n thinkingExcerpts?: string[];\n lastAssistantText?: string;\n /**\n * Headers already committed for earlier batches in this run, in run order\n * with the most recent last. Rendered ahead of the block context so the\n * label continues the run's story instead of restating a line the user is\n * already reading. Capped at {@link MAX_PREVIOUS_LABELS}.\n */\n previousLabels?: string[];\n /**\n * Resolved tool-output tracing policy. The label prompt becomes Langfuse\n * generation input, so outputs/errors excluded from tracing (global\n * disable or `redactedToolNames`) must never appear in it — the same\n * redaction the span processor applies to structured tool observations.\n */\n redaction?: ResolvedLangfuseToolOutputTracingConfig;\n};\n\n/**\n * Builds the user prompt for a fast-model activity label. Pure — exported\n * for direct testing of redaction and truncation behavior.\n */\nexport function buildActivityLabelPrompt({\n entries,\n charLimit,\n thinkingExcerpts,\n lastAssistantText,\n previousLabels,\n redaction,\n}: BuildActivityLabelPromptParams): string {\n const clip = truncateForLabel;\n /** Reasoning and intent text can quote tool output verbatim — including\n * output from EARLIER calls to a redacted tool that this batch does not\n * contain — so any active policy (global disable or a configured\n * redacted-name list) drops both wholesale. There is no reliable way to\n * scrub a quoted fragment out of free-form model prose. */\n const excerptsRedacted =\n redaction != null &&\n (redaction.enabled === false || redaction.redactedToolNames.size > 0);\n const sections: string[] = [];\n /** Previous labels are free-form model prose too, and per-agent overlays\n * mean an earlier header may have been generated under ANOTHER agent's\n * weaker policy — so they share the excerpts' wholesale drop rather than\n * letting a handoff leak a looser agent's phrasing into this trace. */\n if (!excerptsRedacted && previousLabels != null && previousLabels.length > 0) {\n const recent = previousLabels\n .slice(-MAX_PREVIOUS_LABELS)\n .map(sanitizePreviousLabel)\n /** A label that sanitizes to nothing carries no story to continue;\n * rendering it would leave a bare bullet implying a missing header. */\n .filter((label) => label.length > 0);\n if (recent.length > 0) {\n sections.push(\n 'Previous headers in this run (most recent last):\\n' +\n recent.map((label) => `- ${label}`).join('\\n')\n );\n }\n }\n /** Intent text is free-form assistant prose that can quote a redacted\n * tool result just as reasoning can, so it shares the excerpts' fate. */\n if (\n !excerptsRedacted &&\n lastAssistantText != null &&\n lastAssistantText.length > 0\n ) {\n sections.push(\n `Intent (assistant's last message): ${clip(lastAssistantText, INPUT_CONTEXT_LIMIT)}`\n );\n }\n if (\n !excerptsRedacted &&\n thinkingExcerpts != null &&\n thinkingExcerpts.length > 0\n ) {\n sections.push(\n 'Reasoning excerpts:\\n' +\n thinkingExcerpts\n .slice(0, MAX_THINKING_EXCERPTS)\n .map((excerpt) => `- ${clip(excerpt, charLimit)}`)\n .join('\\n')\n );\n }\n if (entries.length > 0) {\n const shown = entries.slice(0, MAX_PROMPT_ENTRIES);\n const omitted = entries.length - shown.length;\n sections.push(\n 'Tool calls:\\n' +\n shown\n .map((entry) => {\n const input = clip(\n serializeForLabel(entry.toolInput, charLimit),\n charLimit\n );\n const redacted =\n redaction != null && shouldRedactTool(entry.toolName, redaction);\n let outcome: string;\n if (redacted) {\n outcome = redaction.redactionText;\n } else if (entry.status === 'error') {\n outcome = `ERROR: ${clip(entry.error ?? 'unknown error', charLimit)}`;\n } else {\n outcome = clip(\n serializeForLabel(entry.toolOutput, charLimit),\n charLimit\n );\n }\n return `- ${entry.toolName}(${input}) → ${outcome}`;\n })\n .join('\\n') +\n (omitted > 0\n ? `\\n- …and ${omitted} more tool ${omitted === 1 ? 'call' : 'calls'}`\n : '')\n );\n }\n sections.push('Label:');\n return sections.join('\\n\\n');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"activityLabel.cjs","names":["shouldRedactTool"],"sources":["../../../src/prompts/activityLabel.ts"],"sourcesContent":["import type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\nimport type { ActivityLabelToolEntry } from '@/types/activityLabel';\nimport { shouldRedactTool } from '@/langfuseToolOutputTracing';\n\n/**\n * Default system prompt for fast-model activity labeling.\n *\n * Style synthesized from Claude Code's tool-use summary prompt (git-subject\n * register, past tense, distinctive nouns) and claude.ai's observed group\n * headers (5–9 words describing a mixed reasoning + tool block, e.g.\n * \"Synthesized version data and curated comparative framework\").\n */\nexport const ACTIVITY_LABEL_PROMPT = `Write a short label describing what this block of agent activity accomplished. It appears as the header of a collapsed activity group in a chat UI.\n\nRules:\n- 5 to 9 words, past-tense verb first\n- Name the most distinctive subject (file, API, topic); drop articles and filler\n- Describe outcomes, not mechanics; if something failed, say so plainly\n- Output only the label — no quotes, no punctuation at the end, no preamble\n\nExamples:\n- Searched Node.js release notes and changelogs\n- Compared runtime versions across official sources\n- Fixed failing auth middleware tests\n- Read project config and dependency manifests\n- Attempted database migration, hit permission errors`;\n\n/** Truncates a serialized value for the label prompt. */\nexport function truncateForLabel(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n return value.slice(0, Math.max(0, maxLength - 1)) + '…';\n}\n\n/**\n * Reduces a committed label to bounded single-line data.\n *\n * Sections in this prompt are delimited by blank lines, so a label carrying\n * embedded newlines could otherwise forge an apparent entries section or\n * `Header:` cue. Unlike every other input here, previous labels re-enter\n * the prompt on EVERY later batch, so one malformed result — plain model\n * noncompliance, or injection surfacing through a tool result — would\n * persistently steer unrelated later labels rather than affecting one. The\n * clip bounds the same way `lastAssistantText` and reasoning excerpts are\n * bounded: oversized headers must not inflate later requests past the fast\n * model's window and starve the run of labels entirely.\n */\nfunction sanitizePreviousLabel(label: string): string {\n return truncateForLabel(\n label.replace(/\\s+/g, ' ').trim(),\n PREVIOUS_LABEL_LIMIT\n );\n}\n\nconst ABORT_SERIALIZATION = Symbol('abort-label-serialization');\n\n/**\n * Serializes a tool value for the prompt WITHOUT materializing huge JSON:\n * the output is clipped to a few hundred characters anyway, so a multi-\n * megabyte tool result must not be stringified in full on the label path.\n * Strings clip immediately; structured values serialize under a character\n * budget and degrade to a shape summary once it is exhausted.\n */\nfunction serializeForLabel(value: unknown, limit: number): string {\n if (value == null) {\n return '';\n }\n if (typeof value === 'string') {\n return value.length > limit ? value.slice(0, limit + 1) : value;\n }\n let budget = limit * 4;\n try {\n return (\n JSON.stringify(value, (_key, nested: unknown) => {\n if (budget <= 0) {\n throw ABORT_SERIALIZATION;\n }\n if (typeof nested === 'string') {\n const clipped =\n nested.length > limit ? nested.slice(0, limit) : nested;\n budget -= clipped.length;\n return clipped;\n }\n budget -= 8;\n return nested;\n }) ?? ''\n );\n } catch (error) {\n if (error === ABORT_SERIALIZATION) {\n return Array.isArray(value) ? `[Array(${value.length})]` : '[Object]';\n }\n return String(value);\n }\n}\n\nconst INPUT_CONTEXT_LIMIT = 200;\nconst MAX_THINKING_EXCERPTS = 4;\nconst MAX_PREVIOUS_LABELS = 3;\n/** Per-label bound. A header is 5-9 words; anything past this is\n * noncompliance or payload, and previous labels are the one input that\n * RE-ENTERS the prompt on every later batch of the run. */\nconst PREVIOUS_LABEL_LIMIT = 200;\n/** A label is 5-9 words; no batch needs more than this many entries to\n * produce one, and the cap keeps a 200-call programmatic batch from\n * building an enormous prompt out of per-field-bounded pieces. */\nconst MAX_PROMPT_ENTRIES = 12;\n\nexport type BuildActivityLabelPromptParams = {\n entries: ActivityLabelToolEntry[];\n charLimit: number;\n thinkingExcerpts?: string[];\n lastAssistantText?: string;\n /**\n * Headers already committed for earlier batches in this run, in run order\n * with the most recent last. Rendered ahead of the block context so the\n * label continues the run's story instead of restating a line the user is\n * already reading. Capped at {@link MAX_PREVIOUS_LABELS}.\n */\n previousLabels?: string[];\n /**\n * Resolved tool-output tracing policy. The label prompt becomes Langfuse\n * generation input, so outputs/errors excluded from tracing (global\n * disable or `redactedToolNames`) must never appear in it — the same\n * redaction the span processor applies to structured tool observations.\n */\n redaction?: ResolvedLangfuseToolOutputTracingConfig;\n};\n\n/**\n * Builds the user prompt for a fast-model activity label. Pure — exported\n * for direct testing of redaction and truncation behavior.\n */\nexport function buildActivityLabelPrompt({\n entries,\n charLimit,\n thinkingExcerpts,\n lastAssistantText,\n previousLabels,\n redaction,\n}: BuildActivityLabelPromptParams): string {\n const clip = truncateForLabel;\n /** Reasoning and intent text can quote tool output verbatim — including\n * output from EARLIER calls to a redacted tool that this batch does not\n * contain — so any active policy (global disable or a configured\n * redacted-name list) drops both wholesale. There is no reliable way to\n * scrub a quoted fragment out of free-form model prose. */\n const excerptsRedacted =\n redaction != null &&\n (redaction.enabled === false || redaction.redactedToolNames.size > 0);\n const sections: string[] = [];\n /** Previous labels are free-form model prose too, and per-agent overlays\n * mean an earlier header may have been generated under ANOTHER agent's\n * weaker policy — so they share the excerpts' wholesale drop rather than\n * letting a handoff leak a looser agent's phrasing into this trace. */\n if (\n !excerptsRedacted &&\n previousLabels != null &&\n previousLabels.length > 0\n ) {\n const recent = previousLabels\n .slice(-MAX_PREVIOUS_LABELS)\n .map(sanitizePreviousLabel)\n /** A label that sanitizes to nothing carries no story to continue;\n * rendering it would leave a bare bullet implying a missing header. */\n .filter((label) => label.length > 0);\n if (recent.length > 0) {\n sections.push(\n 'Previous headers in this run (most recent last):\\n' +\n recent.map((label) => `- ${label}`).join('\\n')\n );\n }\n }\n /** Intent text is free-form assistant prose that can quote a redacted\n * tool result just as reasoning can, so it shares the excerpts' fate. */\n if (\n !excerptsRedacted &&\n lastAssistantText != null &&\n lastAssistantText.length > 0\n ) {\n sections.push(\n `Intent (assistant's last message): ${clip(lastAssistantText, INPUT_CONTEXT_LIMIT)}`\n );\n }\n if (\n !excerptsRedacted &&\n thinkingExcerpts != null &&\n thinkingExcerpts.length > 0\n ) {\n sections.push(\n 'Reasoning excerpts:\\n' +\n thinkingExcerpts\n .slice(0, MAX_THINKING_EXCERPTS)\n .map((excerpt) => `- ${clip(excerpt, charLimit)}`)\n .join('\\n')\n );\n }\n if (entries.length > 0) {\n const shown = entries.slice(0, MAX_PROMPT_ENTRIES);\n const omitted = entries.length - shown.length;\n sections.push(\n /** Frames the list as reference material, not the thing to\n * transcribe. Ported from LibreChat's fallback builder (its\n * runtime.ts documents that without this the model \"hands back a\n * transcription\" of the list) after the eval harness measured it\n * across three independent sweeps: fewer template-redundancy and\n * length violations than a bare `Tool calls:` heading, with no\n * per-case regressions (agents #360). */\n 'What it called, and what came back (do not restate these):\\n' +\n shown\n .map((entry) => {\n const input = clip(\n serializeForLabel(entry.toolInput, charLimit),\n charLimit\n );\n const redacted =\n redaction != null && shouldRedactTool(entry.toolName, redaction);\n let outcome: string;\n if (redacted) {\n outcome = redaction.redactionText;\n } else if (entry.status === 'error') {\n outcome = `ERROR: ${clip(entry.error ?? 'unknown error', charLimit)}`;\n } else {\n outcome = clip(\n serializeForLabel(entry.toolOutput, charLimit),\n charLimit\n );\n }\n return `- ${entry.toolName}(${input}) → ${outcome}`;\n })\n .join('\\n') +\n (omitted > 0\n ? `\\n- …and ${omitted} more tool ${omitted === 1 ? 'call' : 'calls'}`\n : '')\n );\n }\n /** The fallback builder's terminal cue, measured alongside the heading\n * (same sweeps). The default system prompt already describes the\n * output as \"the header of a collapsed activity group\". */\n sections.push('Header:');\n return sections.join('\\n\\n');\n}\n"],"mappings":"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"}
|
package/dist/cjs/run.cjs
CHANGED
|
@@ -2,15 +2,15 @@ const require_langfuseRuntimeContext = require("./langfuseRuntimeContext.cjs");
|
|
|
2
2
|
const require_langfuseConfig = require("./langfuseConfig.cjs");
|
|
3
3
|
const require_langfuseRuntimeScope = require("./langfuseRuntimeScope.cjs");
|
|
4
4
|
const require_langfuse = require("./langfuse.cjs");
|
|
5
|
-
const require_activityLabel = require("./prompts/activityLabel.cjs");
|
|
6
|
-
const require_callbacks = require("./utils/callbacks.cjs");
|
|
7
5
|
require("./common/constants.cjs");
|
|
8
6
|
require("./common/enum.cjs");
|
|
9
7
|
require("./common/index.cjs");
|
|
8
|
+
const require_activityLabel = require("./prompts/activityLabel.cjs");
|
|
9
|
+
const require_callbacks = require("./utils/callbacks.cjs");
|
|
10
10
|
const require_title = require("./utils/title.cjs");
|
|
11
11
|
const require_tokens = require("./utils/tokens.cjs");
|
|
12
|
-
const require_preempt = require("./llm/preempt.cjs");
|
|
13
12
|
const require_instrumentation = require("./instrumentation.cjs");
|
|
13
|
+
const require_preempt = require("./llm/preempt.cjs");
|
|
14
14
|
const require_llm = require("./utils/llm.cjs");
|
|
15
15
|
const require_events = require("./events.cjs");
|
|
16
16
|
const require_init = require("./llm/init.cjs");
|
|
@@ -18,6 +18,7 @@ const require_executeHooks = require("./hooks/executeHooks.cjs");
|
|
|
18
18
|
require("./hooks/index.cjs");
|
|
19
19
|
const require_Graph = require("./graphs/Graph.cjs");
|
|
20
20
|
const require_MultiAgentGraph = require("./graphs/MultiAgentGraph.cjs");
|
|
21
|
+
let nanoid = require("nanoid");
|
|
21
22
|
let _langchain_core_prompts = require("@langchain/core/prompts");
|
|
22
23
|
let _langchain_core_runnables = require("@langchain/core/runnables");
|
|
23
24
|
let _langchain_openai = require("@langchain/openai");
|
|
@@ -376,6 +377,16 @@ var Run = class Run {
|
|
|
376
377
|
return this.Graph.getRunMessages();
|
|
377
378
|
}
|
|
378
379
|
/**
|
|
380
|
+
* Returns a defensive snapshot of tools discovered by the current run.
|
|
381
|
+
* Pass an agent id for that context, or omit it for the ordered union across
|
|
382
|
+
* contexts. Interrupted state is available immediately for host persistence;
|
|
383
|
+
* completed runs retain their final snapshot through graph cleanup.
|
|
384
|
+
*/
|
|
385
|
+
getDiscoveredTools(agentId) {
|
|
386
|
+
if (!this.Graph) throw new Error("Graph not initialized. Make sure to use Run.create() to instantiate the Run.");
|
|
387
|
+
return this.Graph.getDiscoveredTools(agentId);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
379
390
|
* Returns the current calibration ratio (EMA of provider-vs-estimate token ratios).
|
|
380
391
|
* Hosts should persist this value and pass it back as `RunConfig.calibrationRatio`
|
|
381
392
|
* on the next run for the same conversation so the pruner starts with an accurate
|
|
@@ -540,13 +551,22 @@ var Run = class Run {
|
|
|
540
551
|
const traceName = config.runName ?? require_langfuse.getLangfuseTraceName(traceMetadata);
|
|
541
552
|
const streamLangfuseConfig = this.getStreamLangfuseConfig(graph);
|
|
542
553
|
require_instrumentation.initializeLangfuseTracing(streamLangfuseConfig);
|
|
554
|
+
const streamRuntimeScope = require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
|
|
555
|
+
runLangfuse: streamLangfuseConfig,
|
|
556
|
+
langfuseOverlay: this.getStreamToolOutputTracingLangfuseConfig(graph),
|
|
557
|
+
traceIdSeed: streamLangfuseConfig?.deterministicTraceId === true ? this.id : void 0,
|
|
558
|
+
runId: graph.langfuseScopeRunId
|
|
559
|
+
});
|
|
543
560
|
const langfuseHandler = require_langfuse.createLangfuseHandler({
|
|
544
561
|
langfuse: streamLangfuseConfig,
|
|
545
562
|
userId,
|
|
546
563
|
sessionId,
|
|
547
564
|
traceMetadata,
|
|
548
565
|
tags: ["librechat", "agent"],
|
|
549
|
-
traceIdSeed: streamLangfuseConfig?.deterministicTraceId === true ? this.id : void 0
|
|
566
|
+
traceIdSeed: streamLangfuseConfig?.deterministicTraceId === true ? this.id : void 0,
|
|
567
|
+
runId: graph.langfuseScopeRunId,
|
|
568
|
+
toolOutputTracing: streamRuntimeScope.toolOutputTracing,
|
|
569
|
+
traceName
|
|
550
570
|
});
|
|
551
571
|
if (langfuseHandler != null) {
|
|
552
572
|
config.runName = traceName;
|
|
@@ -710,11 +730,7 @@ var Run = class Run {
|
|
|
710
730
|
else if (this._haltedReason == null && graph.preemptIncomplete) this._haltedReason = "preempt_incomplete";
|
|
711
731
|
};
|
|
712
732
|
try {
|
|
713
|
-
await require_langfuseRuntimeScope.withLangfuseRuntimeScope(
|
|
714
|
-
runLangfuse: streamLangfuseConfig,
|
|
715
|
-
langfuseOverlay: this.getStreamToolOutputTracingLangfuseConfig(graph),
|
|
716
|
-
traceIdSeed: streamLangfuseConfig?.deterministicTraceId === true ? this.id : void 0
|
|
717
|
-
}), () => require_langfuse.withLangfuseAttributes({
|
|
733
|
+
await require_langfuseRuntimeScope.withLangfuseRuntimeScope(streamRuntimeScope, () => require_langfuse.withLangfuseAttributes({
|
|
718
734
|
langfuse: streamLangfuseConfig,
|
|
719
735
|
userId,
|
|
720
736
|
sessionId,
|
|
@@ -967,19 +983,35 @@ var Run = class Run {
|
|
|
967
983
|
}
|
|
968
984
|
async generateTitle({ provider, inputText, contentParts, titlePrompt, clientOptions, chainOptions, skipLanguage, titleMethod = "completion", titlePromptTemplate }) {
|
|
969
985
|
let titleLangfuseHandler;
|
|
970
|
-
let titleLangfuseConfig;
|
|
971
986
|
let titleUserId;
|
|
972
987
|
let titleSessionId;
|
|
973
988
|
const titleContext = this.Graph == null ? void 0 : this.Graph.agentContexts.get(this.Graph.defaultAgentId);
|
|
989
|
+
const titleLangfuseConfig = require_langfuseConfig.resolveLangfuseConfig(this.langfuse, titleContext?.langfuse);
|
|
974
990
|
const traceMetadata = require_langfuse.createLangfuseTraceMetadata({
|
|
975
991
|
messageId: "title-" + this.id,
|
|
976
992
|
agentName: titleContext?.name
|
|
977
993
|
});
|
|
978
994
|
const titleRunName = require_langfuse.getLangfuseTraceName(traceMetadata, "LibreChat Title");
|
|
995
|
+
/** Scope identity carries an opaque per-execution component: public run
|
|
996
|
+
* ids are unrestricted, so a purely derived id (`title-<runId>`) could
|
|
997
|
+
* collide with an ordinary concurrent run literally named that way and
|
|
998
|
+
* defeat foreign-scope rejection. */
|
|
999
|
+
const titleScopeRunId = `title:${this.id}:${(0, nanoid.nanoid)()}`;
|
|
1000
|
+
/** Seed policy mirrors `generateActivityLabel`:
|
|
1001
|
+
* `runWithLangfuseRuntimeContext` SPREADS the surrounding context, so an
|
|
1002
|
+
* absent seed INHERITS an active parent run's and collapses the title
|
|
1003
|
+
* into that run's trace. Seeded when determinism is opted into OR a
|
|
1004
|
+
* parent seed is live; otherwise unseeded, matching the other paths. */
|
|
1005
|
+
const inheritedTraceSeed = require_langfuseRuntimeContext.getTraceIdSeed();
|
|
1006
|
+
const titleRuntimeScope = require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
|
|
1007
|
+
runLangfuse: this.langfuse,
|
|
1008
|
+
langfuseOverlay: titleContext?.langfuse,
|
|
1009
|
+
traceIdSeed: titleLangfuseConfig?.deterministicTraceId === true || inheritedTraceSeed != null ? "title-" + this.id : void 0,
|
|
1010
|
+
runId: titleScopeRunId
|
|
1011
|
+
});
|
|
979
1012
|
if (chainOptions != null) {
|
|
980
1013
|
titleUserId = typeof chainOptions.configurable?.user_id === "string" ? chainOptions.configurable.user_id : void 0;
|
|
981
1014
|
titleSessionId = typeof chainOptions.configurable?.thread_id === "string" ? chainOptions.configurable.thread_id : void 0;
|
|
982
|
-
titleLangfuseConfig = require_langfuseConfig.resolveLangfuseConfig(this.langfuse, titleContext?.langfuse);
|
|
983
1015
|
require_instrumentation.initializeLangfuseTracing(titleLangfuseConfig);
|
|
984
1016
|
titleLangfuseHandler = require_langfuse.createLangfuseHandler({
|
|
985
1017
|
langfuse: titleLangfuseConfig,
|
|
@@ -987,7 +1019,10 @@ var Run = class Run {
|
|
|
987
1019
|
sessionId: titleSessionId,
|
|
988
1020
|
traceMetadata,
|
|
989
1021
|
tags: ["librechat", "title"],
|
|
990
|
-
traceIdSeed: titleLangfuseConfig?.deterministicTraceId === true ? "title-" + this.id : void 0
|
|
1022
|
+
traceIdSeed: titleLangfuseConfig?.deterministicTraceId === true ? "title-" + this.id : void 0,
|
|
1023
|
+
runId: titleScopeRunId,
|
|
1024
|
+
toolOutputTracing: titleRuntimeScope.toolOutputTracing,
|
|
1025
|
+
traceName: chainOptions.runName ?? titleRunName
|
|
991
1026
|
});
|
|
992
1027
|
if (titleLangfuseHandler != null) chainOptions.callbacks = require_callbacks.appendCallbacks(chainOptions.callbacks, [titleLangfuseHandler]);
|
|
993
1028
|
}
|
|
@@ -1033,18 +1068,12 @@ var Run = class Run {
|
|
|
1033
1068
|
}, runtimeConfig));
|
|
1034
1069
|
try {
|
|
1035
1070
|
try {
|
|
1036
|
-
return await require_langfuseRuntimeScope.withLangfuseRuntimeScope(
|
|
1037
|
-
runLangfuse: this.langfuse,
|
|
1038
|
-
langfuseOverlay: titleContext?.langfuse
|
|
1039
|
-
}), () => invokeTitleChain(invokeConfig));
|
|
1071
|
+
return await require_langfuseRuntimeScope.withLangfuseRuntimeScope(titleRuntimeScope, () => invokeTitleChain(invokeConfig));
|
|
1040
1072
|
} catch (_e) {
|
|
1041
1073
|
const langfuseHandler = require_callbacks.findCallback(invokeConfig.callbacks, require_langfuse.isLangfuseCallbackHandler);
|
|
1042
1074
|
const { callbacks: _cb, ...rest } = invokeConfig;
|
|
1043
1075
|
const safeConfig = Object.assign({}, rest, { callbacks: langfuseHandler ? [langfuseHandler] : [] });
|
|
1044
|
-
return await require_langfuseRuntimeScope.withLangfuseRuntimeScope(
|
|
1045
|
-
runLangfuse: this.langfuse,
|
|
1046
|
-
langfuseOverlay: titleContext?.langfuse
|
|
1047
|
-
}), () => invokeTitleChain(safeConfig));
|
|
1076
|
+
return await require_langfuseRuntimeScope.withLangfuseRuntimeScope(titleRuntimeScope, () => invokeTitleChain(safeConfig));
|
|
1048
1077
|
}
|
|
1049
1078
|
} finally {
|
|
1050
1079
|
await require_langfuse.disposeLangfuseHandler(titleLangfuseHandler);
|
|
@@ -1096,10 +1125,13 @@ var Run = class Run {
|
|
|
1096
1125
|
* otherwise unseeded, matching the other generation paths. */
|
|
1097
1126
|
const inheritedTraceSeed = require_langfuseRuntimeContext.getTraceIdSeed();
|
|
1098
1127
|
const labelTraceSeed = labelLangfuseConfig?.deterministicTraceId === true || inheritedTraceSeed != null ? traceSeed ?? `activity-label-${this.id}-${labelSeq}` : void 0;
|
|
1128
|
+
/** Opaque per-execution component: see `titleScopeRunId`. */
|
|
1129
|
+
const labelScopeRunId = `activity-label:${this.id}:${labelSeq}:${(0, nanoid.nanoid)()}`;
|
|
1099
1130
|
const labelRuntimeScope = require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
|
|
1100
1131
|
runLangfuse: this.langfuse,
|
|
1101
1132
|
langfuseOverlay: labelContext?.langfuse,
|
|
1102
|
-
traceIdSeed: labelTraceSeed
|
|
1133
|
+
traceIdSeed: labelTraceSeed,
|
|
1134
|
+
runId: labelScopeRunId
|
|
1103
1135
|
});
|
|
1104
1136
|
/** Handler only when a session id resolved from
|
|
1105
1137
|
* `chainOptions.configurable.thread_id`: without it the label call has
|
|
@@ -1115,7 +1147,10 @@ var Run = class Run {
|
|
|
1115
1147
|
sessionId: labelSessionId,
|
|
1116
1148
|
traceMetadata,
|
|
1117
1149
|
tags: ["librechat", "activity-label"],
|
|
1118
|
-
traceIdSeed: labelLangfuseConfig?.deterministicTraceId === true ? labelTraceSeed : void 0
|
|
1150
|
+
traceIdSeed: labelLangfuseConfig?.deterministicTraceId === true ? labelTraceSeed : void 0,
|
|
1151
|
+
runId: labelScopeRunId,
|
|
1152
|
+
toolOutputTracing: labelRuntimeScope.toolOutputTracing,
|
|
1153
|
+
traceName: labelChainOptions.runName ?? labelRunName
|
|
1119
1154
|
});
|
|
1120
1155
|
if (labelLangfuseHandler != null) labelChainOptions.callbacks = require_callbacks.appendCallbacks(labelChainOptions.callbacks, [labelLangfuseHandler]);
|
|
1121
1156
|
/** The label prompt becomes Langfuse generation input, so the resolved
|