@librechat/agents 3.6.7 → 3.6.9
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/common/constants.cjs +14 -0
- package/dist/cjs/common/constants.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +18 -6
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +6 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +2 -0
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +46 -15
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseOperation.cjs +6 -0
- package/dist/cjs/langfuseOperation.cjs.map +1 -0
- package/dist/cjs/langfuseRuntimeContext.cjs +3 -1
- package/dist/cjs/langfuseRuntimeContext.cjs.map +1 -1
- package/dist/cjs/langfuseRuntimeScope.cjs +11 -1
- package/dist/cjs/langfuseRuntimeScope.cjs.map +1 -1
- package/dist/cjs/langfuseSpanRegistry.cjs +35 -0
- package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +1 -6
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/langfuseTraceShaping.cjs +58 -10
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +8 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +8 -1
- package/dist/cjs/run.cjs +59 -25
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +1 -1
- package/dist/cjs/tools/ToolNode.cjs +2 -2
- package/dist/cjs/tools/search/search.cjs +19 -9
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +2 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +3 -3
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/esm/common/constants.mjs +8 -1
- package/dist/esm/common/constants.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +18 -6
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +6 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +4 -2
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +47 -16
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseOperation.mjs +6 -0
- package/dist/esm/langfuseOperation.mjs.map +1 -0
- package/dist/esm/langfuseRuntimeContext.mjs +3 -2
- package/dist/esm/langfuseRuntimeContext.mjs.map +1 -1
- package/dist/esm/langfuseRuntimeScope.mjs +12 -3
- package/dist/esm/langfuseRuntimeScope.mjs.map +1 -1
- package/dist/esm/langfuseSpanRegistry.mjs +34 -1
- package/dist/esm/langfuseSpanRegistry.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +1 -6
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/langfuseTraceShaping.mjs +58 -10
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +8 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -3
- package/dist/esm/run.mjs +59 -25
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +1 -1
- package/dist/esm/tools/ToolNode.mjs +2 -2
- package/dist/esm/tools/search/search.mjs +19 -9
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +2 -1
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +3 -3
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/types/common/constants.d.ts +8 -0
- package/dist/types/graphs/Graph.d.ts +8 -3
- package/dist/types/graphs/MultiAgentGraph.d.ts +1 -0
- package/dist/types/langfuse.d.ts +10 -1
- package/dist/types/langfuseOperation.d.ts +2 -0
- package/dist/types/langfuseRuntimeContext.d.ts +2 -0
- package/dist/types/langfuseRuntimeScope.d.ts +3 -1
- package/dist/types/langfuseSpanRegistry.d.ts +11 -1
- package/dist/types/langfuseTraceShaping.d.ts +8 -6
- package/dist/types/run.d.ts +4 -3
- package/dist/types/tools/search/types.d.ts +15 -0
- package/dist/types/types/activityLabel.d.ts +4 -3
- package/dist/types/types/reasoningLabel.d.ts +1 -1
- package/package.json +5 -5
- package/src/common/constants.ts +9 -0
- package/src/graphs/Graph.ts +125 -103
- package/src/graphs/MultiAgentGraph.ts +6 -2
- package/src/instrumentation.ts +12 -0
- package/src/langfuse.ts +83 -17
- package/src/langfuseOperation.ts +2 -0
- package/src/langfuseRuntimeContext.ts +6 -0
- package/src/langfuseRuntimeScope.ts +31 -1
- package/src/langfuseSpanRegistry.ts +78 -2
- package/src/langfuseToolOutputTracing.ts +0 -2
- package/src/langfuseTraceShaping.ts +120 -26
- package/src/llm/openai/index.ts +34 -3
- package/src/run.ts +118 -49
- package/src/tools/search/search.ts +25 -8
- package/src/tools/search/tool.ts +2 -0
- package/src/tools/search/types.ts +16 -0
- package/src/tools/subagent/SubagentExecutor.ts +8 -4
- package/src/types/activityLabel.ts +4 -3
- package/src/types/reasoningLabel.ts +1 -1
package/src/graphs/Graph.ts
CHANGED
|
@@ -74,6 +74,17 @@ import {
|
|
|
74
74
|
appendPredecessorHandoffCue,
|
|
75
75
|
removePredecessorHandoffCue,
|
|
76
76
|
} from '@/messages';
|
|
77
|
+
import {
|
|
78
|
+
Constants,
|
|
79
|
+
GraphNodeKeys,
|
|
80
|
+
ContentTypes,
|
|
81
|
+
GraphEvents,
|
|
82
|
+
Providers,
|
|
83
|
+
StepTypes,
|
|
84
|
+
STANDARD_GRAPH_RUN_NAME,
|
|
85
|
+
AGENT_MODEL_CALL_RUN_NAME,
|
|
86
|
+
PREEMPT_BOUNDARY_HOOK_TIMEOUT_MS,
|
|
87
|
+
} from '@/common';
|
|
77
88
|
import {
|
|
78
89
|
resetIfNotEmpty,
|
|
79
90
|
isAnthropicLike,
|
|
@@ -106,15 +117,6 @@ import {
|
|
|
106
117
|
isGraphSubagentConfig,
|
|
107
118
|
normalizeSubagentConfigEntries,
|
|
108
119
|
} from '@/tools/subagent';
|
|
109
|
-
import {
|
|
110
|
-
Constants,
|
|
111
|
-
GraphNodeKeys,
|
|
112
|
-
ContentTypes,
|
|
113
|
-
GraphEvents,
|
|
114
|
-
Providers,
|
|
115
|
-
StepTypes,
|
|
116
|
-
PREEMPT_BOUNDARY_HOOK_TIMEOUT_MS,
|
|
117
|
-
} from '@/common';
|
|
118
120
|
import {
|
|
119
121
|
createLangfuseHandler,
|
|
120
122
|
createLangfuseTraceMetadata,
|
|
@@ -1260,6 +1262,7 @@ export abstract class Graph<
|
|
|
1260
1262
|
}
|
|
1261
1263
|
|
|
1262
1264
|
export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
1265
|
+
readonly runName: string = STANDARD_GRAPH_RUN_NAME;
|
|
1263
1266
|
overrideModel?: t.ChatModel;
|
|
1264
1267
|
private subagentModelOverride?: t.ChatModel;
|
|
1265
1268
|
private readonly graphFactory: GraphFactory;
|
|
@@ -1286,10 +1289,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1286
1289
|
* component: public run ids are unrestricted and may repeat across
|
|
1287
1290
|
* concurrently executing runs (retries, duplicate submissions,
|
|
1288
1291
|
* tenant-local message ids), and equal stamps would let those runs adopt
|
|
1289
|
-
* each other's scopes.
|
|
1290
|
-
*
|
|
1292
|
+
* each other's scopes. Fresh stream executions rotate this stamp; resumes
|
|
1293
|
+
* retain it so the continuation stays in the interrupted execution.
|
|
1291
1294
|
*/
|
|
1292
|
-
|
|
1295
|
+
langfuseScopeRunId: string;
|
|
1296
|
+
/** Opaque key for parenting post-processing observations to this run. */
|
|
1297
|
+
langfuseTraceAnchor: object = {};
|
|
1293
1298
|
/**
|
|
1294
1299
|
* Boundary between historical messages (loaded from conversation state)
|
|
1295
1300
|
* and messages produced during the current run. Set once in the state
|
|
@@ -1498,6 +1503,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1498
1503
|
this.defaultAgentId = agents[0].agentId;
|
|
1499
1504
|
}
|
|
1500
1505
|
|
|
1506
|
+
/** Rotates the Langfuse identities that must never cross fresh executions. */
|
|
1507
|
+
startFreshLangfuseExecution(): void {
|
|
1508
|
+
this.langfuseTraceAnchor = {};
|
|
1509
|
+
this.langfuseScopeRunId = `${this.runId ?? 'graph'}:${nanoid()}`;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1501
1512
|
/* Init */
|
|
1502
1513
|
|
|
1503
1514
|
resetValues(keepContent?: boolean, checkpointScope?: string): void {
|
|
@@ -2913,7 +2924,9 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
2913
2924
|
});
|
|
2914
2925
|
|
|
2915
2926
|
if (agentContext.systemRunnable) {
|
|
2916
|
-
model = agentContext.systemRunnable
|
|
2927
|
+
model = agentContext.systemRunnable
|
|
2928
|
+
.pipe(model as Runnable)
|
|
2929
|
+
.withConfig({ runName: AGENT_MODEL_CALL_RUN_NAME });
|
|
2917
2930
|
}
|
|
2918
2931
|
|
|
2919
2932
|
if (agentContext.tokenCalculationPromise) {
|
|
@@ -4010,6 +4023,8 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4010
4023
|
tags: ['librechat', 'agent'],
|
|
4011
4024
|
traceIdSeed:
|
|
4012
4025
|
langfuse?.deterministicTraceId === true ? this.runId : undefined,
|
|
4026
|
+
traceAnchor: this.langfuseTraceAnchor,
|
|
4027
|
+
agentId,
|
|
4013
4028
|
runId: this.langfuseScopeRunId,
|
|
4014
4029
|
toolOutputTracing: hasToolOutputTracingConfig(
|
|
4015
4030
|
this.langfuse,
|
|
@@ -4052,6 +4067,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4052
4067
|
resolveLangfuseRuntimeScope({
|
|
4053
4068
|
runLangfuse: this.langfuse,
|
|
4054
4069
|
langfuseOverlay: agentContext.langfuse,
|
|
4070
|
+
traceAnchor: this.langfuseTraceAnchor,
|
|
4055
4071
|
runId: this.langfuseScopeRunId,
|
|
4056
4072
|
agentId,
|
|
4057
4073
|
}),
|
|
@@ -4845,94 +4861,98 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4845
4861
|
createChildGraphByKind: createConfiguredChildGraph,
|
|
4846
4862
|
createDetachedChildGraphFactory: (
|
|
4847
4863
|
parentHandlerRegistry
|
|
4848
|
-
): GraphFactory =>
|
|
4849
|
-
snapshotChildGraphFactory(parentHandlerRegistry),
|
|
4864
|
+
): GraphFactory => snapshotChildGraphFactory(parentHandlerRegistry),
|
|
4850
4865
|
});
|
|
4851
4866
|
this.registerSubagentExecutor(executor);
|
|
4852
4867
|
|
|
4853
|
-
const subagentTool = tool(
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
parentToolCallId
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4868
|
+
const subagentTool = tool(
|
|
4869
|
+
async (rawInput, config) => {
|
|
4870
|
+
const input = rawInput as {
|
|
4871
|
+
description?: string;
|
|
4872
|
+
subagent_type?: string;
|
|
4873
|
+
subagent_thread_id?: string;
|
|
4874
|
+
run_in_background?: boolean;
|
|
4875
|
+
};
|
|
4876
|
+
const description =
|
|
4877
|
+
typeof input.description === 'string' &&
|
|
4878
|
+
input.description.trim().length > 0
|
|
4879
|
+
? input.description
|
|
4880
|
+
: DEFAULT_SUBAGENT_DESCRIPTION;
|
|
4881
|
+
const subagentType =
|
|
4882
|
+
typeof input.subagent_type === 'string'
|
|
4883
|
+
? input.subagent_type
|
|
4884
|
+
: '';
|
|
4885
|
+
const subagentThreadId =
|
|
4886
|
+
typeof input.subagent_thread_id === 'string' &&
|
|
4887
|
+
input.subagent_thread_id.trim() !== ''
|
|
4888
|
+
? input.subagent_thread_id.trim()
|
|
4889
|
+
: undefined;
|
|
4890
|
+
const threadId = config.configurable?.thread_id as
|
|
4891
|
+
| string
|
|
4892
|
+
| undefined;
|
|
4893
|
+
/** Surface the parent call id so child checkpoints, interrupts, and
|
|
4894
|
+
* update events remain correlated across replay and resume. */
|
|
4895
|
+
const toolRuntime = config as {
|
|
4896
|
+
toolCallId?: string;
|
|
4897
|
+
toolCall?: { id?: string };
|
|
4898
|
+
};
|
|
4899
|
+
const toolCall = toolRuntime.toolCall;
|
|
4900
|
+
let parentToolCallId: string | undefined;
|
|
4901
|
+
if (
|
|
4902
|
+
typeof toolRuntime.toolCallId === 'string' &&
|
|
4903
|
+
toolRuntime.toolCallId !== ''
|
|
4904
|
+
) {
|
|
4905
|
+
parentToolCallId = toolRuntime.toolCallId;
|
|
4906
|
+
} else if (typeof toolCall?.id === 'string' && toolCall.id !== '') {
|
|
4907
|
+
parentToolCallId = toolCall.id;
|
|
4908
|
+
}
|
|
4909
|
+
/** The parent tool batch's entry-captured scope (stamped by
|
|
4910
|
+
* ToolNode before PreToolUse hooks) — binds this child to the
|
|
4911
|
+
* run that dispatched it, not to whatever controller a reset
|
|
4912
|
+
* installed while the hooks were awaited. */
|
|
4913
|
+
const batchScope = config.configurable?.[
|
|
4914
|
+
RUN_BREAKER_SCOPE_CONFIG_KEY
|
|
4915
|
+
] as RunBreakerScope | undefined;
|
|
4916
|
+
const executeParams = {
|
|
4917
|
+
description,
|
|
4918
|
+
subagentType,
|
|
4919
|
+
threadId,
|
|
4920
|
+
signal: config.signal,
|
|
4921
|
+
parentToolCallId,
|
|
4922
|
+
breaker: batchScope?.controller,
|
|
4923
|
+
/**
|
|
4924
|
+
* Forward the parent's `configurable` so host-set fields
|
|
4925
|
+
* (`requestBody`, `user`, etc.) propagate into the child
|
|
4926
|
+
* workflow. The executor scrubs run-identity fields before
|
|
4927
|
+
* forwarding — see `SubagentExecuteParams.parentConfigurable`.
|
|
4928
|
+
*/
|
|
4929
|
+
parentConfigurable: config.configurable as
|
|
4930
|
+
| Record<string, unknown>
|
|
4931
|
+
| undefined,
|
|
4932
|
+
};
|
|
4933
|
+
if (input.run_in_background === true) {
|
|
4934
|
+
return executor.executeInBackground({
|
|
4935
|
+
...executeParams,
|
|
4936
|
+
...(subagentThreadId == null ? {} : { subagentThreadId }),
|
|
4937
|
+
});
|
|
4938
|
+
}
|
|
4939
|
+
if (subagentThreadId != null) {
|
|
4940
|
+
return JSON.stringify({
|
|
4941
|
+
status: 'rejected',
|
|
4942
|
+
tool: Constants.SUBAGENT,
|
|
4943
|
+
message:
|
|
4944
|
+
'Child-thread continuation requires run_in_background.',
|
|
4945
|
+
});
|
|
4946
|
+
}
|
|
4947
|
+
const result = await executor.execute(executeParams);
|
|
4948
|
+
return result.content;
|
|
4949
|
+
},
|
|
4950
|
+
buildSubagentToolParams(executableConfigs, {
|
|
4951
|
+
background: this.subagentTasks != null,
|
|
4952
|
+
threadContinuation:
|
|
4953
|
+
this.subagentTasks?.store.supportsThreadContinuation === true,
|
|
4954
|
+
})
|
|
4955
|
+
);
|
|
4936
4956
|
const replayableSubagentTool = subagentTool as typeof subagentTool &
|
|
4937
4957
|
ReplayableSubagentTool;
|
|
4938
4958
|
replayableSubagentTool[SUBAGENT_REPLAY_CONTROLLER] = {
|
|
@@ -5218,7 +5238,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
5218
5238
|
.addEdge(summarizeNode, agentNode)
|
|
5219
5239
|
.addEdge(toolNode, agentContext.toolEnd ? END : agentNode);
|
|
5220
5240
|
|
|
5221
|
-
return workflow.compile();
|
|
5241
|
+
return workflow.compile({ name: STANDARD_GRAPH_RUN_NAME });
|
|
5222
5242
|
}
|
|
5223
5243
|
|
|
5224
5244
|
createWorkflow(): t.CompiledStateWorkflow {
|
|
@@ -5247,11 +5267,13 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
5247
5267
|
>,
|
|
5248
5268
|
{ ends: [END] }
|
|
5249
5269
|
)
|
|
5250
|
-
.addEdge(START, this.defaultAgentId)
|
|
5251
|
-
// LangGraph compile() types are overly strict for opt-in options
|
|
5252
|
-
.compile(this.compileOptions as unknown as never);
|
|
5270
|
+
.addEdge(START, this.defaultAgentId);
|
|
5253
5271
|
|
|
5254
|
-
|
|
5272
|
+
// LangGraph compile() types are overly strict for opt-in options
|
|
5273
|
+
return workflow.compile({
|
|
5274
|
+
...this.compileOptions,
|
|
5275
|
+
name: STANDARD_GRAPH_RUN_NAME,
|
|
5276
|
+
} as unknown as never);
|
|
5255
5277
|
}
|
|
5256
5278
|
|
|
5257
5279
|
/**
|
|
@@ -20,9 +20,9 @@ import type { ToolRuntime } from '@langchain/core/tools';
|
|
|
20
20
|
import type { GraphFactoryDependencies } from '@/graphs/graphFactory';
|
|
21
21
|
import type * as t from '@/types';
|
|
22
22
|
import { serializeToolContentBounded } from '@/utils/toolContent';
|
|
23
|
+
import { Constants, MULTI_AGENT_GRAPH_RUN_NAME } from '@/common';
|
|
23
24
|
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
24
25
|
import { StandardGraph } from './Graph';
|
|
25
|
-
import { Constants } from '@/common';
|
|
26
26
|
|
|
27
27
|
/** Pattern to extract instructions from transfer ToolMessage content */
|
|
28
28
|
const HANDOFF_INSTRUCTIONS_PATTERN = /(?:Instructions?|Context):\s*(.+)/is;
|
|
@@ -177,6 +177,7 @@ function withHandoffGroupMetadata(
|
|
|
177
177
|
* OR continues its workflow (direct edges), but not both simultaneously.
|
|
178
178
|
*/
|
|
179
179
|
export class MultiAgentGraph extends StandardGraph {
|
|
180
|
+
override readonly runName = MULTI_AGENT_GRAPH_RUN_NAME;
|
|
180
181
|
private edges: t.GraphEdge[];
|
|
181
182
|
private startingNodes: Set<string> = new Set();
|
|
182
183
|
private directEdges: t.GraphEdge[] = [];
|
|
@@ -1438,6 +1439,9 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1438
1439
|
}
|
|
1439
1440
|
}
|
|
1440
1441
|
|
|
1441
|
-
return builder.compile(
|
|
1442
|
+
return builder.compile({
|
|
1443
|
+
...this.compileOptions,
|
|
1444
|
+
name: MULTI_AGENT_GRAPH_RUN_NAME,
|
|
1445
|
+
} as unknown as never);
|
|
1442
1446
|
}
|
|
1443
1447
|
}
|
package/src/instrumentation.ts
CHANGED
|
@@ -16,9 +16,12 @@ import {
|
|
|
16
16
|
getLangfuseDestinationKey,
|
|
17
17
|
getLangfuseSpanProcessorParams,
|
|
18
18
|
registerLangfuseManagedSpan,
|
|
19
|
+
registerLangfuseTraceAnchorSpan,
|
|
19
20
|
} from '@/langfuseSpanRegistry';
|
|
20
21
|
import {
|
|
21
22
|
resolveLangfuseConfigForSpan,
|
|
23
|
+
resolveLangfuseScopeAgentId,
|
|
24
|
+
resolveLangfuseTraceAnchor,
|
|
22
25
|
resolveTraceIdSeedForSpan,
|
|
23
26
|
} from '@/langfuseRuntimeScope';
|
|
24
27
|
import { createLangfuseSpanProcessor } from '@/langfuseToolOutputTracing';
|
|
@@ -137,6 +140,15 @@ class RoutingLangfuseSpanProcessor implements SpanProcessor {
|
|
|
137
140
|
langfuse
|
|
138
141
|
);
|
|
139
142
|
registerLangfuseManagedSpan(span, destinationKey);
|
|
143
|
+
const traceAnchor = resolveLangfuseTraceAnchor(parentContext);
|
|
144
|
+
if (traceAnchor != null) {
|
|
145
|
+
registerLangfuseTraceAnchorSpan(
|
|
146
|
+
traceAnchor,
|
|
147
|
+
span,
|
|
148
|
+
destinationKey,
|
|
149
|
+
resolveLangfuseScopeAgentId(parentContext)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
140
152
|
|
|
141
153
|
const librechatTraceAttributes = createLibreChatTraceAttributes(
|
|
142
154
|
langfuse?.librechatTraceAttributes ?? {}
|
package/src/langfuse.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
LLMResult,
|
|
19
19
|
} from '@langchain/core/outputs';
|
|
20
20
|
import type { PropagateAttributesParams } from '@langfuse/tracing';
|
|
21
|
-
import type { Context } from '@opentelemetry/api';
|
|
21
|
+
import type { Context, SpanContext } from '@opentelemetry/api';
|
|
22
22
|
import type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';
|
|
23
23
|
import type * as t from '@/types';
|
|
24
24
|
import {
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
} from '@/langfuseConfig';
|
|
37
37
|
import {
|
|
38
38
|
getLangfuseManagedSpanDestination,
|
|
39
|
+
registerLangfuseManagedSpan,
|
|
39
40
|
resolveLangfuseDestinationKey,
|
|
40
41
|
} from '@/langfuseSpanRegistry';
|
|
41
42
|
import { isPresent, parseBooleanEnv } from '@/utils/misc';
|
|
@@ -66,6 +67,14 @@ type LangfuseHandlerParams = {
|
|
|
66
67
|
traceMetadata?: LangfuseTraceMetadata;
|
|
67
68
|
tags?: string[];
|
|
68
69
|
traceIdSeed?: string;
|
|
70
|
+
/** Opaque key used by the span processor to capture this run's parents. */
|
|
71
|
+
traceAnchor?: object;
|
|
72
|
+
/** Agent lane this handler owns when ambient callback scope is unavailable. */
|
|
73
|
+
agentId?: string;
|
|
74
|
+
/** Exported observation that auxiliary work should be nested beneath. */
|
|
75
|
+
parentSpanContext?: SpanContext;
|
|
76
|
+
/** Keep an existing parent trace's user, session, name, and metadata. */
|
|
77
|
+
inheritTraceIdentity?: boolean;
|
|
69
78
|
/** Identity of the run this handler traces; ambient runtime scopes are
|
|
70
79
|
* only adopted when stamped with the same run (see
|
|
71
80
|
* `LangfuseRuntimeContext.runId`). */
|
|
@@ -251,7 +260,7 @@ function callbackNodeMatchesAgent(node: string, agentId: string): boolean {
|
|
|
251
260
|
* Hosts often execute agent code inside their own OpenTelemetry spans (HTTP
|
|
252
261
|
* server auto-instrumentation on the global provider). Root observations must
|
|
253
262
|
* not inherit that ambient identity: the foreign parent is never exported to
|
|
254
|
-
* Langfuse, which orphans the trace root (root
|
|
263
|
+
* Langfuse, which orphans the trace root (root-observation input/output shaping is
|
|
255
264
|
* skipped because the span no longer looks like a root), collapses concurrent
|
|
256
265
|
* runs inside one request context — an agent run and the previous turn's
|
|
257
266
|
* title run — into a single merged trace with racing names and unioned tags,
|
|
@@ -280,6 +289,9 @@ function detachForeignAmbientSpan(
|
|
|
280
289
|
class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
281
290
|
private readonly langfuse?: t.LangfuseConfig;
|
|
282
291
|
private readonly traceIdSeed?: string;
|
|
292
|
+
private readonly traceAnchor?: object;
|
|
293
|
+
private readonly agentId?: string;
|
|
294
|
+
private readonly parentSpanContext?: SpanContext;
|
|
283
295
|
private readonly runId?: string;
|
|
284
296
|
private readonly identity: HandlerIdentity;
|
|
285
297
|
private readonly toolOutputTracing?: ResolvedLangfuseToolOutputTracingConfig;
|
|
@@ -289,22 +301,40 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
289
301
|
const {
|
|
290
302
|
langfuse,
|
|
291
303
|
traceIdSeed,
|
|
304
|
+
traceAnchor,
|
|
305
|
+
agentId,
|
|
306
|
+
parentSpanContext,
|
|
307
|
+
inheritTraceIdentity,
|
|
292
308
|
runId,
|
|
293
309
|
toolOutputTracing,
|
|
294
310
|
traceName,
|
|
295
311
|
...handlerParams
|
|
296
312
|
} = params ?? {};
|
|
297
|
-
super(
|
|
313
|
+
super({
|
|
314
|
+
...handlerParams,
|
|
315
|
+
...(inheritTraceIdentity === true
|
|
316
|
+
? {
|
|
317
|
+
userId: undefined,
|
|
318
|
+
sessionId: undefined,
|
|
319
|
+
traceMetadata: undefined,
|
|
320
|
+
}
|
|
321
|
+
: {}),
|
|
322
|
+
});
|
|
298
323
|
this.langfuse = langfuse;
|
|
299
324
|
this.traceIdSeed = traceIdSeed;
|
|
325
|
+
this.traceAnchor = traceAnchor;
|
|
326
|
+
this.agentId = agentId;
|
|
327
|
+
this.parentSpanContext = parentSpanContext;
|
|
300
328
|
this.runId = runId;
|
|
301
329
|
this.toolOutputTracing = toolOutputTracing;
|
|
302
330
|
this.identity = {
|
|
303
|
-
userId: handlerParams.userId,
|
|
304
|
-
sessionId:
|
|
331
|
+
userId: inheritTraceIdentity === true ? undefined : handlerParams.userId,
|
|
332
|
+
sessionId:
|
|
333
|
+
inheritTraceIdentity === true ? undefined : handlerParams.sessionId,
|
|
305
334
|
tags: handlerParams.tags,
|
|
306
|
-
metadata:
|
|
307
|
-
|
|
335
|
+
metadata:
|
|
336
|
+
inheritTraceIdentity === true ? undefined : handlerParams.traceMetadata,
|
|
337
|
+
traceName: inheritTraceIdentity === true ? undefined : traceName,
|
|
308
338
|
};
|
|
309
339
|
}
|
|
310
340
|
|
|
@@ -314,6 +344,22 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
314
344
|
: undefined;
|
|
315
345
|
}
|
|
316
346
|
|
|
347
|
+
private applyExplicitParent(
|
|
348
|
+
activeContext: Context,
|
|
349
|
+
langfuse?: t.LangfuseConfig
|
|
350
|
+
): Context {
|
|
351
|
+
if (this.parentSpanContext == null) {
|
|
352
|
+
return activeContext;
|
|
353
|
+
}
|
|
354
|
+
const destinationKey = resolveLangfuseDestinationKey(langfuse);
|
|
355
|
+
if (destinationKey == null) {
|
|
356
|
+
return activeContext;
|
|
357
|
+
}
|
|
358
|
+
const parentSpan = otelTrace.wrapSpanContext(this.parentSpanContext);
|
|
359
|
+
registerLangfuseManagedSpan(parentSpan, destinationKey);
|
|
360
|
+
return otelTrace.setSpan(activeContext, parentSpan);
|
|
361
|
+
}
|
|
362
|
+
|
|
317
363
|
/**
|
|
318
364
|
* Mirrors the base handler's `runMap`: a start callback whose `parentRunId`
|
|
319
365
|
* this handler never observed gets no explicit parent span and falls back
|
|
@@ -401,12 +447,15 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
401
447
|
}
|
|
402
448
|
const langfuse =
|
|
403
449
|
resolveLangfuseConfigForSpan(currentContext) ?? this.langfuse;
|
|
450
|
+
const parentedContext = isDetachedRun
|
|
451
|
+
? this.applyExplicitParent(currentContext, langfuse)
|
|
452
|
+
: currentContext;
|
|
404
453
|
const activeContext = isDetachedRun
|
|
405
454
|
? detachForeignAmbientSpan(
|
|
406
|
-
|
|
455
|
+
parentedContext,
|
|
407
456
|
resolveLangfuseDestinationKey(langfuse)
|
|
408
457
|
)
|
|
409
|
-
:
|
|
458
|
+
: parentedContext;
|
|
410
459
|
const scoped = (): T =>
|
|
411
460
|
withLangfuseRuntimeScope(
|
|
412
461
|
{
|
|
@@ -414,7 +463,9 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
414
463
|
traceIdSeed:
|
|
415
464
|
resolveTraceIdSeedForSpan(activeContext) ??
|
|
416
465
|
this.getDeterministicTraceSeed(),
|
|
466
|
+
traceAnchor: this.traceAnchor,
|
|
417
467
|
runId: scopeRunId ?? this.runId,
|
|
468
|
+
agentId: scopeAgentId ?? this.agentId,
|
|
418
469
|
},
|
|
419
470
|
action
|
|
420
471
|
);
|
|
@@ -438,12 +489,15 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
438
489
|
for (const key of Object.values(LangfuseOtelContextKeys)) {
|
|
439
490
|
cleanContext = cleanContext.deleteValue(key);
|
|
440
491
|
}
|
|
492
|
+
cleanContext = this.applyExplicitParent(cleanContext, this.langfuse);
|
|
441
493
|
const scoped = (): T =>
|
|
442
494
|
withLangfuseRuntimeScope(
|
|
443
495
|
{
|
|
444
496
|
langfuse: this.langfuse,
|
|
445
497
|
traceIdSeed: this.getDeterministicTraceSeed(),
|
|
498
|
+
traceAnchor: this.traceAnchor,
|
|
446
499
|
runId: this.runId,
|
|
500
|
+
agentId: this.agentId,
|
|
447
501
|
toolOutputTracing:
|
|
448
502
|
this.toolOutputTracing ??
|
|
449
503
|
resolveToolOutputTracingConfig(this.langfuse),
|
|
@@ -733,6 +787,10 @@ export function createLangfuseHandler({
|
|
|
733
787
|
traceMetadata,
|
|
734
788
|
tags,
|
|
735
789
|
traceIdSeed,
|
|
790
|
+
traceAnchor,
|
|
791
|
+
agentId,
|
|
792
|
+
parentSpanContext,
|
|
793
|
+
inheritTraceIdentity,
|
|
736
794
|
runId,
|
|
737
795
|
toolOutputTracing,
|
|
738
796
|
traceName,
|
|
@@ -743,13 +801,17 @@ export function createLangfuseHandler({
|
|
|
743
801
|
return new ScopedLangfuseCallbackHandler({
|
|
744
802
|
userId,
|
|
745
803
|
sessionId,
|
|
746
|
-
traceMetadata:
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
804
|
+
traceMetadata:
|
|
805
|
+
inheritTraceIdentity === true
|
|
806
|
+
? undefined
|
|
807
|
+
: mergeLangfuseTraceMetadata(traceMetadata, langfuse?.metadata),
|
|
750
808
|
tags: mergeLangfuseTags(tags, langfuse?.tags),
|
|
751
809
|
langfuse,
|
|
752
810
|
traceIdSeed,
|
|
811
|
+
traceAnchor,
|
|
812
|
+
agentId,
|
|
813
|
+
parentSpanContext,
|
|
814
|
+
inheritTraceIdentity,
|
|
753
815
|
runId,
|
|
754
816
|
toolOutputTracing,
|
|
755
817
|
traceName,
|
|
@@ -763,13 +825,17 @@ function createPropagateAttributeParams({
|
|
|
763
825
|
traceMetadata,
|
|
764
826
|
traceName,
|
|
765
827
|
tags,
|
|
828
|
+
inheritTraceIdentity,
|
|
766
829
|
}: LangfuseAttributeParams): PropagateAttributesParams {
|
|
767
830
|
return {
|
|
768
|
-
userId,
|
|
769
|
-
sessionId,
|
|
770
|
-
traceName,
|
|
831
|
+
userId: inheritTraceIdentity === true ? undefined : userId,
|
|
832
|
+
sessionId: inheritTraceIdentity === true ? undefined : sessionId,
|
|
833
|
+
traceName: inheritTraceIdentity === true ? undefined : traceName,
|
|
771
834
|
tags: mergeLangfuseTags(tags, langfuse?.tags),
|
|
772
|
-
metadata:
|
|
835
|
+
metadata:
|
|
836
|
+
inheritTraceIdentity === true
|
|
837
|
+
? undefined
|
|
838
|
+
: mergeLangfuseTraceMetadata(traceMetadata, langfuse?.metadata),
|
|
773
839
|
};
|
|
774
840
|
}
|
|
775
841
|
|
|
@@ -12,6 +12,8 @@ export type ResolvedLangfuseToolOutputTracingConfig = {
|
|
|
12
12
|
export type LangfuseRuntimeContext = {
|
|
13
13
|
langfuse?: t.LangfuseConfig;
|
|
14
14
|
traceIdSeed?: string;
|
|
15
|
+
/** Opaque run-owned key used to recover the exported trace parent. */
|
|
16
|
+
traceAnchor?: object;
|
|
15
17
|
toolOutputTracing?: ResolvedLangfuseToolOutputTracingConfig;
|
|
16
18
|
/**
|
|
17
19
|
* Identity of the run this scope belongs to. LangChain executes
|
|
@@ -44,6 +46,7 @@ export function hasLangfuseRuntimeContextValue(
|
|
|
44
46
|
return (
|
|
45
47
|
context.langfuse != null ||
|
|
46
48
|
hasText(context.traceIdSeed) ||
|
|
49
|
+
context.traceAnchor != null ||
|
|
47
50
|
hasText(context.runId) ||
|
|
48
51
|
hasText(context.agentId) ||
|
|
49
52
|
context.toolOutputTracing != null
|
|
@@ -100,6 +103,9 @@ export function runWithLangfuseRuntimeContext<T>(
|
|
|
100
103
|
...(hasText(context.traceIdSeed)
|
|
101
104
|
? { traceIdSeed: context.traceIdSeed }
|
|
102
105
|
: {}),
|
|
106
|
+
...(context.traceAnchor != null
|
|
107
|
+
? { traceAnchor: context.traceAnchor }
|
|
108
|
+
: {}),
|
|
103
109
|
...(hasText(context.runId) ? { runId: context.runId } : {}),
|
|
104
110
|
...(hasText(context.agentId) ? { agentId: context.agentId } : {}),
|
|
105
111
|
...(context.toolOutputTracing !== undefined
|