@librechat/agents 3.2.44 → 3.2.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/graphs/Graph.cjs +17 -8
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +20 -21
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +65 -13
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseConfig.cjs +106 -0
- package/dist/cjs/langfuseConfig.cjs.map +1 -0
- package/dist/cjs/langfuseRuntimeContext.cjs +48 -0
- package/dist/cjs/langfuseRuntimeContext.cjs.map +1 -0
- package/dist/cjs/langfuseRuntimeScope.cjs +57 -0
- package/dist/cjs/langfuseRuntimeScope.cjs.map +1 -0
- package/dist/cjs/langfuseToolOutputTracing.cjs +14 -119
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/main.cjs +1 -0
- package/dist/cjs/run.cjs +25 -12
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +5 -2
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/utils/misc.cjs +17 -0
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +16 -7
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +22 -22
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +65 -14
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseConfig.mjs +102 -0
- package/dist/esm/langfuseConfig.mjs.map +1 -0
- package/dist/esm/langfuseRuntimeContext.mjs +44 -0
- package/dist/esm/langfuseRuntimeContext.mjs.map +1 -0
- package/dist/esm/langfuseRuntimeScope.mjs +53 -0
- package/dist/esm/langfuseRuntimeScope.mjs.map +1 -0
- package/dist/esm/langfuseToolOutputTracing.mjs +13 -115
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/run.mjs +21 -8
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +5 -2
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/utils/misc.mjs +17 -1
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/types/instrumentation.d.ts +0 -1
- package/dist/types/langfuse.d.ts +5 -1
- package/dist/types/langfuseConfig.d.ts +7 -0
- package/dist/types/langfuseRuntimeContext.d.ts +26 -0
- package/dist/types/langfuseRuntimeScope.d.ts +17 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +4 -11
- package/dist/types/types/graph.d.ts +6 -0
- package/dist/types/utils/misc.d.ts +1 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +20 -13
- package/src/instrumentation.ts +33 -29
- package/src/langfuse.ts +114 -7
- package/src/langfuseConfig.ts +214 -0
- package/src/langfuseRuntimeContext.ts +93 -0
- package/src/langfuseRuntimeScope.ts +135 -0
- package/src/langfuseToolOutputTracing.ts +25 -263
- package/src/run.ts +44 -34
- package/src/specs/deterministic-trace-id.test.ts +4 -11
- package/src/specs/langfuse-callbacks.test.ts +278 -22
- package/src/specs/langfuse-config.test.ts +10 -3
- package/src/specs/langfuse-instrumentation.test.ts +32 -12
- package/src/specs/langfuse-routing.integration.test.ts +543 -0
- package/src/specs/langfuse-runtime-context.test.ts +92 -0
- package/src/specs/langfuse-tool-output-tracing.test.ts +353 -5
- package/src/tools/ToolNode.ts +10 -5
- package/src/tools/__tests__/ToolNode.langfuse.test.ts +21 -11
- package/src/types/graph.ts +9 -0
- package/src/utils/misc.ts +18 -0
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
1
|
import { LangfuseSpanProcessor } from '@langfuse/otel';
|
|
3
|
-
import { context, createContextKey } from '@opentelemetry/api';
|
|
4
2
|
import { LangfuseOtelSpanAttributes } from '@langfuse/tracing';
|
|
5
3
|
import type {
|
|
6
4
|
ReadableSpan,
|
|
@@ -9,17 +7,19 @@ import type {
|
|
|
9
7
|
} from '@opentelemetry/sdk-trace-base';
|
|
10
8
|
import type { LangfuseSpanProcessorParams } from '@langfuse/otel';
|
|
11
9
|
import type { Context } from '@opentelemetry/api';
|
|
10
|
+
import type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';
|
|
12
11
|
import type * as t from '@/types';
|
|
12
|
+
import {
|
|
13
|
+
LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,
|
|
14
|
+
hasToolOutputTracingConfig,
|
|
15
|
+
normalizeToolName,
|
|
16
|
+
resolveLangfuseConfig,
|
|
17
|
+
resolveToolOutputTracingConfig,
|
|
18
|
+
} from '@/langfuseConfig';
|
|
19
|
+
import { resolveToolOutputTracingConfigForSpan } from '@/langfuseRuntimeScope';
|
|
13
20
|
|
|
14
|
-
export
|
|
21
|
+
export { LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT, resolveLangfuseConfig };
|
|
15
22
|
|
|
16
|
-
const langfuseToolOutputTracingConfigKey = createContextKey(
|
|
17
|
-
'librechat.langfuse.tool-output-tracing'
|
|
18
|
-
);
|
|
19
|
-
const langfuseConfigKey = createContextKey('librechat.langfuse.config');
|
|
20
|
-
const toolOutputTracingStorage =
|
|
21
|
-
new AsyncLocalStorage<ResolvedLangfuseToolOutputTracingConfig>();
|
|
22
|
-
const langfuseConfigStorage = new AsyncLocalStorage<t.LangfuseConfig>();
|
|
23
23
|
const LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';
|
|
24
24
|
|
|
25
25
|
const CHAT_ROLES = new Set([
|
|
@@ -30,13 +30,6 @@ const CHAT_ROLES = new Set([
|
|
|
30
30
|
'user',
|
|
31
31
|
]);
|
|
32
32
|
|
|
33
|
-
export type ResolvedLangfuseToolOutputTracingConfig = {
|
|
34
|
-
enabled: boolean;
|
|
35
|
-
redactedToolNames: Set<string>;
|
|
36
|
-
redactedToolNameMatchMode: 'exact' | 'partial';
|
|
37
|
-
redactionText: string;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
33
|
type SpanWithAttributes = ReadableSpan & {
|
|
41
34
|
attributes: Record<string, unknown>;
|
|
42
35
|
};
|
|
@@ -60,122 +53,6 @@ function isPresent(value: unknown): value is string {
|
|
|
60
53
|
return typeof value === 'string' && value.trim() !== '';
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
function parseBoolean(value: string | undefined): boolean | undefined {
|
|
64
|
-
if (value == null) {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const normalized = value.trim().toLowerCase();
|
|
69
|
-
if (['1', 'true', 'yes', 'on'].includes(normalized)) {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
if (['0', 'false', 'no', 'off'].includes(normalized)) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function normalizeToolName(name: string): string {
|
|
80
|
-
return name.trim().toLowerCase();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function normalizeToolNames(names: string[] | undefined): Set<string> {
|
|
84
|
-
const normalized = new Set<string>();
|
|
85
|
-
for (const name of names ?? []) {
|
|
86
|
-
if (isPresent(name)) {
|
|
87
|
-
normalized.add(normalizeToolName(name));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return normalized;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function parseToolNames(value: string | undefined): string[] | undefined {
|
|
94
|
-
if (!isPresent(value)) {
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return value
|
|
99
|
-
.split(',')
|
|
100
|
-
.map((name) => name.trim())
|
|
101
|
-
.filter((name) => name !== '');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function getEnvToolOutputTracingEnabled(): boolean | undefined {
|
|
105
|
-
const traceToolOutputs = parseBoolean(
|
|
106
|
-
process.env.LANGFUSE_TRACE_TOOL_OUTPUTS
|
|
107
|
-
);
|
|
108
|
-
if (traceToolOutputs != null) {
|
|
109
|
-
return traceToolOutputs;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const redactToolOutputs = parseBoolean(
|
|
113
|
-
process.env.LANGFUSE_REDACT_TOOL_OUTPUTS
|
|
114
|
-
);
|
|
115
|
-
if (redactToolOutputs != null) {
|
|
116
|
-
return !redactToolOutputs;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return parseBoolean(process.env.LANGFUSE_TOOL_OUTPUT_TRACING_ENABLED);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function getEnvRedactedToolNames(): string[] | undefined {
|
|
123
|
-
return (
|
|
124
|
-
parseToolNames(process.env.LANGFUSE_REDACT_TOOL_OUTPUT_NAMES) ??
|
|
125
|
-
parseToolNames(process.env.LANGFUSE_REDACT_TOOL_NAMES)
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function getEnvRedactionText(): string | undefined {
|
|
130
|
-
return isPresent(process.env.LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT)
|
|
131
|
-
? process.env.LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT
|
|
132
|
-
: undefined;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function getEnvToolNameMatchMode(): 'exact' | 'partial' | undefined {
|
|
136
|
-
const mode = (
|
|
137
|
-
process.env.LANGFUSE_REDACT_TOOL_OUTPUT_NAME_MATCH_MODE ??
|
|
138
|
-
process.env.LANGFUSE_REDACT_TOOL_NAME_MATCH_MODE
|
|
139
|
-
)
|
|
140
|
-
?.trim()
|
|
141
|
-
.toLowerCase();
|
|
142
|
-
if (mode === 'exact' || mode === 'partial') {
|
|
143
|
-
return mode;
|
|
144
|
-
}
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function resolveToolOutputTracingConfig(
|
|
149
|
-
runLangfuse?: t.LangfuseConfig,
|
|
150
|
-
agentLangfuse?: t.LangfuseConfig
|
|
151
|
-
): ResolvedLangfuseToolOutputTracingConfig {
|
|
152
|
-
const runConfig = runLangfuse?.toolOutputTracing;
|
|
153
|
-
const agentConfig = agentLangfuse?.toolOutputTracing;
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
enabled:
|
|
157
|
-
agentConfig?.enabled ??
|
|
158
|
-
runConfig?.enabled ??
|
|
159
|
-
getEnvToolOutputTracingEnabled() ??
|
|
160
|
-
true,
|
|
161
|
-
redactedToolNames: normalizeToolNames(
|
|
162
|
-
agentConfig?.redactedToolNames ??
|
|
163
|
-
runConfig?.redactedToolNames ??
|
|
164
|
-
getEnvRedactedToolNames()
|
|
165
|
-
),
|
|
166
|
-
redactedToolNameMatchMode:
|
|
167
|
-
agentConfig?.redactedToolNameMatchMode ??
|
|
168
|
-
runConfig?.redactedToolNameMatchMode ??
|
|
169
|
-
getEnvToolNameMatchMode() ??
|
|
170
|
-
'exact',
|
|
171
|
-
redactionText:
|
|
172
|
-
agentConfig?.redactionText ??
|
|
173
|
-
runConfig?.redactionText ??
|
|
174
|
-
getEnvRedactionText() ??
|
|
175
|
-
LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
56
|
function shouldApplyToolOutputRedaction(
|
|
180
57
|
config: ResolvedLangfuseToolOutputTracingConfig
|
|
181
58
|
): boolean {
|
|
@@ -467,6 +344,10 @@ function classifyLangGraphToolNodeSpan(
|
|
|
467
344
|
}
|
|
468
345
|
}
|
|
469
346
|
|
|
347
|
+
export function classifyLangfuseToolNodeSpan(span: ReadableSpan): void {
|
|
348
|
+
classifyLangGraphToolNodeSpan((span as SpanWithAttributes).attributes);
|
|
349
|
+
}
|
|
350
|
+
|
|
470
351
|
function redactToolObservationOutput(
|
|
471
352
|
span: ReadableSpan,
|
|
472
353
|
attributes: Record<string, unknown>,
|
|
@@ -491,7 +372,7 @@ export function redactLangfuseSpanToolOutputs(
|
|
|
491
372
|
config: ResolvedLangfuseToolOutputTracingConfig
|
|
492
373
|
): void {
|
|
493
374
|
const attributes = (span as SpanWithAttributes).attributes;
|
|
494
|
-
|
|
375
|
+
classifyLangfuseToolNodeSpan(span);
|
|
495
376
|
|
|
496
377
|
if (!shouldApplyToolOutputRedaction(config)) {
|
|
497
378
|
return;
|
|
@@ -509,32 +390,6 @@ export function redactLangfuseSpanToolOutputs(
|
|
|
509
390
|
}
|
|
510
391
|
}
|
|
511
392
|
|
|
512
|
-
function getContextToolOutputTracingConfig(
|
|
513
|
-
activeContext: Context
|
|
514
|
-
): ResolvedLangfuseToolOutputTracingConfig | undefined {
|
|
515
|
-
const asyncConfig = toolOutputTracingStorage.getStore();
|
|
516
|
-
if (asyncConfig != null) {
|
|
517
|
-
return asyncConfig;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
const value = activeContext.getValue(langfuseToolOutputTracingConfigKey);
|
|
521
|
-
return isRecord(value)
|
|
522
|
-
? (value as ResolvedLangfuseToolOutputTracingConfig)
|
|
523
|
-
: undefined;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
export function getContextLangfuseConfig(
|
|
527
|
-
activeContext: Context
|
|
528
|
-
): t.LangfuseConfig | undefined {
|
|
529
|
-
const asyncConfig = langfuseConfigStorage.getStore();
|
|
530
|
-
if (asyncConfig != null) {
|
|
531
|
-
return asyncConfig;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
const value = activeContext.getValue(langfuseConfigKey);
|
|
535
|
-
return isRecord(value) ? (value as t.LangfuseConfig) : undefined;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
393
|
class ToolOutputRedactingLangfuseSpanProcessor implements SpanProcessor {
|
|
539
394
|
private readonly processor: LangfuseSpanProcessor;
|
|
540
395
|
private readonly fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig;
|
|
@@ -553,7 +408,8 @@ class ToolOutputRedactingLangfuseSpanProcessor implements SpanProcessor {
|
|
|
553
408
|
|
|
554
409
|
onStart(span: Span, parentContext: Context): void {
|
|
555
410
|
const config =
|
|
556
|
-
|
|
411
|
+
resolveToolOutputTracingConfigForSpan(parentContext) ??
|
|
412
|
+
this.fallbackConfig;
|
|
557
413
|
if (config != null) {
|
|
558
414
|
this.spanConfigs.set(span, config);
|
|
559
415
|
}
|
|
@@ -561,12 +417,11 @@ class ToolOutputRedactingLangfuseSpanProcessor implements SpanProcessor {
|
|
|
561
417
|
}
|
|
562
418
|
|
|
563
419
|
onEnd(span: ReadableSpan): void {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
redactLangfuseSpanToolOutputs(span, config);
|
|
420
|
+
classifyLangfuseToolNodeSpan(span);
|
|
421
|
+
const config = this.spanConfigs.get(span) ?? this.fallbackConfig;
|
|
422
|
+
if (config != null) {
|
|
423
|
+
redactLangfuseSpanToolOutputs(span, config);
|
|
424
|
+
}
|
|
570
425
|
this.processor.onEnd(span);
|
|
571
426
|
}
|
|
572
427
|
|
|
@@ -584,52 +439,12 @@ export function createLangfuseSpanProcessor(
|
|
|
584
439
|
runLangfuse?: t.LangfuseConfig,
|
|
585
440
|
agentLangfuse?: t.LangfuseConfig
|
|
586
441
|
): SpanProcessor {
|
|
587
|
-
const fallbackConfig =
|
|
588
|
-
runLangfuse
|
|
589
|
-
|
|
590
|
-
: undefined;
|
|
442
|
+
const fallbackConfig = hasToolOutputTracingConfig(runLangfuse, agentLangfuse)
|
|
443
|
+
? resolveToolOutputTracingConfig(runLangfuse, agentLangfuse)
|
|
444
|
+
: undefined;
|
|
591
445
|
return new ToolOutputRedactingLangfuseSpanProcessor(params, fallbackConfig);
|
|
592
446
|
}
|
|
593
447
|
|
|
594
|
-
export function withLangfuseToolOutputTracingConfig<T>(
|
|
595
|
-
runLangfuse: t.LangfuseConfig | undefined,
|
|
596
|
-
action: () => T,
|
|
597
|
-
agentLangfuse?: t.LangfuseConfig
|
|
598
|
-
): T {
|
|
599
|
-
const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);
|
|
600
|
-
const hasNoToolOutputConfig =
|
|
601
|
-
runLangfuse?.toolOutputTracing == null &&
|
|
602
|
-
agentLangfuse?.toolOutputTracing == null;
|
|
603
|
-
|
|
604
|
-
if (langfuse == null && hasNoToolOutputConfig) {
|
|
605
|
-
return action();
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
const config = hasNoToolOutputConfig
|
|
609
|
-
? undefined
|
|
610
|
-
: resolveToolOutputTracingConfig(runLangfuse, agentLangfuse);
|
|
611
|
-
let activeContext = context.active();
|
|
612
|
-
if (langfuse != null) {
|
|
613
|
-
activeContext = activeContext.setValue(langfuseConfigKey, langfuse);
|
|
614
|
-
}
|
|
615
|
-
if (config != null) {
|
|
616
|
-
activeContext = activeContext.setValue(
|
|
617
|
-
langfuseToolOutputTracingConfigKey,
|
|
618
|
-
config
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
const runWithContext = (): T => context.with(activeContext, action);
|
|
623
|
-
const runWithToolOutputConfig = (): T =>
|
|
624
|
-
config != null
|
|
625
|
-
? toolOutputTracingStorage.run(config, runWithContext)
|
|
626
|
-
: runWithContext();
|
|
627
|
-
|
|
628
|
-
return langfuse != null
|
|
629
|
-
? langfuseConfigStorage.run(langfuse, runWithToolOutputConfig)
|
|
630
|
-
: runWithToolOutputConfig();
|
|
631
|
-
}
|
|
632
|
-
|
|
633
448
|
function hasLangfuseEnvKeys(): boolean {
|
|
634
449
|
return (
|
|
635
450
|
isPresent(process.env.LANGFUSE_SECRET_KEY) &&
|
|
@@ -665,56 +480,3 @@ export function shouldTraceToolNodeForLangfuse({
|
|
|
665
480
|
|
|
666
481
|
return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();
|
|
667
482
|
}
|
|
668
|
-
|
|
669
|
-
export function resolveLangfuseConfig(
|
|
670
|
-
runLangfuse?: t.LangfuseConfig,
|
|
671
|
-
agentLangfuse?: t.LangfuseConfig
|
|
672
|
-
): t.LangfuseConfig | undefined {
|
|
673
|
-
if (runLangfuse == null) {
|
|
674
|
-
return agentLangfuse;
|
|
675
|
-
}
|
|
676
|
-
if (agentLangfuse == null) {
|
|
677
|
-
return runLangfuse;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
const toolNodeTracing =
|
|
681
|
-
runLangfuse.toolNodeTracing != null || agentLangfuse.toolNodeTracing != null
|
|
682
|
-
? {
|
|
683
|
-
...runLangfuse.toolNodeTracing,
|
|
684
|
-
...agentLangfuse.toolNodeTracing,
|
|
685
|
-
}
|
|
686
|
-
: undefined;
|
|
687
|
-
const toolOutputTracing =
|
|
688
|
-
runLangfuse.toolOutputTracing != null ||
|
|
689
|
-
agentLangfuse.toolOutputTracing != null
|
|
690
|
-
? {
|
|
691
|
-
...runLangfuse.toolOutputTracing,
|
|
692
|
-
...agentLangfuse.toolOutputTracing,
|
|
693
|
-
}
|
|
694
|
-
: undefined;
|
|
695
|
-
const metadata =
|
|
696
|
-
runLangfuse.metadata != null || agentLangfuse.metadata != null
|
|
697
|
-
? {
|
|
698
|
-
...runLangfuse.metadata,
|
|
699
|
-
...agentLangfuse.metadata,
|
|
700
|
-
}
|
|
701
|
-
: undefined;
|
|
702
|
-
const tags =
|
|
703
|
-
runLangfuse.tags != null || agentLangfuse.tags != null
|
|
704
|
-
? [
|
|
705
|
-
...new Set([
|
|
706
|
-
...(runLangfuse.tags ?? []),
|
|
707
|
-
...(agentLangfuse.tags ?? []),
|
|
708
|
-
]),
|
|
709
|
-
]
|
|
710
|
-
: undefined;
|
|
711
|
-
|
|
712
|
-
return {
|
|
713
|
-
...runLangfuse,
|
|
714
|
-
...agentLangfuse,
|
|
715
|
-
...(metadata != null ? { metadata } : {}),
|
|
716
|
-
...(tags != null ? { tags } : {}),
|
|
717
|
-
...(toolNodeTracing != null ? { toolNodeTracing } : {}),
|
|
718
|
-
...(toolOutputTracing != null ? { toolOutputTracing } : {}),
|
|
719
|
-
};
|
|
720
|
-
}
|
package/src/run.ts
CHANGED
|
@@ -27,9 +27,9 @@ import {
|
|
|
27
27
|
withLangfuseAttributes,
|
|
28
28
|
} from '@/langfuse';
|
|
29
29
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from '@/
|
|
30
|
+
resolveLangfuseRuntimeScope,
|
|
31
|
+
withLangfuseRuntimeScope,
|
|
32
|
+
} from '@/langfuseRuntimeScope';
|
|
33
33
|
import {
|
|
34
34
|
appendCallbacks,
|
|
35
35
|
findCallback,
|
|
@@ -39,13 +39,11 @@ import {
|
|
|
39
39
|
createCompletionTitleRunnable,
|
|
40
40
|
createTitleRunnable,
|
|
41
41
|
} from '@/utils/title';
|
|
42
|
-
import {
|
|
43
|
-
initializeLangfuseTracing,
|
|
44
|
-
runWithTraceIdSeed,
|
|
45
|
-
} from './instrumentation';
|
|
46
42
|
import { createTokenCounter, encodingForModel } from '@/utils/tokens';
|
|
43
|
+
import { initializeLangfuseTracing } from './instrumentation';
|
|
47
44
|
import { GraphEvents, Callback, TitleMethod } from '@/common';
|
|
48
45
|
import { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
|
|
46
|
+
import { resolveLangfuseConfig } from '@/langfuseConfig';
|
|
49
47
|
import { StandardGraph } from '@/graphs/Graph';
|
|
50
48
|
import { initializeModel } from '@/llm/init';
|
|
51
49
|
import { HandlerRegistry } from '@/events';
|
|
@@ -746,6 +744,10 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
746
744
|
sessionId,
|
|
747
745
|
traceMetadata,
|
|
748
746
|
tags: ['librechat', 'agent'],
|
|
747
|
+
traceIdSeed:
|
|
748
|
+
streamLangfuseConfig?.deterministicTraceId === true
|
|
749
|
+
? this.id
|
|
750
|
+
: undefined,
|
|
749
751
|
});
|
|
750
752
|
if (langfuseHandler != null) {
|
|
751
753
|
config.runName = traceName;
|
|
@@ -915,26 +917,26 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
915
917
|
// When opted in, seed the root trace id from this run's id so feedback /
|
|
916
918
|
// other external signals can be attached to the trace later without a
|
|
917
919
|
// lookup (see SeededTraceIdGenerator in ./instrumentation).
|
|
918
|
-
await
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
:
|
|
920
|
+
await withLangfuseRuntimeScope(
|
|
921
|
+
resolveLangfuseRuntimeScope({
|
|
922
|
+
runLangfuse: streamLangfuseConfig,
|
|
923
|
+
langfuseOverlay: this.getStreamToolOutputTracingLangfuseConfig(graph),
|
|
924
|
+
traceIdSeed:
|
|
925
|
+
streamLangfuseConfig?.deterministicTraceId === true
|
|
926
|
+
? this.id
|
|
927
|
+
: undefined,
|
|
928
|
+
}),
|
|
922
929
|
() =>
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
tags: ['librechat', 'agent'],
|
|
934
|
-
},
|
|
935
|
-
consumeStream
|
|
936
|
-
),
|
|
937
|
-
this.getStreamToolOutputTracingLangfuseConfig(graph)
|
|
930
|
+
withLangfuseAttributes(
|
|
931
|
+
{
|
|
932
|
+
langfuse: streamLangfuseConfig,
|
|
933
|
+
userId,
|
|
934
|
+
sessionId,
|
|
935
|
+
traceName,
|
|
936
|
+
traceMetadata,
|
|
937
|
+
tags: ['librechat', 'agent'],
|
|
938
|
+
},
|
|
939
|
+
consumeStream
|
|
938
940
|
)
|
|
939
941
|
);
|
|
940
942
|
} catch (err) {
|
|
@@ -1312,6 +1314,10 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1312
1314
|
sessionId: titleSessionId,
|
|
1313
1315
|
traceMetadata,
|
|
1314
1316
|
tags: ['librechat', 'title'],
|
|
1317
|
+
traceIdSeed:
|
|
1318
|
+
titleLangfuseConfig?.deterministicTraceId === true
|
|
1319
|
+
? 'title-' + this.id
|
|
1320
|
+
: undefined,
|
|
1315
1321
|
});
|
|
1316
1322
|
|
|
1317
1323
|
if (titleLangfuseHandler != null) {
|
|
@@ -1404,10 +1410,12 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1404
1410
|
|
|
1405
1411
|
try {
|
|
1406
1412
|
try {
|
|
1407
|
-
return await
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1413
|
+
return await withLangfuseRuntimeScope(
|
|
1414
|
+
resolveLangfuseRuntimeScope({
|
|
1415
|
+
runLangfuse: this.langfuse,
|
|
1416
|
+
langfuseOverlay: titleContext?.langfuse,
|
|
1417
|
+
}),
|
|
1418
|
+
() => invokeTitleChain(invokeConfig)
|
|
1411
1419
|
);
|
|
1412
1420
|
} catch (_e) {
|
|
1413
1421
|
// Fallback: strip callbacks to avoid EventStream tracer errors in certain environments
|
|
@@ -1420,10 +1428,12 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1420
1428
|
const safeConfig = Object.assign({}, rest, {
|
|
1421
1429
|
callbacks: langfuseHandler ? [langfuseHandler] : [],
|
|
1422
1430
|
});
|
|
1423
|
-
return await
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1431
|
+
return await withLangfuseRuntimeScope(
|
|
1432
|
+
resolveLangfuseRuntimeScope({
|
|
1433
|
+
runLangfuse: this.langfuse,
|
|
1434
|
+
langfuseOverlay: titleContext?.langfuse,
|
|
1435
|
+
}),
|
|
1436
|
+
() => invokeTitleChain(safeConfig as Partial<RunnableConfig>)
|
|
1427
1437
|
);
|
|
1428
1438
|
}
|
|
1429
1439
|
} finally {
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
initializeLangfuseTracing,
|
|
4
|
-
runWithTraceIdSeed,
|
|
5
|
-
} from '@/instrumentation';
|
|
1
|
+
import { runWithTraceIdSeed, traceIdFromSeed } from '@/langfuseRuntimeContext';
|
|
2
|
+
import { initializeLangfuseTracing } from '@/instrumentation';
|
|
6
3
|
|
|
7
4
|
const langfuse = {
|
|
8
5
|
publicKey: 'pk-lf-test',
|
|
@@ -10,10 +7,6 @@ const langfuse = {
|
|
|
10
7
|
baseUrl: 'http://localhost:3999',
|
|
11
8
|
};
|
|
12
9
|
|
|
13
|
-
/** sha256(seed) → first 32 hex chars; what `@langfuse/tracing` `createTraceId` produces. */
|
|
14
|
-
const expectedTraceId = (seed: string): string =>
|
|
15
|
-
createHash('sha256').update(seed, 'utf8').digest('hex').slice(0, 32);
|
|
16
|
-
|
|
17
10
|
describe('deterministic Langfuse trace ids', () => {
|
|
18
11
|
it('derives the root trace id from the seed when run inside runWithTraceIdSeed', () => {
|
|
19
12
|
const provider = initializeLangfuseTracing(langfuse);
|
|
@@ -28,7 +21,7 @@ describe('deterministic Langfuse trace ids', () => {
|
|
|
28
21
|
span.end();
|
|
29
22
|
});
|
|
30
23
|
|
|
31
|
-
expect(traceId).toBe(
|
|
24
|
+
expect(traceId).toBe(traceIdFromSeed(seed));
|
|
32
25
|
});
|
|
33
26
|
|
|
34
27
|
it('falls back to a random trace id when no seed is active', () => {
|
|
@@ -40,7 +33,7 @@ describe('deterministic Langfuse trace ids', () => {
|
|
|
40
33
|
span.end();
|
|
41
34
|
|
|
42
35
|
expect(traceId).toMatch(/^[0-9a-f]{32}$/);
|
|
43
|
-
expect(traceId).not.toBe(
|
|
36
|
+
expect(traceId).not.toBe(traceIdFromSeed('response-message-id-1234'));
|
|
44
37
|
});
|
|
45
38
|
|
|
46
39
|
it('runWithTraceIdSeed is a passthrough when the seed is undefined', () => {
|