@anvia/langfuse 0.3.9 → 0.5.0
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/README.md +40 -23
- package/dist/index.d.ts +8 -1
- package/dist/index.js +566 -233
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -20,21 +20,30 @@ function modelParameters(request) {
|
|
|
20
20
|
return params;
|
|
21
21
|
}
|
|
22
22
|
function usageDetails(usage) {
|
|
23
|
+
if (usage.details !== void 0 && Object.keys(usage.details).length > 0) {
|
|
24
|
+
return { ...usage.details };
|
|
25
|
+
}
|
|
23
26
|
return {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cachedInputTokens: usage.cachedInputTokens,
|
|
28
|
-
cacheCreationInputTokens: usage.cacheCreationInputTokens
|
|
27
|
+
input: usage.inputTokens,
|
|
28
|
+
output: usage.outputTokens,
|
|
29
|
+
total: usage.totalTokens
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
function usageDetailsFromRecord(usage) {
|
|
33
|
+
if (isRecord(usage.details)) {
|
|
34
|
+
const details = Object.fromEntries(
|
|
35
|
+
Object.entries(usage.details).filter(
|
|
36
|
+
(entry) => typeof entry[1] === "number" && Number.isFinite(entry[1]) && entry[1] >= 0
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
if (Object.keys(details).length > 0) {
|
|
40
|
+
return details;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
32
43
|
return {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
cachedInputTokens: numberValue(usage.cachedInputTokens) ?? 0,
|
|
37
|
-
cacheCreationInputTokens: numberValue(usage.cacheCreationInputTokens) ?? 0
|
|
44
|
+
input: numberValue(usage.inputTokens) ?? 0,
|
|
45
|
+
output: numberValue(usage.outputTokens) ?? 0,
|
|
46
|
+
total: numberValue(usage.totalTokens) ?? (numberValue(usage.inputTokens) ?? 0) + (numberValue(usage.outputTokens) ?? 0)
|
|
38
47
|
};
|
|
39
48
|
}
|
|
40
49
|
function childMetadata(args, agentId, agentName, childTurn) {
|
|
@@ -121,6 +130,8 @@ var LangfuseScoreError = class extends Error {
|
|
|
121
130
|
}
|
|
122
131
|
}
|
|
123
132
|
};
|
|
133
|
+
var RetryableLangfuseScoreError = class extends LangfuseScoreError {
|
|
134
|
+
};
|
|
124
135
|
var defaultSleep = (ms) => new Promise((resolve) => {
|
|
125
136
|
setTimeout(resolve, ms);
|
|
126
137
|
});
|
|
@@ -139,6 +150,7 @@ var ScoreQueue = class {
|
|
|
139
150
|
pending = [];
|
|
140
151
|
timer = null;
|
|
141
152
|
flushing = null;
|
|
153
|
+
closed = false;
|
|
142
154
|
baseUrl;
|
|
143
155
|
publicKey;
|
|
144
156
|
secretKey;
|
|
@@ -164,9 +176,12 @@ var ScoreQueue = class {
|
|
|
164
176
|
this.clearTimer = options.clearTimer ?? defaultClearTimer;
|
|
165
177
|
}
|
|
166
178
|
enqueue(score) {
|
|
179
|
+
if (this.closed) {
|
|
180
|
+
throw new Error("Langfuse score queue is shut down");
|
|
181
|
+
}
|
|
167
182
|
this.pending.push(score);
|
|
168
183
|
if (this.pending.length >= this.batchSize) {
|
|
169
|
-
|
|
184
|
+
this.triggerBackgroundFlush();
|
|
170
185
|
return;
|
|
171
186
|
}
|
|
172
187
|
this.scheduleTimer();
|
|
@@ -175,24 +190,36 @@ var ScoreQueue = class {
|
|
|
175
190
|
return this.pending.length;
|
|
176
191
|
}
|
|
177
192
|
async flush() {
|
|
178
|
-
if (this.flushing !== null) {
|
|
179
|
-
await this.flushing;
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (this.pending.length === 0) {
|
|
183
|
-
this.clearScheduledTimer();
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const batch = this.pending.splice(0, this.pending.length);
|
|
187
193
|
this.clearScheduledTimer();
|
|
188
|
-
|
|
189
|
-
this.flushing
|
|
190
|
-
|
|
191
|
-
|
|
194
|
+
while (true) {
|
|
195
|
+
if (this.flushing !== null) {
|
|
196
|
+
await this.flushing;
|
|
197
|
+
if (this.pending.length === 0) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (this.pending.length === 0) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const draining = this.drain();
|
|
206
|
+
this.flushing = draining;
|
|
207
|
+
try {
|
|
208
|
+
await draining;
|
|
209
|
+
} finally {
|
|
210
|
+
if (this.flushing === draining) {
|
|
211
|
+
this.flushing = null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
192
215
|
}
|
|
193
216
|
async shutdown() {
|
|
217
|
+
this.closed = true;
|
|
194
218
|
this.clearScheduledTimer();
|
|
195
|
-
|
|
219
|
+
try {
|
|
220
|
+
await this.flush();
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
196
223
|
}
|
|
197
224
|
scheduleTimer() {
|
|
198
225
|
if (this.timer !== null) {
|
|
@@ -200,9 +227,13 @@ var ScoreQueue = class {
|
|
|
200
227
|
}
|
|
201
228
|
this.timer = this.setTimer(() => {
|
|
202
229
|
this.timer = null;
|
|
203
|
-
|
|
230
|
+
this.triggerBackgroundFlush();
|
|
204
231
|
}, this.flushIntervalMs);
|
|
205
232
|
}
|
|
233
|
+
triggerBackgroundFlush() {
|
|
234
|
+
void this.flush().catch(() => {
|
|
235
|
+
});
|
|
236
|
+
}
|
|
206
237
|
clearScheduledTimer() {
|
|
207
238
|
if (this.timer !== null) {
|
|
208
239
|
this.clearTimer(this.timer);
|
|
@@ -245,12 +276,35 @@ var ScoreQueue = class {
|
|
|
245
276
|
await this.sleep(computeBackoff(attempt));
|
|
246
277
|
}
|
|
247
278
|
}
|
|
248
|
-
throw new
|
|
279
|
+
throw new RetryableLangfuseScoreError(
|
|
249
280
|
`Langfuse score batch failed after ${this.maxRetries} attempts`,
|
|
250
281
|
scores,
|
|
251
282
|
lastError
|
|
252
283
|
);
|
|
253
284
|
}
|
|
285
|
+
async drain() {
|
|
286
|
+
let permanentError;
|
|
287
|
+
while (this.pending.length > 0) {
|
|
288
|
+
const batch = this.pending.splice(0, this.batchSize);
|
|
289
|
+
try {
|
|
290
|
+
await this.sendBatch(batch);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
if (error instanceof RetryableLangfuseScoreError) {
|
|
293
|
+
this.pending.unshift(...batch);
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
if (error instanceof LangfuseScoreError) {
|
|
297
|
+
permanentError ??= error;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
this.pending.unshift(...batch);
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (permanentError !== void 0) {
|
|
305
|
+
throw permanentError;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
254
308
|
};
|
|
255
309
|
function buildScoreBody(score) {
|
|
256
310
|
const body = {
|
|
@@ -452,17 +506,26 @@ function toJsonValue(value) {
|
|
|
452
506
|
}
|
|
453
507
|
|
|
454
508
|
// src/eval-reporter.ts
|
|
509
|
+
import {
|
|
510
|
+
projectEvalOutcome,
|
|
511
|
+
resolveEvalTraceRef
|
|
512
|
+
} from "@anvia/core/evals";
|
|
455
513
|
var DEFAULT_TRUNCATE_BYTES = 2048;
|
|
456
514
|
function createLangfuseEvalReporter(tracing, options = {}) {
|
|
457
515
|
const onMissingTrace = options.onMissingTrace ?? (options.strict === true ? "throw" : "ignore");
|
|
458
516
|
const truncateAt = options.truncateInputAt ?? DEFAULT_TRUNCATE_BYTES;
|
|
459
517
|
const includeMessages = options.includeMessages ?? true;
|
|
518
|
+
const includeContext = options.includeContext ?? false;
|
|
460
519
|
return {
|
|
461
520
|
async report(args) {
|
|
462
521
|
if (args.outcome.outcome === "invalid" && options.publishInvalid !== true) {
|
|
463
522
|
return;
|
|
464
523
|
}
|
|
465
|
-
const trace =
|
|
524
|
+
const trace = args.trace ?? resolveEvalTraceRef({
|
|
525
|
+
output: args.output,
|
|
526
|
+
input: args.case.input,
|
|
527
|
+
metadata: args.case.metadata
|
|
528
|
+
});
|
|
466
529
|
if (trace?.traceId === void 0 || trace.traceId.length === 0) {
|
|
467
530
|
if (onMissingTrace === "throw") {
|
|
468
531
|
throw new Error("Langfuse eval reporter requires traceId");
|
|
@@ -475,23 +538,23 @@ function createLangfuseEvalReporter(tracing, options = {}) {
|
|
|
475
538
|
}
|
|
476
539
|
return;
|
|
477
540
|
}
|
|
478
|
-
const
|
|
541
|
+
const projection = projectEvalOutcome(args.outcome, args.metric.dataType);
|
|
479
542
|
const metadata = buildScoreMetadata({
|
|
480
543
|
args,
|
|
481
544
|
truncateAt,
|
|
482
|
-
includeMessages
|
|
545
|
+
includeMessages,
|
|
546
|
+
includeContext
|
|
483
547
|
});
|
|
484
|
-
const comment = scoreComment(args.outcome);
|
|
485
548
|
const configId = resolveConfigId(args.metric);
|
|
486
549
|
const score = {
|
|
487
550
|
traceId: trace.traceId,
|
|
488
551
|
name: args.metric.name,
|
|
489
|
-
value:
|
|
552
|
+
value: projection.value
|
|
490
553
|
};
|
|
491
554
|
if (trace.observationId !== void 0) score.observationId = trace.observationId;
|
|
492
555
|
if (args.metric.dataType !== void 0) score.dataType = args.metric.dataType;
|
|
493
556
|
if (configId !== void 0) score.configId = configId;
|
|
494
|
-
if (
|
|
557
|
+
if (projection.explanation !== void 0) score.comment = projection.explanation;
|
|
495
558
|
if (metadata !== void 0) score.metadata = metadata;
|
|
496
559
|
await tracing.score(score);
|
|
497
560
|
}
|
|
@@ -503,7 +566,8 @@ function resolveConfigId(metric) {
|
|
|
503
566
|
function buildScoreMetadata({
|
|
504
567
|
args,
|
|
505
568
|
truncateAt,
|
|
506
|
-
includeMessages
|
|
569
|
+
includeMessages,
|
|
570
|
+
includeContext
|
|
507
571
|
}) {
|
|
508
572
|
const merged = {
|
|
509
573
|
suiteName: args.suiteName,
|
|
@@ -512,6 +576,15 @@ function buildScoreMetadata({
|
|
|
512
576
|
};
|
|
513
577
|
mergeMetadata(merged, args.outcome.metadata);
|
|
514
578
|
mergeMetadata(merged, args.metric.metadata);
|
|
579
|
+
if (args.case.metadata !== void 0) {
|
|
580
|
+
merged.caseMetadata = { ...args.case.metadata };
|
|
581
|
+
}
|
|
582
|
+
if (includeContext && args.case.context !== void 0) {
|
|
583
|
+
merged.context = [...args.case.context];
|
|
584
|
+
}
|
|
585
|
+
if (includeContext && args.case.retrievalContext !== void 0) {
|
|
586
|
+
merged.retrievalContext = [...args.case.retrievalContext];
|
|
587
|
+
}
|
|
515
588
|
const inputSummary = truncateValue(args.case.input, truncateAt);
|
|
516
589
|
if (inputSummary !== void 0) {
|
|
517
590
|
merged.caseInputSummary = inputSummary;
|
|
@@ -569,84 +642,6 @@ function readMessages(output) {
|
|
|
569
642
|
}
|
|
570
643
|
return serialized;
|
|
571
644
|
}
|
|
572
|
-
function buildScoreValue(outcome, dataType) {
|
|
573
|
-
if (dataType === "CATEGORICAL") {
|
|
574
|
-
const score = outcome.score;
|
|
575
|
-
if (typeof score === "string") return score;
|
|
576
|
-
if (typeof score === "number") return String(score);
|
|
577
|
-
if (typeof score === "boolean") return score ? "true" : "false";
|
|
578
|
-
if (score === null || score === void 0) {
|
|
579
|
-
return outcome.outcome === "pass" ? "pass" : outcome.outcome === "fail" ? "fail" : "invalid";
|
|
580
|
-
}
|
|
581
|
-
try {
|
|
582
|
-
return JSON.stringify(score);
|
|
583
|
-
} catch {
|
|
584
|
-
return outcome.outcome === "pass" ? "pass" : outcome.outcome;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
if (dataType === "BOOLEAN") {
|
|
588
|
-
const score = outcome.score;
|
|
589
|
-
if (typeof score === "boolean") return score ? 1 : 0;
|
|
590
|
-
if (typeof score === "number") return score === 0 ? 0 : 1;
|
|
591
|
-
return outcome.outcome === "pass" ? 1 : 0;
|
|
592
|
-
}
|
|
593
|
-
if (typeof outcome.score === "number") {
|
|
594
|
-
return outcome.score;
|
|
595
|
-
}
|
|
596
|
-
if (typeof outcome.score === "boolean") {
|
|
597
|
-
return outcome.score ? 1 : 0;
|
|
598
|
-
}
|
|
599
|
-
if (typeof outcome.score === "object" && outcome.score !== null && "score" in outcome.score && typeof outcome.score.score === "number") {
|
|
600
|
-
return outcome.score.score;
|
|
601
|
-
}
|
|
602
|
-
return outcome.outcome === "pass" ? 1 : 0;
|
|
603
|
-
}
|
|
604
|
-
function scoreComment(outcome) {
|
|
605
|
-
return outcome.comment ?? (outcome.outcome === "invalid" ? outcome.reason : void 0);
|
|
606
|
-
}
|
|
607
|
-
function traceFromEvalReport(args) {
|
|
608
|
-
const outputTrace = traceFromOutput(args.output);
|
|
609
|
-
if (outputTrace !== void 0) {
|
|
610
|
-
return outputTrace;
|
|
611
|
-
}
|
|
612
|
-
const inputTrace = traceFromInput(args.case.input);
|
|
613
|
-
if (inputTrace !== void 0) {
|
|
614
|
-
return inputTrace;
|
|
615
|
-
}
|
|
616
|
-
const traceId = args.case.metadata?.traceId;
|
|
617
|
-
const observationId = args.case.metadata?.observationId;
|
|
618
|
-
if (typeof traceId !== "string") {
|
|
619
|
-
return void 0;
|
|
620
|
-
}
|
|
621
|
-
const trace = { traceId };
|
|
622
|
-
if (typeof observationId === "string") trace.observationId = observationId;
|
|
623
|
-
return trace;
|
|
624
|
-
}
|
|
625
|
-
function traceFromOutput(output) {
|
|
626
|
-
if (typeof output !== "object" || output === null || !("trace" in output)) {
|
|
627
|
-
return void 0;
|
|
628
|
-
}
|
|
629
|
-
return readTrace(output.trace);
|
|
630
|
-
}
|
|
631
|
-
function traceFromInput(input) {
|
|
632
|
-
if (typeof input !== "object" || input === null || !("trace" in input)) {
|
|
633
|
-
return void 0;
|
|
634
|
-
}
|
|
635
|
-
return readTrace(input.trace);
|
|
636
|
-
}
|
|
637
|
-
function readTrace(trace) {
|
|
638
|
-
if (typeof trace !== "object" || trace === null) {
|
|
639
|
-
return void 0;
|
|
640
|
-
}
|
|
641
|
-
const traceId = trace.traceId;
|
|
642
|
-
const observationId = trace.observationId;
|
|
643
|
-
if (typeof traceId !== "string") {
|
|
644
|
-
return void 0;
|
|
645
|
-
}
|
|
646
|
-
const result = { traceId };
|
|
647
|
-
if (typeof observationId === "string") result.observationId = observationId;
|
|
648
|
-
return result;
|
|
649
|
-
}
|
|
650
645
|
|
|
651
646
|
// src/experiment-runner.ts
|
|
652
647
|
import { runEvalSuite } from "@anvia/core/evals";
|
|
@@ -656,7 +651,17 @@ async function runEvalAsExperiment(evalOptions, experimentOptions) {
|
|
|
656
651
|
if (experimentOptions.timeoutMs !== void 0)
|
|
657
652
|
clientOptions.timeoutMs = experimentOptions.timeoutMs;
|
|
658
653
|
const client = experimentOptions.client ?? createLangfuseDatasetClient(experimentOptions.tracing, clientOptions);
|
|
659
|
-
const
|
|
654
|
+
const suiteOptions = experimentOptions.publishScores === true ? {
|
|
655
|
+
...evalOptions,
|
|
656
|
+
reporters: [
|
|
657
|
+
...evalOptions.reporters ?? [],
|
|
658
|
+
createLangfuseEvalReporter(
|
|
659
|
+
experimentOptions.tracing,
|
|
660
|
+
experimentOptions.reporterOptions
|
|
661
|
+
)
|
|
662
|
+
]
|
|
663
|
+
} : evalOptions;
|
|
664
|
+
const suite = await runEvalSuite(suiteOptions);
|
|
660
665
|
const items = evalOptions.cases.map((testCase) => {
|
|
661
666
|
const item = {
|
|
662
667
|
id: testCase.id,
|
|
@@ -665,8 +670,17 @@ async function runEvalAsExperiment(evalOptions, experimentOptions) {
|
|
|
665
670
|
if (testCase.expected !== void 0) {
|
|
666
671
|
item.expected = testCase.expected;
|
|
667
672
|
}
|
|
668
|
-
|
|
669
|
-
|
|
673
|
+
const metadata = {
|
|
674
|
+
...testCase.metadata ?? {}
|
|
675
|
+
};
|
|
676
|
+
if (experimentOptions.includeContexts === true && testCase.context !== void 0) {
|
|
677
|
+
metadata.context = [...testCase.context];
|
|
678
|
+
}
|
|
679
|
+
if (experimentOptions.includeContexts === true && testCase.retrievalContext !== void 0) {
|
|
680
|
+
metadata.retrievalContext = [...testCase.retrievalContext];
|
|
681
|
+
}
|
|
682
|
+
if (Object.keys(metadata).length > 0) {
|
|
683
|
+
item.metadata = metadata;
|
|
670
684
|
}
|
|
671
685
|
return item;
|
|
672
686
|
});
|
|
@@ -947,29 +961,42 @@ function cloneRegex(source, flags) {
|
|
|
947
961
|
}
|
|
948
962
|
function redactValue(value, depth, redactStringFn) {
|
|
949
963
|
if (depth > MAX_DEPTH) return value;
|
|
950
|
-
if (typeof value === "string")
|
|
964
|
+
if (typeof value === "string") {
|
|
965
|
+
return isBase64DataUrl(value) ? value : redactStringFn(value);
|
|
966
|
+
}
|
|
951
967
|
if (Array.isArray(value))
|
|
952
968
|
return value.map((entry) => redactValue(entry, depth + 1, redactStringFn));
|
|
953
969
|
if (value !== null && typeof value === "object") {
|
|
970
|
+
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
|
|
971
|
+
return value;
|
|
972
|
+
}
|
|
973
|
+
const record = value;
|
|
954
974
|
const out = {};
|
|
955
|
-
for (const [key, entry] of Object.entries(
|
|
956
|
-
|
|
975
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
976
|
+
if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" || record.type === "encrypted" || record.type === "redacted")) {
|
|
977
|
+
out[key] = entry;
|
|
978
|
+
} else {
|
|
979
|
+
out[key] = redactValue(entry, depth + 1, redactStringFn);
|
|
980
|
+
}
|
|
957
981
|
}
|
|
958
982
|
return out;
|
|
959
983
|
}
|
|
960
984
|
return value;
|
|
961
985
|
}
|
|
962
986
|
function redactMessage(message, redactStringFn) {
|
|
963
|
-
if (
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
return
|
|
987
|
+
if (message.role === "system") {
|
|
988
|
+
return { ...message, content: redactStringFn(message.content) };
|
|
989
|
+
}
|
|
990
|
+
return {
|
|
991
|
+
...message,
|
|
992
|
+
content: redactMessageContent(message.content, redactStringFn)
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
function redactMessageContent(value, redactStringFn) {
|
|
996
|
+
return redactValue(value, 0, redactStringFn);
|
|
997
|
+
}
|
|
998
|
+
function isBase64DataUrl(value) {
|
|
999
|
+
return /^data:[^;,]+;base64,/i.test(value);
|
|
973
1000
|
}
|
|
974
1001
|
|
|
975
1002
|
// src/tracing.ts
|
|
@@ -982,6 +1009,107 @@ import {
|
|
|
982
1009
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
983
1010
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
984
1011
|
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
1012
|
+
|
|
1013
|
+
// src/capture.ts
|
|
1014
|
+
var DEFAULT_CAPTURE_MAX_BYTES = 262144;
|
|
1015
|
+
var MIN_CAPTURE_MAX_BYTES = 96;
|
|
1016
|
+
function validateCaptureMaxBytes(value) {
|
|
1017
|
+
const resolved = value ?? DEFAULT_CAPTURE_MAX_BYTES;
|
|
1018
|
+
if (!Number.isInteger(resolved) || resolved < MIN_CAPTURE_MAX_BYTES) {
|
|
1019
|
+
throw new TypeError(
|
|
1020
|
+
`Langfuse captureMaxBytes must be an integer of at least ${MIN_CAPTURE_MAX_BYTES}`
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
return resolved;
|
|
1024
|
+
}
|
|
1025
|
+
function sanitizeTraceValue(value, maxBytes) {
|
|
1026
|
+
validateCaptureMaxBytes(maxBytes);
|
|
1027
|
+
const sanitized = sanitizeValue(value, 0, /* @__PURE__ */ new WeakSet());
|
|
1028
|
+
let serialized;
|
|
1029
|
+
try {
|
|
1030
|
+
serialized = JSON.stringify(sanitized) ?? String(sanitized);
|
|
1031
|
+
} catch {
|
|
1032
|
+
return omitted("unserializable");
|
|
1033
|
+
}
|
|
1034
|
+
const originalBytes = utf8Bytes(serialized);
|
|
1035
|
+
if (originalBytes <= maxBytes) {
|
|
1036
|
+
return sanitized;
|
|
1037
|
+
}
|
|
1038
|
+
const preview = boundedPreview(serialized, originalBytes, maxBytes);
|
|
1039
|
+
return {
|
|
1040
|
+
anviaTraceValue: "truncated",
|
|
1041
|
+
originalBytes,
|
|
1042
|
+
preview
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function sanitizeValue(value, depth, seen) {
|
|
1046
|
+
if (depth > 16) {
|
|
1047
|
+
return omitted("depth");
|
|
1048
|
+
}
|
|
1049
|
+
if (typeof value === "string") {
|
|
1050
|
+
if (/^data:[^;,]+;base64,/i.test(value)) {
|
|
1051
|
+
return omitted("base64", utf8Bytes(value));
|
|
1052
|
+
}
|
|
1053
|
+
return value;
|
|
1054
|
+
}
|
|
1055
|
+
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
|
|
1056
|
+
const byteLength = value.byteLength;
|
|
1057
|
+
return omitted("binary", byteLength);
|
|
1058
|
+
}
|
|
1059
|
+
if (value === null || typeof value !== "object") {
|
|
1060
|
+
return value;
|
|
1061
|
+
}
|
|
1062
|
+
if (seen.has(value)) {
|
|
1063
|
+
return omitted("circular");
|
|
1064
|
+
}
|
|
1065
|
+
seen.add(value);
|
|
1066
|
+
if (Array.isArray(value)) {
|
|
1067
|
+
const result2 = value.map((entry) => sanitizeValue(entry, depth + 1, seen));
|
|
1068
|
+
seen.delete(value);
|
|
1069
|
+
return result2;
|
|
1070
|
+
}
|
|
1071
|
+
const record = value;
|
|
1072
|
+
const result = {};
|
|
1073
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
1074
|
+
if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" && typeof record.mediaType === "string")) {
|
|
1075
|
+
result[key] = omitted("base64", utf8Bytes(entry));
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
result[key] = sanitizeValue(entry, depth + 1, seen);
|
|
1079
|
+
}
|
|
1080
|
+
seen.delete(value);
|
|
1081
|
+
return result;
|
|
1082
|
+
}
|
|
1083
|
+
function omitted(reason, originalBytes) {
|
|
1084
|
+
return {
|
|
1085
|
+
anviaTraceValue: "omitted",
|
|
1086
|
+
reason,
|
|
1087
|
+
...originalBytes === void 0 ? {} : { originalBytes }
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function boundedPreview(value, originalBytes, maxBytes) {
|
|
1091
|
+
let low = 0;
|
|
1092
|
+
let high = value.length;
|
|
1093
|
+
while (low < high) {
|
|
1094
|
+
const middle = Math.ceil((low + high) / 2);
|
|
1095
|
+
const candidate = {
|
|
1096
|
+
anviaTraceValue: "truncated",
|
|
1097
|
+
originalBytes,
|
|
1098
|
+
preview: value.slice(0, middle)
|
|
1099
|
+
};
|
|
1100
|
+
if (utf8Bytes(JSON.stringify(candidate)) <= maxBytes) {
|
|
1101
|
+
low = middle;
|
|
1102
|
+
} else {
|
|
1103
|
+
high = middle - 1;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return value.slice(0, low);
|
|
1107
|
+
}
|
|
1108
|
+
function utf8Bytes(value) {
|
|
1109
|
+
return typeof Buffer === "undefined" ? new TextEncoder().encode(value).byteLength : Buffer.byteLength(value, "utf8");
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// src/tracing.ts
|
|
985
1113
|
var langfuse = {
|
|
986
1114
|
create(options = {}) {
|
|
987
1115
|
return new LangfuseAgentObserver(options);
|
|
@@ -1001,6 +1129,8 @@ var LangfuseAgentObserver = class {
|
|
|
1001
1129
|
redactor;
|
|
1002
1130
|
redactInputs;
|
|
1003
1131
|
redactOutputs;
|
|
1132
|
+
captureMode;
|
|
1133
|
+
captureMaxBytes;
|
|
1004
1134
|
constructor(options) {
|
|
1005
1135
|
const resolvedConfig = resolveLangfuseConfig(options);
|
|
1006
1136
|
this[langfuseResolvedConfigSymbol] = resolvedConfig;
|
|
@@ -1041,23 +1171,31 @@ var LangfuseAgentObserver = class {
|
|
|
1041
1171
|
}) : null;
|
|
1042
1172
|
this.redactInputs = options.redactInputs;
|
|
1043
1173
|
this.redactOutputs = options.redactOutputs;
|
|
1174
|
+
this.captureMode = options.captureMode ?? "safe";
|
|
1175
|
+
this.captureMaxBytes = validateCaptureMaxBytes(options.captureMaxBytes);
|
|
1044
1176
|
this.redactor = options.redactInputs !== void 0 || options.redactOutputs !== void 0 ? createPiiRedactor(options.redaction) : void 0;
|
|
1045
1177
|
}
|
|
1046
1178
|
async startRun(args) {
|
|
1047
1179
|
const traceId = args.trace?.traceId;
|
|
1048
|
-
const
|
|
1180
|
+
const capturedInput = this.captureInput({
|
|
1181
|
+
instructions: args.instructions,
|
|
1049
1182
|
prompt: args.prompt,
|
|
1050
1183
|
history: args.history
|
|
1051
1184
|
});
|
|
1185
|
+
const capturedTraceMetadata = this.captureInput(args.trace?.metadata ?? {});
|
|
1052
1186
|
const metadata = {
|
|
1053
1187
|
agentName: args.agentName,
|
|
1054
1188
|
agentDescription: args.agentDescription,
|
|
1055
1189
|
maxTurns: args.maxTurns
|
|
1056
1190
|
};
|
|
1057
1191
|
if (this.serviceName !== void 0) metadata.serviceName = this.serviceName;
|
|
1058
|
-
|
|
1192
|
+
if (isRecord(capturedTraceMetadata)) {
|
|
1193
|
+
Object.assign(metadata, capturedTraceMetadata);
|
|
1194
|
+
} else {
|
|
1195
|
+
metadata.traceMetadata = capturedTraceMetadata;
|
|
1196
|
+
}
|
|
1059
1197
|
const rootAttributes = {
|
|
1060
|
-
input:
|
|
1198
|
+
input: capturedInput,
|
|
1061
1199
|
metadata
|
|
1062
1200
|
};
|
|
1063
1201
|
if (args.trace?.version !== void 0) {
|
|
@@ -1075,7 +1213,7 @@ var LangfuseAgentObserver = class {
|
|
|
1075
1213
|
}
|
|
1076
1214
|
}
|
|
1077
1215
|
);
|
|
1078
|
-
applyTraceAttributes(root, args);
|
|
1216
|
+
applyTraceAttributes(root, args, capturedTraceMetadata);
|
|
1079
1217
|
const promptRef = resolvePromptRef(args);
|
|
1080
1218
|
const runObserver = new LangfuseRunObserver(
|
|
1081
1219
|
root,
|
|
@@ -1087,7 +1225,9 @@ var LangfuseAgentObserver = class {
|
|
|
1087
1225
|
{
|
|
1088
1226
|
redactor: this.redactor,
|
|
1089
1227
|
redactInputs: this.redactInputs,
|
|
1090
|
-
redactOutputs: this.redactOutputs
|
|
1228
|
+
redactOutputs: this.redactOutputs,
|
|
1229
|
+
captureMode: this.captureMode,
|
|
1230
|
+
captureMaxBytes: this.captureMaxBytes
|
|
1091
1231
|
}
|
|
1092
1232
|
);
|
|
1093
1233
|
this.currentHandle = runObserver.getHandle();
|
|
@@ -1118,13 +1258,9 @@ var LangfuseAgentObserver = class {
|
|
|
1118
1258
|
getCurrentTrace() {
|
|
1119
1259
|
return this.currentHandle;
|
|
1120
1260
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
return
|
|
1124
|
-
}
|
|
1125
|
-
maybeRedactOutput(value) {
|
|
1126
|
-
if (this.redactor === void 0 || this.redactOutputs === void 0) return value;
|
|
1127
|
-
return applyRedaction(this.redactor, value, this.redactOutputs);
|
|
1261
|
+
captureInput(value) {
|
|
1262
|
+
const redacted = this.redactor === void 0 || this.redactInputs === void 0 ? value : applyRedaction(this.redactor, value, this.redactInputs);
|
|
1263
|
+
return sanitizeTraceValue(redacted, this.captureMaxBytes);
|
|
1128
1264
|
}
|
|
1129
1265
|
async score(args) {
|
|
1130
1266
|
if (args.traceId === void 0 || args.traceId.length === 0) {
|
|
@@ -1196,7 +1332,7 @@ function buildScoreBody2(score) {
|
|
|
1196
1332
|
}
|
|
1197
1333
|
return body;
|
|
1198
1334
|
}
|
|
1199
|
-
function applyTraceAttributes(root, args) {
|
|
1335
|
+
function applyTraceAttributes(root, args, capturedMetadata) {
|
|
1200
1336
|
const traceName = args.trace?.name ?? args.agentName;
|
|
1201
1337
|
if (traceName !== void 0) {
|
|
1202
1338
|
root.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_NAME, traceName);
|
|
@@ -1210,7 +1346,9 @@ function applyTraceAttributes(root, args) {
|
|
|
1210
1346
|
if (args.trace?.tags !== void 0) {
|
|
1211
1347
|
root.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_TAGS, args.trace.tags);
|
|
1212
1348
|
}
|
|
1213
|
-
for (const [key, value] of Object.entries(
|
|
1349
|
+
for (const [key, value] of Object.entries(
|
|
1350
|
+
isRecord(capturedMetadata) ? capturedMetadata : { value: capturedMetadata }
|
|
1351
|
+
)) {
|
|
1214
1352
|
const serialized = serializeMetadataValue(value);
|
|
1215
1353
|
if (serialized === void 0) {
|
|
1216
1354
|
continue;
|
|
@@ -1238,9 +1376,6 @@ function applyRedaction(redactor, value, mode) {
|
|
|
1238
1376
|
if (typeof value === "string") {
|
|
1239
1377
|
return redactor.redactString(value);
|
|
1240
1378
|
}
|
|
1241
|
-
if (Array.isArray(value)) {
|
|
1242
|
-
return redactor.redactMessages(value);
|
|
1243
|
-
}
|
|
1244
1379
|
if (value !== null && typeof value === "object") {
|
|
1245
1380
|
return redactor.redactObject(value);
|
|
1246
1381
|
}
|
|
@@ -1250,6 +1385,9 @@ function resolvePromptRef(args) {
|
|
|
1250
1385
|
if (args.promptRef !== void 0) {
|
|
1251
1386
|
return args.promptRef;
|
|
1252
1387
|
}
|
|
1388
|
+
if (args.trace?.promptRef !== void 0) {
|
|
1389
|
+
return args.trace.promptRef;
|
|
1390
|
+
}
|
|
1253
1391
|
const metadata = args.trace?.metadata;
|
|
1254
1392
|
if (metadata === void 0) {
|
|
1255
1393
|
return void 0;
|
|
@@ -1291,6 +1429,19 @@ function serializeMetadataValue(value) {
|
|
|
1291
1429
|
return "<failed to serialize>";
|
|
1292
1430
|
}
|
|
1293
1431
|
}
|
|
1432
|
+
function asMetadata(value) {
|
|
1433
|
+
return isRecord(value) ? value : { value };
|
|
1434
|
+
}
|
|
1435
|
+
function eventStartTime(value) {
|
|
1436
|
+
if (value instanceof Date) {
|
|
1437
|
+
return Number.isNaN(value.getTime()) ? void 0 : value;
|
|
1438
|
+
}
|
|
1439
|
+
if (typeof value !== "string") {
|
|
1440
|
+
return void 0;
|
|
1441
|
+
}
|
|
1442
|
+
const parsed = new Date(value);
|
|
1443
|
+
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
1444
|
+
}
|
|
1294
1445
|
var LangfuseRunObserver = class {
|
|
1295
1446
|
constructor(root, trace, promptRef, redaction) {
|
|
1296
1447
|
this.root = root;
|
|
@@ -1300,6 +1451,8 @@ var LangfuseRunObserver = class {
|
|
|
1300
1451
|
this.redactor = redaction.redactor;
|
|
1301
1452
|
this.redactInputs = redaction.redactInputs;
|
|
1302
1453
|
this.redactOutputs = redaction.redactOutputs;
|
|
1454
|
+
this.captureMode = redaction.captureMode;
|
|
1455
|
+
this.captureMaxBytes = redaction.captureMaxBytes;
|
|
1303
1456
|
}
|
|
1304
1457
|
root;
|
|
1305
1458
|
trace;
|
|
@@ -1313,24 +1466,41 @@ var LangfuseRunObserver = class {
|
|
|
1313
1466
|
redactor;
|
|
1314
1467
|
redactInputs;
|
|
1315
1468
|
redactOutputs;
|
|
1469
|
+
captureMode;
|
|
1470
|
+
captureMaxBytes;
|
|
1316
1471
|
redactInputValue(value) {
|
|
1317
|
-
|
|
1318
|
-
return
|
|
1472
|
+
const redacted = this.redactor === void 0 || this.redactInputs === void 0 ? value : applyRedaction(this.redactor, value, this.redactInputs);
|
|
1473
|
+
return sanitizeTraceValue(redacted, this.captureMaxBytes);
|
|
1319
1474
|
}
|
|
1320
1475
|
redactOutputValue(value) {
|
|
1321
|
-
|
|
1322
|
-
return
|
|
1476
|
+
const redacted = this.redactor === void 0 || this.redactOutputs === void 0 ? value : applyRedaction(this.redactor, value, this.redactOutputs);
|
|
1477
|
+
return sanitizeTraceValue(redacted, this.captureMaxBytes);
|
|
1323
1478
|
}
|
|
1324
1479
|
startGeneration(args) {
|
|
1325
1480
|
this.closeEarlierTurns(args.turn);
|
|
1326
1481
|
const turn = this.turnSpan(args.turn);
|
|
1327
|
-
const
|
|
1482
|
+
const safeInput = {
|
|
1483
|
+
instructions: args.request.instructions,
|
|
1484
|
+
messages: modelInputMessages(args.request.chatHistory)
|
|
1485
|
+
};
|
|
1486
|
+
if (this.captureMode === "full") {
|
|
1487
|
+
safeInput.documents = args.request.documents;
|
|
1488
|
+
safeInput.tools = args.request.tools;
|
|
1489
|
+
safeInput.providerTools = args.request.providerTools;
|
|
1490
|
+
safeInput.outputSchema = args.request.outputSchema;
|
|
1491
|
+
safeInput.additionalParams = args.request.additionalParams;
|
|
1492
|
+
}
|
|
1328
1493
|
const metadata = {
|
|
1329
1494
|
turn: args.turn,
|
|
1330
|
-
|
|
1331
|
-
|
|
1495
|
+
documentCount: args.request.documents.length,
|
|
1496
|
+
toolNames: args.request.tools.map((tool) => tool.name),
|
|
1497
|
+
providerToolNames: args.request.providerTools?.map((tool) => tool.name) ?? [],
|
|
1498
|
+
hasOutputSchema: args.request.outputSchema !== void 0,
|
|
1499
|
+
additionalParamKeys: isRecord(args.request.additionalParams) ? Object.keys(args.request.additionalParams) : []
|
|
1332
1500
|
};
|
|
1333
|
-
if (args.providerRequest !== void 0)
|
|
1501
|
+
if (this.captureMode === "full" && args.providerRequest !== void 0) {
|
|
1502
|
+
metadata.providerRequest = args.providerRequest;
|
|
1503
|
+
}
|
|
1334
1504
|
if (args.modelInfo !== void 0) {
|
|
1335
1505
|
const modelInfo = {
|
|
1336
1506
|
provider: args.modelInfo.provider,
|
|
@@ -1342,36 +1512,43 @@ var LangfuseRunObserver = class {
|
|
|
1342
1512
|
metadata.modelInfo = modelInfo;
|
|
1343
1513
|
}
|
|
1344
1514
|
Object.assign(metadata, promptMetadata(this.promptRef));
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1515
|
+
const generationAttributes = {
|
|
1516
|
+
input: this.redactInputValue(safeInput),
|
|
1517
|
+
model: args.request.model ?? args.modelInfo?.defaultModel ?? "default",
|
|
1518
|
+
modelParameters: modelParameters(args.request),
|
|
1519
|
+
metadata: asMetadata(this.redactInputValue(metadata))
|
|
1520
|
+
};
|
|
1521
|
+
if (this.promptRef?.version !== void 0) {
|
|
1522
|
+
generationAttributes.prompt = {
|
|
1523
|
+
name: this.promptRef.name,
|
|
1524
|
+
version: this.promptRef.version,
|
|
1525
|
+
isFallback: false
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
const generation = turn.startObservation(`model.turn.${args.turn}`, generationAttributes, {
|
|
1529
|
+
asType: "generation"
|
|
1530
|
+
});
|
|
1531
|
+
return new LangfuseGenerationObserver(generation, this, /* @__PURE__ */ new Date());
|
|
1356
1532
|
}
|
|
1357
1533
|
startTool(args) {
|
|
1358
1534
|
const turn = this.turnSpan(args.turn);
|
|
1359
|
-
const redactedArgs = this.redactInputValue(args.args);
|
|
1360
1535
|
const metadata = {
|
|
1361
1536
|
turn: args.turn,
|
|
1362
1537
|
internalCallId: args.internalCallId,
|
|
1363
1538
|
toolCallId: args.toolCallId
|
|
1364
1539
|
};
|
|
1365
|
-
if (
|
|
1366
|
-
|
|
1540
|
+
if (this.captureMode === "full") {
|
|
1541
|
+
if (args.toolDefinition !== void 0) metadata.toolDefinition = args.toolDefinition;
|
|
1542
|
+
if (args.toolMetadata !== void 0) metadata.toolMetadata = args.toolMetadata;
|
|
1543
|
+
}
|
|
1367
1544
|
const tool = turn.startObservation(
|
|
1368
1545
|
`tool.${args.toolName}`,
|
|
1369
1546
|
{
|
|
1370
|
-
input: {
|
|
1371
|
-
args:
|
|
1547
|
+
input: this.redactInputValue({
|
|
1548
|
+
args: args.args,
|
|
1372
1549
|
toolCall: args.toolCall
|
|
1373
|
-
},
|
|
1374
|
-
metadata
|
|
1550
|
+
}),
|
|
1551
|
+
metadata: asMetadata(this.redactInputValue(metadata))
|
|
1375
1552
|
},
|
|
1376
1553
|
{ asType: "tool" }
|
|
1377
1554
|
);
|
|
@@ -1380,34 +1557,52 @@ var LangfuseRunObserver = class {
|
|
|
1380
1557
|
end(args) {
|
|
1381
1558
|
this.closeAllTurns();
|
|
1382
1559
|
const redactedOutput = this.redactOutputValue(args.output);
|
|
1560
|
+
const metadata = {
|
|
1561
|
+
usage: args.usage,
|
|
1562
|
+
messageCount: args.messages.length,
|
|
1563
|
+
sources: this.redactOutputValue(args.sources),
|
|
1564
|
+
providerToolCalls: this.redactOutputValue(args.providerToolCalls)
|
|
1565
|
+
};
|
|
1566
|
+
if (this.captureMode === "full") {
|
|
1567
|
+
metadata.messages = this.redactTranscript(args.messages);
|
|
1568
|
+
}
|
|
1383
1569
|
this.root.update({
|
|
1384
1570
|
output: redactedOutput,
|
|
1385
|
-
metadata
|
|
1386
|
-
usage: args.usage,
|
|
1387
|
-
messages: args.messages
|
|
1388
|
-
}
|
|
1571
|
+
metadata
|
|
1389
1572
|
}).end();
|
|
1390
1573
|
this.clearCurrentHandle?.();
|
|
1391
1574
|
}
|
|
1392
1575
|
error(args) {
|
|
1393
1576
|
this.closeAllTurns();
|
|
1577
|
+
const redactedError = this.redactOutputValue(errorMessage(args.error));
|
|
1578
|
+
const metadata = {
|
|
1579
|
+
usage: args.usage,
|
|
1580
|
+
messageCount: args.messages.length
|
|
1581
|
+
};
|
|
1582
|
+
if (this.captureMode === "full") {
|
|
1583
|
+
metadata.messages = this.redactTranscript(args.messages);
|
|
1584
|
+
}
|
|
1394
1585
|
this.root.update({
|
|
1395
1586
|
level: "ERROR",
|
|
1396
|
-
statusMessage:
|
|
1587
|
+
statusMessage: typeof redactedError === "string" ? redactedError : "Agent run failed",
|
|
1397
1588
|
output: {
|
|
1398
|
-
error:
|
|
1589
|
+
error: redactedError
|
|
1399
1590
|
},
|
|
1400
|
-
metadata
|
|
1401
|
-
usage: args.usage,
|
|
1402
|
-
messages: args.messages
|
|
1403
|
-
}
|
|
1591
|
+
metadata
|
|
1404
1592
|
}).end();
|
|
1405
1593
|
this.clearCurrentHandle?.();
|
|
1406
1594
|
}
|
|
1407
1595
|
event(args) {
|
|
1408
|
-
const metadata = {};
|
|
1409
|
-
|
|
1410
|
-
|
|
1596
|
+
const metadata = asMetadata(this.redactOutputValue(args.attributes ?? {}));
|
|
1597
|
+
const attributes = { metadata };
|
|
1598
|
+
if (args.level !== void 0) {
|
|
1599
|
+
attributes.level = args.level;
|
|
1600
|
+
}
|
|
1601
|
+
const startTime = eventStartTime(args.timestamp);
|
|
1602
|
+
this.root.startObservation(args.name, attributes, {
|
|
1603
|
+
asType: "event",
|
|
1604
|
+
...startTime === void 0 ? {} : { startTime }
|
|
1605
|
+
});
|
|
1411
1606
|
}
|
|
1412
1607
|
getHandle() {
|
|
1413
1608
|
return this.handle;
|
|
@@ -1417,13 +1612,15 @@ var LangfuseRunObserver = class {
|
|
|
1417
1612
|
traceId: this.trace.traceId ?? "",
|
|
1418
1613
|
observationId: this.trace.observationId ?? "",
|
|
1419
1614
|
addAttributes: (attributes) => {
|
|
1420
|
-
this.root.update({ metadata: attributes });
|
|
1615
|
+
this.root.update({ metadata: asMetadata(this.redactOutputValue(attributes)) });
|
|
1421
1616
|
this.setCurrentHandle?.(this.handle);
|
|
1422
1617
|
},
|
|
1423
1618
|
addEvent: (name, attributes) => {
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1619
|
+
this.root.startObservation(
|
|
1620
|
+
name,
|
|
1621
|
+
{ metadata: asMetadata(this.redactOutputValue(attributes ?? {})) },
|
|
1622
|
+
{ asType: "event" }
|
|
1623
|
+
);
|
|
1427
1624
|
this.setCurrentHandle?.(this.handle);
|
|
1428
1625
|
}
|
|
1429
1626
|
};
|
|
@@ -1457,17 +1654,27 @@ var LangfuseRunObserver = class {
|
|
|
1457
1654
|
}
|
|
1458
1655
|
this.turnSpans.clear();
|
|
1459
1656
|
}
|
|
1657
|
+
isFullCapture() {
|
|
1658
|
+
return this.captureMode === "full";
|
|
1659
|
+
}
|
|
1660
|
+
redactTranscript(messages) {
|
|
1661
|
+
const inputRedacted = this.redactor === void 0 || this.redactInputs === void 0 ? messages : applyRedaction(this.redactor, messages, this.redactInputs);
|
|
1662
|
+
const outputRedacted = this.redactor === void 0 || this.redactOutputs === void 0 ? inputRedacted : applyRedaction(this.redactor, inputRedacted, this.redactOutputs);
|
|
1663
|
+
return sanitizeTraceValue(outputRedacted, this.captureMaxBytes);
|
|
1664
|
+
}
|
|
1460
1665
|
};
|
|
1461
1666
|
var LangfuseGenerationObserver = class {
|
|
1462
|
-
constructor(generation, run) {
|
|
1667
|
+
constructor(generation, run, startedAt) {
|
|
1463
1668
|
this.generation = generation;
|
|
1464
1669
|
this.run = run;
|
|
1670
|
+
this.startedAt = startedAt;
|
|
1465
1671
|
}
|
|
1466
1672
|
generation;
|
|
1467
1673
|
run;
|
|
1674
|
+
startedAt;
|
|
1468
1675
|
update(args) {
|
|
1469
1676
|
this.generation.update({
|
|
1470
|
-
output: { delta: args.delta }
|
|
1677
|
+
output: this.run.redactOutputValue({ delta: args.delta })
|
|
1471
1678
|
});
|
|
1472
1679
|
}
|
|
1473
1680
|
end(args) {
|
|
@@ -1475,21 +1682,30 @@ var LangfuseGenerationObserver = class {
|
|
|
1475
1682
|
const redactedChoice = this.run.redactOutputValue(args.response.choice);
|
|
1476
1683
|
const metadata = { turn: args.turn };
|
|
1477
1684
|
if (args.firstDeltaMs !== void 0) metadata.firstDeltaMs = args.firstDeltaMs;
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1685
|
+
const output = {
|
|
1686
|
+
messageId: args.response.messageId,
|
|
1687
|
+
content: redactedChoice,
|
|
1688
|
+
text: redactedText,
|
|
1689
|
+
sources: this.run.redactOutputValue(args.response.sources),
|
|
1690
|
+
providerToolCalls: this.run.redactOutputValue(args.response.providerToolCalls)
|
|
1691
|
+
};
|
|
1692
|
+
const completionStartTime = args.firstDeltaMs === void 0 ? void 0 : new Date(this.startedAt.getTime() + args.firstDeltaMs);
|
|
1693
|
+
const update = {
|
|
1694
|
+
output,
|
|
1484
1695
|
usageDetails: usageDetails(args.response.usage),
|
|
1485
1696
|
metadata
|
|
1486
|
-
}
|
|
1697
|
+
};
|
|
1698
|
+
if (completionStartTime !== void 0) {
|
|
1699
|
+
update.completionStartTime = completionStartTime;
|
|
1700
|
+
}
|
|
1701
|
+
this.generation.update(update).end();
|
|
1487
1702
|
}
|
|
1488
1703
|
error(args) {
|
|
1704
|
+
const redactedError = this.run.redactOutputValue(errorMessage(args.error));
|
|
1489
1705
|
this.generation.update({
|
|
1490
1706
|
level: "ERROR",
|
|
1491
|
-
statusMessage:
|
|
1492
|
-
output: { error:
|
|
1707
|
+
statusMessage: typeof redactedError === "string" ? redactedError : "Generation failed",
|
|
1708
|
+
output: { error: redactedError },
|
|
1493
1709
|
metadata: { turn: args.turn }
|
|
1494
1710
|
}).end();
|
|
1495
1711
|
}
|
|
@@ -1515,36 +1731,129 @@ var LangfuseToolObserver = class {
|
|
|
1515
1731
|
const childTurn = typeof child.turn === "number" ? child.turn : args.turn;
|
|
1516
1732
|
const agent = this.childAgent(agentId, agentName, args);
|
|
1517
1733
|
if (child.type === "turn_start") {
|
|
1734
|
+
const promptMessage = isRecord(child.prompt) ? child.prompt : void 0;
|
|
1735
|
+
const historyMessages = Array.isArray(child.history) ? child.history.filter(isRecord) : [];
|
|
1736
|
+
agent.startObservation(
|
|
1737
|
+
`${agentLabel(agentId, agentName)}.turn.${childTurn}.start`,
|
|
1738
|
+
{
|
|
1739
|
+
input: this.run.redactInputValue({
|
|
1740
|
+
prompt: promptMessage === void 0 ? void 0 : modelInputMessage(promptMessage),
|
|
1741
|
+
history: modelInputMessages(historyMessages)
|
|
1742
|
+
}),
|
|
1743
|
+
metadata: asMetadata(
|
|
1744
|
+
this.run.redactInputValue(childMetadata(args, agentId, agentName, childTurn))
|
|
1745
|
+
)
|
|
1746
|
+
},
|
|
1747
|
+
{ asType: "event" }
|
|
1748
|
+
);
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
if (child.type === "generation_start" && isRecord(child.request)) {
|
|
1752
|
+
const request = child.request;
|
|
1753
|
+
const modelInfo = isRecord(child.modelInfo) ? child.modelInfo : void 0;
|
|
1754
|
+
const messages = Array.isArray(request.chatHistory) ? modelInputMessages(request.chatHistory.filter(isRecord)) : [];
|
|
1755
|
+
const input = {
|
|
1756
|
+
instructions: typeof request.instructions === "string" ? request.instructions : void 0,
|
|
1757
|
+
messages
|
|
1758
|
+
};
|
|
1759
|
+
if (this.run.isFullCapture()) {
|
|
1760
|
+
input.documents = request.documents;
|
|
1761
|
+
input.tools = request.tools;
|
|
1762
|
+
input.providerTools = request.providerTools;
|
|
1763
|
+
input.outputSchema = request.outputSchema;
|
|
1764
|
+
input.additionalParams = request.additionalParams;
|
|
1765
|
+
}
|
|
1766
|
+
const toolNames = Array.isArray(request.tools) ? request.tools.filter(isRecord).map((tool) => tool.name).filter((name) => typeof name === "string") : [];
|
|
1767
|
+
const providerToolNames = Array.isArray(request.providerTools) ? request.providerTools.filter(isRecord).map((tool) => tool.name).filter((name) => typeof name === "string") : [];
|
|
1518
1768
|
const generation = agent.startObservation(
|
|
1519
1769
|
`${agentLabel(agentId, agentName)}.model.turn.${childTurn}`,
|
|
1520
1770
|
{
|
|
1521
|
-
input:
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1771
|
+
input: this.run.redactInputValue(input),
|
|
1772
|
+
model: typeof request.model === "string" ? request.model : typeof modelInfo?.defaultModel === "string" ? modelInfo.defaultModel : "default",
|
|
1773
|
+
modelParameters: modelParameters(request),
|
|
1774
|
+
metadata: asMetadata(
|
|
1775
|
+
this.run.redactInputValue({
|
|
1776
|
+
...childMetadata(args, agentId, agentName, childTurn),
|
|
1777
|
+
documentCount: Array.isArray(request.documents) ? request.documents.length : 0,
|
|
1778
|
+
toolNames,
|
|
1779
|
+
providerToolNames,
|
|
1780
|
+
hasOutputSchema: request.outputSchema !== void 0,
|
|
1781
|
+
modelInfo
|
|
1782
|
+
})
|
|
1783
|
+
)
|
|
1526
1784
|
},
|
|
1527
1785
|
{ asType: "generation" }
|
|
1528
1786
|
);
|
|
1529
|
-
this.childGenerations.set(generationKey(agentId, childTurn),
|
|
1787
|
+
this.childGenerations.set(generationKey(agentId, childTurn), {
|
|
1788
|
+
generation,
|
|
1789
|
+
startedAt: /* @__PURE__ */ new Date()
|
|
1790
|
+
});
|
|
1530
1791
|
return;
|
|
1531
1792
|
}
|
|
1532
1793
|
if (child.type === "turn_end") {
|
|
1533
|
-
const
|
|
1534
|
-
if (
|
|
1794
|
+
const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
|
|
1795
|
+
if (childGeneration !== void 0) {
|
|
1535
1796
|
const update = {
|
|
1536
|
-
output: child.response,
|
|
1537
|
-
metadata:
|
|
1797
|
+
output: this.run.redactOutputValue(child.response),
|
|
1798
|
+
metadata: asMetadata(
|
|
1799
|
+
this.run.redactOutputValue(childMetadata(args, agentId, agentName, childTurn))
|
|
1800
|
+
)
|
|
1538
1801
|
};
|
|
1539
1802
|
if (isRecord(child.response) && isRecord(child.response.usage)) {
|
|
1540
1803
|
update.usageDetails = usageDetailsFromRecord(child.response.usage);
|
|
1541
1804
|
}
|
|
1542
|
-
|
|
1805
|
+
if (typeof child.firstDeltaMs === "number") {
|
|
1806
|
+
update.completionStartTime = new Date(
|
|
1807
|
+
childGeneration.startedAt.getTime() + child.firstDeltaMs
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1810
|
+
childGeneration.generation.update(update).end();
|
|
1543
1811
|
this.childGenerations.delete(generationKey(agentId, childTurn));
|
|
1544
1812
|
}
|
|
1545
1813
|
return;
|
|
1546
1814
|
}
|
|
1815
|
+
if (child.type === "text_delta" || child.type === "reasoning_delta" || child.type === "tool_call_delta") {
|
|
1816
|
+
const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
|
|
1817
|
+
childGeneration?.generation.update({
|
|
1818
|
+
output: this.run.redactOutputValue({ delta: child })
|
|
1819
|
+
});
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
if (child.type === "source" || child.type === "provider_tool_call") {
|
|
1823
|
+
const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
|
|
1824
|
+
const parent = childGeneration?.generation ?? agent;
|
|
1825
|
+
parent.startObservation(
|
|
1826
|
+
`${agentLabel(agentId, agentName)}.${child.type}`,
|
|
1827
|
+
{
|
|
1828
|
+
output: this.run.redactOutputValue(
|
|
1829
|
+
child.type === "source" ? child.source : child.toolCall
|
|
1830
|
+
),
|
|
1831
|
+
metadata: asMetadata(
|
|
1832
|
+
this.run.redactOutputValue(childMetadata(args, agentId, agentName, childTurn))
|
|
1833
|
+
)
|
|
1834
|
+
},
|
|
1835
|
+
{ asType: "event" }
|
|
1836
|
+
);
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
if (child.type === "guardrail_decision") {
|
|
1840
|
+
agent.startObservation(
|
|
1841
|
+
`${agentLabel(agentId, agentName)}.guardrail`,
|
|
1842
|
+
{
|
|
1843
|
+
output: this.run.redactOutputValue(child.decision),
|
|
1844
|
+
metadata: asMetadata(
|
|
1845
|
+
this.run.redactOutputValue(childMetadata(args, agentId, agentName, childTurn))
|
|
1846
|
+
)
|
|
1847
|
+
},
|
|
1848
|
+
{ asType: "guardrail" }
|
|
1849
|
+
).end();
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1547
1852
|
if (child.type === "tool_call" && isRecord(child.toolCall)) {
|
|
1853
|
+
const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
|
|
1854
|
+
childGeneration?.generation.update({
|
|
1855
|
+
output: this.run.redactOutputValue({ toolCall: child.toolCall })
|
|
1856
|
+
});
|
|
1548
1857
|
const toolCall = child.toolCall;
|
|
1549
1858
|
const toolCallFunction = isRecord(toolCall.function) ? toolCall.function : void 0;
|
|
1550
1859
|
const toolName = typeof toolCallFunction?.name === "string" ? toolCallFunction.name : "tool";
|
|
@@ -1552,15 +1861,17 @@ var LangfuseToolObserver = class {
|
|
|
1552
1861
|
const childTool = agent.startObservation(
|
|
1553
1862
|
`${agentLabel(agentId, agentName)}.${toolName}`,
|
|
1554
1863
|
{
|
|
1555
|
-
input: {
|
|
1864
|
+
input: this.run.redactInputValue({
|
|
1556
1865
|
args: toolCallFunction?.arguments ?? {},
|
|
1557
1866
|
toolCall
|
|
1558
|
-
},
|
|
1559
|
-
metadata:
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1867
|
+
}),
|
|
1868
|
+
metadata: asMetadata(
|
|
1869
|
+
this.run.redactInputValue({
|
|
1870
|
+
...childMetadata(args, agentId, agentName, childTurn),
|
|
1871
|
+
toolName,
|
|
1872
|
+
toolCallId
|
|
1873
|
+
})
|
|
1874
|
+
)
|
|
1564
1875
|
},
|
|
1565
1876
|
{ asType: "tool" }
|
|
1566
1877
|
);
|
|
@@ -1581,30 +1892,49 @@ var LangfuseToolObserver = class {
|
|
|
1581
1892
|
if (childTool !== void 0) {
|
|
1582
1893
|
childTool.ended = true;
|
|
1583
1894
|
childTool.tool.update({
|
|
1584
|
-
output:
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1895
|
+
output: this.run.redactOutputValue(
|
|
1896
|
+
typeof child.result === "string" ? child.result : child
|
|
1897
|
+
),
|
|
1898
|
+
metadata: asMetadata(
|
|
1899
|
+
this.run.redactInputValue({
|
|
1900
|
+
...childMetadata(args, agentId, agentName, childTurn),
|
|
1901
|
+
toolName,
|
|
1902
|
+
toolCallId,
|
|
1903
|
+
internalCallId: typeof child.internalCallId === "string" ? child.internalCallId : void 0,
|
|
1904
|
+
args: typeof child.args === "string" ? child.args : void 0
|
|
1905
|
+
})
|
|
1906
|
+
)
|
|
1592
1907
|
}).end();
|
|
1593
1908
|
}
|
|
1594
1909
|
return;
|
|
1595
1910
|
}
|
|
1596
1911
|
if (child.type === "final") {
|
|
1597
|
-
const update = {
|
|
1598
|
-
|
|
1912
|
+
const update = {
|
|
1913
|
+
output: this.run.redactOutputValue(child.output)
|
|
1914
|
+
};
|
|
1915
|
+
const metadata = {};
|
|
1916
|
+
if (isRecord(child.usage)) metadata.usage = child.usage;
|
|
1917
|
+
if (this.run.isFullCapture() && Array.isArray(child.messages)) {
|
|
1918
|
+
metadata.messages = this.run.redactTranscript(child.messages);
|
|
1919
|
+
}
|
|
1920
|
+
if (Object.keys(metadata).length > 0) update.metadata = metadata;
|
|
1599
1921
|
agent.update(update).end();
|
|
1600
1922
|
this.childAgents.delete(agentId);
|
|
1601
1923
|
return;
|
|
1602
1924
|
}
|
|
1603
1925
|
if (child.type === "error") {
|
|
1926
|
+
const redactedError = this.run.redactOutputValue(errorMessage(child.error));
|
|
1927
|
+
const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
|
|
1928
|
+
childGeneration?.generation.update({
|
|
1929
|
+
level: "ERROR",
|
|
1930
|
+
statusMessage: typeof redactedError === "string" ? redactedError : "Generation failed",
|
|
1931
|
+
output: { error: redactedError }
|
|
1932
|
+
}).end();
|
|
1933
|
+
this.childGenerations.delete(generationKey(agentId, childTurn));
|
|
1604
1934
|
const update = {
|
|
1605
1935
|
level: "ERROR",
|
|
1606
|
-
statusMessage:
|
|
1607
|
-
output: { error:
|
|
1936
|
+
statusMessage: typeof redactedError === "string" ? redactedError : "Child agent failed",
|
|
1937
|
+
output: { error: redactedError }
|
|
1608
1938
|
};
|
|
1609
1939
|
if (isRecord(child.usage)) update.metadata = { usage: child.usage };
|
|
1610
1940
|
agent.update(update).end();
|
|
@@ -1634,10 +1964,11 @@ var LangfuseToolObserver = class {
|
|
|
1634
1964
|
}
|
|
1635
1965
|
error(args) {
|
|
1636
1966
|
this.endOpenChildren();
|
|
1967
|
+
const redactedError = this.run.redactOutputValue(errorMessage(args.error));
|
|
1637
1968
|
this.tool.update({
|
|
1638
1969
|
level: "ERROR",
|
|
1639
|
-
statusMessage:
|
|
1640
|
-
output: { error:
|
|
1970
|
+
statusMessage: typeof redactedError === "string" ? redactedError : "Tool failed",
|
|
1971
|
+
output: { error: redactedError },
|
|
1641
1972
|
metadata: {
|
|
1642
1973
|
turn: args.turn,
|
|
1643
1974
|
internalCallId: args.internalCallId,
|
|
@@ -1653,7 +1984,9 @@ var LangfuseToolObserver = class {
|
|
|
1653
1984
|
const agent = this.tool.startObservation(
|
|
1654
1985
|
`${agentLabel(agentId, agentName)}.run`,
|
|
1655
1986
|
{
|
|
1656
|
-
metadata:
|
|
1987
|
+
metadata: asMetadata(
|
|
1988
|
+
this.run.redactInputValue(childMetadata(args, agentId, agentName, args.turn))
|
|
1989
|
+
)
|
|
1657
1990
|
},
|
|
1658
1991
|
{ asType: "agent" }
|
|
1659
1992
|
);
|
|
@@ -1674,7 +2007,7 @@ var LangfuseToolObserver = class {
|
|
|
1674
2007
|
}
|
|
1675
2008
|
endOpenChildren() {
|
|
1676
2009
|
for (const generation of this.childGenerations.values()) {
|
|
1677
|
-
generation.end();
|
|
2010
|
+
generation.generation.end();
|
|
1678
2011
|
}
|
|
1679
2012
|
this.childGenerations.clear();
|
|
1680
2013
|
for (const tool of this.childTools) {
|