@librechat/agents 3.6.7 → 3.6.8
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/subagent/SubagentExecutor.cjs +2 -2
- 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/subagent/SubagentExecutor.mjs +2 -2
- 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/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/types/activityLabel.ts +4 -3
- package/src/types/reasoningLabel.ts +1 -1
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
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import type * as t from '@/types';
|
|
8
8
|
import {
|
|
9
9
|
getLangfuseRuntimeConfig,
|
|
10
|
+
getLangfuseRuntimeContext,
|
|
10
11
|
getLangfuseRuntimeToolOutputTracingConfig,
|
|
11
12
|
getLangfuseScopeAgentId,
|
|
12
13
|
getLangfuseScopeRunId,
|
|
@@ -27,6 +28,7 @@ export type ResolveLangfuseRuntimeScopeParams = {
|
|
|
27
28
|
runLangfuse?: t.LangfuseConfig;
|
|
28
29
|
langfuseOverlay?: t.LangfuseConfig;
|
|
29
30
|
traceIdSeed?: string;
|
|
31
|
+
traceAnchor?: object;
|
|
30
32
|
runId?: string;
|
|
31
33
|
agentId?: string;
|
|
32
34
|
};
|
|
@@ -46,6 +48,9 @@ const langfuseConfigKey = createContextKey('librechat.langfuse.config');
|
|
|
46
48
|
const langfuseTraceIdSeedKey = createContextKey(
|
|
47
49
|
'librechat.langfuse.trace-id-seed'
|
|
48
50
|
);
|
|
51
|
+
const langfuseTraceAnchorKey = createContextKey(
|
|
52
|
+
'librechat.langfuse.trace-anchor'
|
|
53
|
+
);
|
|
49
54
|
const langfuseScopeRunIdKey = createContextKey('librechat.langfuse.run-id');
|
|
50
55
|
const langfuseScopeAgentIdKey = createContextKey('librechat.langfuse.agent-id');
|
|
51
56
|
|
|
@@ -69,6 +74,17 @@ export function getOtelTraceIdSeed(activeContext: Context): string | undefined {
|
|
|
69
74
|
return typeof value === 'string' && value.trim() !== '' ? value : undefined;
|
|
70
75
|
}
|
|
71
76
|
|
|
77
|
+
export function resolveLangfuseTraceAnchor(
|
|
78
|
+
activeContext: Context
|
|
79
|
+
): object | undefined {
|
|
80
|
+
const runtimeAnchor = getLangfuseRuntimeContext()?.traceAnchor;
|
|
81
|
+
if (runtimeAnchor != null) {
|
|
82
|
+
return runtimeAnchor;
|
|
83
|
+
}
|
|
84
|
+
const value = activeContext.getValue(langfuseTraceAnchorKey);
|
|
85
|
+
return typeof value === 'object' && value != null ? value : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
72
88
|
export function getOtelToolOutputTracingConfig(
|
|
73
89
|
activeContext: Context
|
|
74
90
|
): ResolvedLangfuseToolOutputTracingConfig | undefined {
|
|
@@ -166,6 +182,12 @@ export function withLangfuseRuntimeScope<T>(
|
|
|
166
182
|
hasText(scope.traceIdSeed) ? scope.traceIdSeed : undefined,
|
|
167
183
|
replace
|
|
168
184
|
);
|
|
185
|
+
activeContext = setOrClearContextValue(
|
|
186
|
+
activeContext,
|
|
187
|
+
langfuseTraceAnchorKey,
|
|
188
|
+
scope.traceAnchor,
|
|
189
|
+
replace
|
|
190
|
+
);
|
|
169
191
|
activeContext = setOrClearContextValue(
|
|
170
192
|
activeContext,
|
|
171
193
|
langfuseScopeRunIdKey,
|
|
@@ -194,6 +216,7 @@ export function resolveLangfuseRuntimeScope({
|
|
|
194
216
|
runLangfuse,
|
|
195
217
|
langfuseOverlay,
|
|
196
218
|
traceIdSeed,
|
|
219
|
+
traceAnchor,
|
|
197
220
|
runId,
|
|
198
221
|
agentId,
|
|
199
222
|
}: ResolveLangfuseRuntimeScopeParams): LangfuseRuntimeScope {
|
|
@@ -204,5 +227,12 @@ export function resolveLangfuseRuntimeScope({
|
|
|
204
227
|
)
|
|
205
228
|
? undefined
|
|
206
229
|
: resolveToolOutputTracingConfig(runLangfuse, langfuseOverlay);
|
|
207
|
-
return {
|
|
230
|
+
return {
|
|
231
|
+
langfuse,
|
|
232
|
+
traceIdSeed,
|
|
233
|
+
traceAnchor,
|
|
234
|
+
toolOutputTracing,
|
|
235
|
+
runId,
|
|
236
|
+
agentId,
|
|
237
|
+
};
|
|
208
238
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
+
import { context, trace } from '@opentelemetry/api';
|
|
2
3
|
import type { LangfuseSpanProcessorParams } from '@langfuse/otel';
|
|
3
|
-
import type { Span } from '@opentelemetry/api';
|
|
4
|
+
import type { Span, SpanContext } from '@opentelemetry/api';
|
|
4
5
|
import type * as t from '@/types';
|
|
5
6
|
import {
|
|
6
7
|
hasLangfuseConfigCredentials,
|
|
@@ -16,7 +17,7 @@ import { isPresent } from '@/utils/misc';
|
|
|
16
17
|
*
|
|
17
18
|
* - Foreign spans (a host's own OpenTelemetry instrumentation, e.g. HTTP
|
|
18
19
|
* server spans on the global provider) are never exported to Langfuse —
|
|
19
|
-
* inheriting one orphans the trace root (its root
|
|
20
|
+
* inheriting one orphans the trace root (its root-observation input/output
|
|
20
21
|
* shaping is skipped), collapses concurrent runs inside one request
|
|
21
22
|
* context into a single merged trace, and bypasses the seeded
|
|
22
23
|
* deterministic trace id generator.
|
|
@@ -30,6 +31,19 @@ import { isPresent } from '@/utils/misc';
|
|
|
30
31
|
*/
|
|
31
32
|
const managedSpanDestinations = new WeakMap<Span, string>();
|
|
32
33
|
|
|
34
|
+
type AnchoredSpan = {
|
|
35
|
+
destinationKey: string;
|
|
36
|
+
spanContext: SpanContext;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type TraceAnchorState = {
|
|
40
|
+
spans: Map<string, AnchoredSpan>;
|
|
41
|
+
spanIds: Set<string>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const ROOT_TRACE_ANCHOR = '';
|
|
45
|
+
const traceAnchorStates = new WeakMap<object, TraceAnchorState>();
|
|
46
|
+
|
|
33
47
|
export function registerLangfuseManagedSpan(
|
|
34
48
|
span: Span,
|
|
35
49
|
destinationKey: string
|
|
@@ -43,6 +57,68 @@ export function getLangfuseManagedSpanDestination(
|
|
|
43
57
|
return managedSpanDestinations.get(span);
|
|
44
58
|
}
|
|
45
59
|
|
|
60
|
+
/** Captures the first exported observation for a run and for each agent lane. */
|
|
61
|
+
export function registerLangfuseTraceAnchorSpan(
|
|
62
|
+
anchor: object,
|
|
63
|
+
span: Span,
|
|
64
|
+
destinationKey: string,
|
|
65
|
+
agentId?: string
|
|
66
|
+
): void {
|
|
67
|
+
let state = traceAnchorStates.get(anchor);
|
|
68
|
+
if (state == null) {
|
|
69
|
+
state = { spans: new Map<string, AnchoredSpan>(), spanIds: new Set() };
|
|
70
|
+
traceAnchorStates.set(anchor, state);
|
|
71
|
+
}
|
|
72
|
+
const spanContext = span.spanContext();
|
|
73
|
+
state.spanIds.add(spanContext.spanId);
|
|
74
|
+
const key = agentId ?? ROOT_TRACE_ANCHOR;
|
|
75
|
+
if (state.spans.has(key)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
state.spans.set(key, { destinationKey, spanContext });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolves the most specific safe parent available for auxiliary work. An
|
|
83
|
+
* active observation wins so labels emitted inside a tool or reasoning step
|
|
84
|
+
* stay beside their source; otherwise the run root anchors the label in the
|
|
85
|
+
* same trace. An agent lane is the fallback when its overlay exports to a
|
|
86
|
+
* different destination. Cross-destination parents are never returned.
|
|
87
|
+
*/
|
|
88
|
+
export function resolveLangfuseTraceAnchorParent(
|
|
89
|
+
anchor: object | undefined,
|
|
90
|
+
destinationKey: string | undefined,
|
|
91
|
+
agentId?: string
|
|
92
|
+
): SpanContext | undefined {
|
|
93
|
+
if (anchor == null || destinationKey == null) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const state = traceAnchorStates.get(anchor);
|
|
97
|
+
if (state == null) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const agentSpan = agentId == null ? undefined : state.spans.get(agentId);
|
|
101
|
+
const rootSpan = state.spans.get(ROOT_TRACE_ANCHOR);
|
|
102
|
+
let anchoredSpan = rootSpan;
|
|
103
|
+
if (anchoredSpan?.destinationKey !== destinationKey) {
|
|
104
|
+
anchoredSpan = agentSpan;
|
|
105
|
+
}
|
|
106
|
+
if (anchoredSpan?.destinationKey !== destinationKey) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const activeSpan = trace.getSpan(context.active());
|
|
111
|
+
if (
|
|
112
|
+
activeSpan != null &&
|
|
113
|
+
getLangfuseManagedSpanDestination(activeSpan) === destinationKey &&
|
|
114
|
+
activeSpan.spanContext().traceId === anchoredSpan.spanContext.traceId &&
|
|
115
|
+
state.spanIds.has(activeSpan.spanContext().spanId)
|
|
116
|
+
) {
|
|
117
|
+
return activeSpan.spanContext();
|
|
118
|
+
}
|
|
119
|
+
return anchoredSpan.spanContext;
|
|
120
|
+
}
|
|
121
|
+
|
|
46
122
|
function resolveLangfuseEnvironment(
|
|
47
123
|
langfuse?: t.LangfuseConfig
|
|
48
124
|
): string | undefined {
|
|
@@ -779,8 +779,6 @@ export function redactLangfuseSpanToolOutputs(
|
|
|
779
779
|
for (const key of [
|
|
780
780
|
LangfuseOtelSpanAttributes.OBSERVATION_INPUT,
|
|
781
781
|
LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT,
|
|
782
|
-
LangfuseOtelSpanAttributes.TRACE_INPUT,
|
|
783
|
-
LangfuseOtelSpanAttributes.TRACE_OUTPUT,
|
|
784
782
|
]) {
|
|
785
783
|
redactAttribute(attributes, key, config);
|
|
786
784
|
}
|