@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/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
- inputTokens: usage.inputTokens,
25
- outputTokens: usage.outputTokens,
26
- totalTokens: usage.totalTokens,
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
- inputTokens: numberValue(usage.inputTokens) ?? 0,
34
- outputTokens: numberValue(usage.outputTokens) ?? 0,
35
- totalTokens: numberValue(usage.totalTokens) ?? (numberValue(usage.inputTokens) ?? 0) + (numberValue(usage.outputTokens) ?? 0),
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
- void this.flush();
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
- this.flushing = this.sendBatch(batch).finally(() => {
189
- this.flushing = null;
190
- });
191
- await this.flushing;
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
- await this.flush();
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
- void this.flush();
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 LangfuseScoreError(
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 = traceFromEvalReport(args);
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 scoreValue = buildScoreValue(args.outcome, args.metric.dataType);
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: scoreValue
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 (comment !== void 0) score.comment = comment;
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 suite = await runEvalSuite(evalOptions);
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
- if (testCase.metadata !== void 0) {
669
- item.metadata = testCase.metadata;
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") return redactStringFn(value);
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(value)) {
956
- out[key] = redactValue(entry, depth + 1, redactStringFn);
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 (!Array.isArray(message.content)) return message;
964
- const newContent = message.content.map((part) => {
965
- if (part === null || typeof part !== "object") return part;
966
- const rec = part;
967
- if (rec.type === "text" && typeof rec.text === "string") {
968
- return { ...part, text: redactStringFn(rec.text) };
969
- }
970
- return part;
971
- });
972
- return { ...message, content: newContent };
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 redactedInput = this.maybeRedactInput({
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
- Object.assign(metadata, args.trace?.metadata);
1192
+ if (isRecord(capturedTraceMetadata)) {
1193
+ Object.assign(metadata, capturedTraceMetadata);
1194
+ } else {
1195
+ metadata.traceMetadata = capturedTraceMetadata;
1196
+ }
1059
1197
  const rootAttributes = {
1060
- input: redactedInput,
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
- maybeRedactInput(value) {
1122
- if (this.redactor === void 0 || this.redactInputs === void 0) return value;
1123
- return applyRedaction(this.redactor, value, this.redactInputs);
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(args.trace?.metadata ?? {})) {
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
- if (this.redactor === void 0 || this.redactInputs === void 0) return value;
1318
- return applyRedaction(this.redactor, value, this.redactInputs);
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
- if (this.redactor === void 0 || this.redactOutputs === void 0) return value;
1322
- return applyRedaction(this.redactor, value, this.redactOutputs);
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 redactedChatHistory = this.redactInputValue(modelInputMessages(args.request.chatHistory));
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
- toolCount: args.request.tools.length,
1331
- hasOutputSchema: args.request.outputSchema !== void 0
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) metadata.providerRequest = args.providerRequest;
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 generation = turn.startObservation(
1346
- `model.turn.${args.turn}`,
1347
- {
1348
- input: redactedChatHistory,
1349
- model: args.request.model ?? "default",
1350
- modelParameters: modelParameters(args.request),
1351
- metadata
1352
- },
1353
- { asType: "generation" }
1354
- );
1355
- return new LangfuseGenerationObserver(generation, this);
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 (args.toolDefinition !== void 0) metadata.toolDefinition = args.toolDefinition;
1366
- if (args.toolMetadata !== void 0) metadata.toolMetadata = args.toolMetadata;
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: redactedArgs,
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: errorMessage(args.error),
1587
+ statusMessage: typeof redactedError === "string" ? redactedError : "Agent run failed",
1397
1588
  output: {
1398
- error: errorMessage(args.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
- Object.assign(metadata, args.attributes);
1410
- this.root.startObservation(args.name, { metadata }, { asType: "event" }).end();
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
- const metadata = {};
1425
- Object.assign(metadata, attributes);
1426
- this.root.startObservation(name, { metadata }, { asType: "event" }).end();
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
- this.generation.update({
1479
- output: {
1480
- messageId: args.response.messageId,
1481
- content: redactedChoice,
1482
- text: redactedText
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
- }).end();
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: errorMessage(args.error),
1492
- output: { error: errorMessage(args.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
- prompt: modelInputMessage(child.prompt),
1523
- history: modelInputMessages(child.history)
1524
- },
1525
- metadata: childMetadata(args, agentId, agentName, childTurn)
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), generation);
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 generation = this.childGenerations.get(generationKey(agentId, childTurn));
1534
- if (generation !== void 0) {
1794
+ const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
1795
+ if (childGeneration !== void 0) {
1535
1796
  const update = {
1536
- output: child.response,
1537
- metadata: childMetadata(args, agentId, agentName, childTurn)
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
- generation.update(update).end();
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
- ...childMetadata(args, agentId, agentName, childTurn),
1561
- toolName,
1562
- toolCallId
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: typeof child.result === "string" ? child.result : child,
1585
- metadata: {
1586
- ...childMetadata(args, agentId, agentName, childTurn),
1587
- toolName,
1588
- toolCallId,
1589
- internalCallId: typeof child.internalCallId === "string" ? child.internalCallId : void 0,
1590
- args: typeof child.args === "string" ? child.args : void 0
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 = { output: child.output };
1598
- if (isRecord(child.usage)) update.metadata = { usage: child.usage };
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: errorMessage(child.error),
1607
- output: { error: errorMessage(child.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: errorMessage(args.error),
1640
- output: { error: errorMessage(args.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: childMetadata(args, agentId, agentName, args.turn)
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) {