@anvia/langfuse 1.0.0-rc.1 → 1.0.0-rc.2

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
@@ -1,116 +1,155 @@
1
- // src/helpers.ts
2
- function modelInputMessage(message) {
3
- const { metadata: _metadata, ...result } = message;
4
- return result;
5
- }
6
- function modelInputMessages(messages) {
7
- return messages.map(modelInputMessage);
8
- }
9
- function modelParameters(request) {
10
- const params = {};
11
- if (request.temperature !== void 0) params.temperature = request.temperature;
12
- if (request.maxTokens !== void 0) params.maxTokens = request.maxTokens;
13
- if (request.toolChoice !== void 0) {
14
- params.toolChoice = typeof request.toolChoice === "string" ? request.toolChoice : request.toolChoice.name;
1
+ // src/redaction.ts
2
+ var DEFAULT_REPLACEMENT = "[REDACTED]";
3
+ var MAX_DEPTH = 16;
4
+ function createPiiRedactor(options = {}) {
5
+ const patterns = options.patterns ?? DEFAULT_PATTERNS;
6
+ const replacement = options.replacement ?? DEFAULT_REPLACEMENT;
7
+ const compiled = patterns.map((p) => ({
8
+ name: p.name,
9
+ regex: cloneRegex(p.regex, "g")
10
+ }));
11
+ const patternNamesList = patterns.map((p) => p.name);
12
+ function redactString(input) {
13
+ if (typeof input !== "string") return input;
14
+ let out = input;
15
+ for (const { name, regex } of compiled) {
16
+ out = applyPattern(out, name, regex, replacement);
17
+ }
18
+ return out;
15
19
  }
16
- return params;
20
+ function redactObject(input) {
21
+ return redactValue(input, 0, redactString);
22
+ }
23
+ function redactMessages(input) {
24
+ return input.map((message) => redactMessage(message, redactString));
25
+ }
26
+ function patternNames() {
27
+ return patternNamesList;
28
+ }
29
+ return { redactString, redactObject, redactMessages, patternNames };
17
30
  }
18
- function usageDetails(usage) {
19
- if (usage.details !== void 0 && Object.keys(usage.details).length > 0) {
20
- return { ...usage.details };
31
+ var DEFAULT_PATTERNS = [
32
+ { name: "email", regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g },
33
+ { name: "creditCard", regex: /\b(?:\d[ -]?){13,19}\b/g },
34
+ { name: "ipv4", regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g },
35
+ {
36
+ name: "phone",
37
+ regex: /(?<!\d)(?:\+\d{1,3}[\s.-]?)?(?:\(\d{2,4}\)[\s.-]?)?\d{3,4}[\s.-]?\d{3,4}(?:[\s.-]?\d{3,4})?(?!\d)/g
38
+ },
39
+ { name: "jwt", regex: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g },
40
+ {
41
+ name: "apiKey",
42
+ regex: /\b(?:sk|pk|api|key|token)[-_][A-Za-z0-9]{16,}\b/gi
21
43
  }
22
- return {
23
- input: usage.inputTokens,
24
- output: usage.outputTokens,
25
- total: usage.totalTokens
26
- };
44
+ ];
45
+ function applyPattern(input, name, regex, replacement) {
46
+ if (name === "creditCard") {
47
+ return redactCreditCards(input, replacement);
48
+ }
49
+ return input.replace(regex, replacement);
27
50
  }
28
- function usageDetailsFromRecord(usage) {
29
- if (isRecord(usage.details)) {
30
- const details = Object.fromEntries(
31
- Object.entries(usage.details).filter(
32
- (entry) => typeof entry[1] === "number" && Number.isFinite(entry[1]) && entry[1] >= 0
33
- )
34
- );
35
- if (Object.keys(details).length > 0) {
36
- return details;
51
+ function redactCreditCards(input, replacement) {
52
+ let out = "";
53
+ let i = 0;
54
+ while (i < input.length) {
55
+ const ch = input.charAt(i);
56
+ if (/\d/.test(ch)) {
57
+ const { length, valid } = longestLuhnChunk(input.slice(i));
58
+ if (valid) {
59
+ out += replacement;
60
+ i += length;
61
+ continue;
62
+ }
37
63
  }
64
+ out += ch;
65
+ i += 1;
38
66
  }
39
- return {
40
- input: numberValue(usage.inputTokens) ?? 0,
41
- output: numberValue(usage.outputTokens) ?? 0,
42
- total: numberValue(usage.totalTokens) ?? (numberValue(usage.inputTokens) ?? 0) + (numberValue(usage.outputTokens) ?? 0)
43
- };
44
- }
45
- function childMetadata(args, agentId, agentName, childTurn) {
46
- return {
47
- source: "agent_tool_event",
48
- childAgentId: agentId,
49
- childAgentName: agentName,
50
- childTurn,
51
- parentToolName: args.toolName,
52
- parentInternalCallId: args.internalCallId,
53
- parentToolCallId: args.toolCallId
54
- };
55
- }
56
- function generationKey(agentId, turn) {
57
- return `${agentId}:${turn}`;
67
+ return out;
58
68
  }
59
- function agentLabel(agentId, agentName) {
60
- return (agentName ?? agentId).replaceAll(/\s+/g, "_");
69
+ function longestLuhnChunk(s) {
70
+ let length = 0;
71
+ let bestValid = 0;
72
+ while (length < s.length && length < 40) {
73
+ const ch = s.charAt(length);
74
+ if (!/\d/.test(ch) && ch !== "-") break;
75
+ length += 1;
76
+ const candidate = s.slice(0, length).replace(/\D/g, "");
77
+ if (candidate.length >= 13 && candidate.length <= 19) {
78
+ if (startsWithKnownPrefix(candidate) && passesLuhn(candidate)) {
79
+ bestValid = length;
80
+ }
81
+ }
82
+ }
83
+ return { length: bestValid, valid: bestValid > 0 };
61
84
  }
62
- function isRecord(value) {
63
- return typeof value === "object" && value !== null && !Array.isArray(value);
85
+ function startsWithKnownPrefix(digits) {
86
+ if (digits.startsWith("4")) return true;
87
+ const two = digits.slice(0, 2);
88
+ if (two === "51" || two === "52" || two === "53" || two === "54" || two === "55") return true;
89
+ const four = digits.slice(0, 4);
90
+ if (four === "2221" || four === "2720") return true;
91
+ const twoAgain = digits.slice(0, 2);
92
+ if (twoAgain === "34" || twoAgain === "37") return true;
93
+ if (four === "6011" || twoAgain === "65") return true;
94
+ return digits.startsWith("35");
64
95
  }
65
- function numberValue(value) {
66
- return typeof value === "number" ? value : void 0;
96
+ function passesLuhn(digits) {
97
+ if (!/^\d+$/.test(digits)) return false;
98
+ let sum = 0;
99
+ let alt = false;
100
+ for (let i = digits.length - 1; i >= 0; i -= 1) {
101
+ const raw = digits.charCodeAt(i) - 48;
102
+ let value = raw;
103
+ if (alt) {
104
+ value *= 2;
105
+ if (value > 9) value -= 9;
106
+ }
107
+ sum += value;
108
+ alt = !alt;
109
+ }
110
+ return sum % 10 === 0;
67
111
  }
68
- function emptyToUndefined(value) {
69
- return value === void 0 || value.length === 0 ? void 0 : value;
112
+ function cloneRegex(source, flags) {
113
+ return new RegExp(source.source, flags + source.flags.replace(/g/g, ""));
70
114
  }
71
- function errorMessage(error) {
72
- return error instanceof Error ? error.message : String(error);
115
+ function redactValue(value, depth, redactStringFn) {
116
+ if (depth > MAX_DEPTH) return value;
117
+ if (typeof value === "string") {
118
+ return isBase64DataUrl(value) ? value : redactStringFn(value);
119
+ }
120
+ if (Array.isArray(value))
121
+ return value.map((entry) => redactValue(entry, depth + 1, redactStringFn));
122
+ if (value !== null && typeof value === "object") {
123
+ if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
124
+ return value;
125
+ }
126
+ const record = value;
127
+ const out = {};
128
+ for (const [key, entry] of Object.entries(record)) {
129
+ if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" || record.type === "encrypted" || record.type === "redacted")) {
130
+ out[key] = entry;
131
+ } else {
132
+ out[key] = redactValue(entry, depth + 1, redactStringFn);
133
+ }
134
+ }
135
+ return out;
136
+ }
137
+ return value;
73
138
  }
74
-
75
- // src/config.ts
76
- var langfuseResolvedConfigSymbol = /* @__PURE__ */ Symbol.for(
77
- "@anvia/langfuse.resolvedConfig"
78
- );
79
- function resolveLangfuseConfig(options = {}, fallback) {
139
+ function redactMessage(message, redactStringFn) {
140
+ if (message.role === "system") {
141
+ return { ...message, content: redactStringFn(message.content) };
142
+ }
80
143
  return {
81
- publicKey: resolveStringOption(
82
- options.publicKey,
83
- fallback?.publicKey,
84
- process.env.LANGFUSE_PUBLIC_KEY
85
- ),
86
- secretKey: resolveStringOption(
87
- options.secretKey,
88
- fallback?.secretKey,
89
- process.env.LANGFUSE_SECRET_KEY
90
- ),
91
- baseUrl: resolveStringOption(options.baseUrl, fallback?.baseUrl, process.env.LANGFUSE_BASE_URL) ?? "https://cloud.langfuse.com",
92
- environment: resolveStringOption(
93
- options.environment,
94
- fallback?.environment,
95
- process.env.LANGFUSE_TRACING_ENVIRONMENT
96
- ),
97
- release: resolveStringOption(options.release, fallback?.release, process.env.LANGFUSE_RELEASE),
98
- serviceName: resolveStringOption(
99
- options.serviceName,
100
- fallback?.serviceName,
101
- process.env.LANGFUSE_SERVICE_NAME
102
- ),
103
- timeoutMs: options.timeoutMs ?? fallback?.timeoutMs ?? 3e4
144
+ ...message,
145
+ content: redactMessageContent(message.content, redactStringFn)
104
146
  };
105
147
  }
106
- function getResolvedLangfuseConfig(value) {
107
- if (typeof value !== "object" || value === null) {
108
- return void 0;
109
- }
110
- return value[langfuseResolvedConfigSymbol];
148
+ function redactMessageContent(value, redactStringFn) {
149
+ return redactValue(value, 0, redactStringFn);
111
150
  }
112
- function resolveStringOption(option, fallback, envVar) {
113
- return emptyToUndefined(option) ?? emptyToUndefined(fallback) ?? emptyToUndefined(envVar);
151
+ function isBase64DataUrl(value) {
152
+ return /^data:[^;,]+;base64,/i.test(value);
114
153
  }
115
154
 
116
155
  // src/scoring.ts
@@ -153,7 +192,7 @@ var ScoreQueue = class {
153
192
  timeoutMs;
154
193
  batchSize;
155
194
  flushIntervalMs;
156
- maxRetries;
195
+ maxAttempts;
157
196
  fetchImpl;
158
197
  sleep;
159
198
  setTimer;
@@ -165,7 +204,7 @@ var ScoreQueue = class {
165
204
  this.timeoutMs = options.timeoutMs;
166
205
  this.batchSize = options.batchSize;
167
206
  this.flushIntervalMs = options.flushIntervalMs;
168
- this.maxRetries = options.maxRetries;
207
+ this.maxAttempts = options.maxAttempts;
169
208
  this.fetchImpl = options.fetchImpl ?? fetch;
170
209
  this.sleep = options.sleep ?? defaultSleep;
171
210
  this.setTimer = options.setTimer ?? defaultSetTimer;
@@ -212,10 +251,7 @@ var ScoreQueue = class {
212
251
  async shutdown() {
213
252
  this.closed = true;
214
253
  this.clearScheduledTimer();
215
- try {
216
- await this.flush();
217
- } catch {
218
- }
254
+ await this.flush();
219
255
  }
220
256
  scheduleTimer() {
221
257
  if (this.timer !== null) {
@@ -239,7 +275,7 @@ var ScoreQueue = class {
239
275
  async sendBatch(scores) {
240
276
  const body = scores.map((score) => buildScoreBody(score));
241
277
  let lastError;
242
- for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
278
+ for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {
243
279
  try {
244
280
  const response = await this.fetchImpl(`${this.baseUrl}/api/public/scores`, {
245
281
  method: "POST",
@@ -268,12 +304,12 @@ var ScoreQueue = class {
268
304
  }
269
305
  lastError = error;
270
306
  }
271
- if (attempt < this.maxRetries - 1) {
307
+ if (attempt < this.maxAttempts - 1) {
272
308
  await this.sleep(computeBackoff(attempt));
273
309
  }
274
310
  }
275
311
  throw new RetryableLangfuseScoreError(
276
- `Langfuse score batch failed after ${this.maxRetries} attempts`,
312
+ `Langfuse score batch failed after ${this.maxAttempts} attempts`,
277
313
  scores,
278
314
  lastError
279
315
  );
@@ -308,17 +344,251 @@ function buildScoreBody(score) {
308
344
  name: score.name,
309
345
  value: score.value
310
346
  };
311
- if (score.observationId !== void 0) body.observationId = score.observationId;
312
- if (score.dataType !== void 0) body.dataType = score.dataType;
313
- if (score.comment !== void 0) body.comment = score.comment;
314
- if (score.metadata !== void 0) body.metadata = score.metadata;
315
- const configId = score.configId ?? score.scoreConfigId;
316
- if (configId !== void 0) body.configId = configId;
317
- if (score.environment !== void 0) body.environment = score.environment;
318
- if (score.timestamp !== void 0) {
319
- body.timestamp = score.timestamp instanceof Date ? score.timestamp.toISOString() : score.timestamp;
347
+ if (score.observationId !== void 0) body.observationId = score.observationId;
348
+ if (score.dataType !== void 0) body.dataType = score.dataType;
349
+ if (score.comment !== void 0) body.comment = score.comment;
350
+ if (score.metadata !== void 0) body.metadata = score.metadata;
351
+ const configId = score.configId ?? score.scoreConfigId;
352
+ if (configId !== void 0) body.configId = configId;
353
+ if (score.environment !== void 0) body.environment = score.environment;
354
+ if (score.timestamp !== void 0) {
355
+ body.timestamp = score.timestamp instanceof Date ? score.timestamp.toISOString() : score.timestamp;
356
+ }
357
+ return body;
358
+ }
359
+
360
+ // src/tracing.ts
361
+ import { LangfuseSpanProcessor } from "@langfuse/otel";
362
+ import {
363
+ LangfuseAgent,
364
+ LangfuseEvent,
365
+ LangfuseGeneration,
366
+ LangfuseGuardrail,
367
+ LangfuseOtelSpanAttributes,
368
+ LangfuseSpan,
369
+ LangfuseTool
370
+ } from "@langfuse/tracing";
371
+ import {
372
+ ROOT_CONTEXT,
373
+ TraceFlags,
374
+ trace
375
+ } from "@opentelemetry/api";
376
+ import { resourceFromAttributes } from "@opentelemetry/resources";
377
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
378
+ import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
379
+
380
+ // src/capture.ts
381
+ var DEFAULT_CAPTURE_MAX_BYTES = 262144;
382
+ var MIN_CAPTURE_MAX_BYTES = 96;
383
+ function validateCaptureMaxBytes(value) {
384
+ const resolved = value ?? DEFAULT_CAPTURE_MAX_BYTES;
385
+ if (!Number.isInteger(resolved) || resolved < MIN_CAPTURE_MAX_BYTES) {
386
+ throw new TypeError(
387
+ `Langfuse captureMaxBytes must be an integer of at least ${MIN_CAPTURE_MAX_BYTES}`
388
+ );
389
+ }
390
+ return resolved;
391
+ }
392
+ function sanitizeTraceValue(value, maxBytes) {
393
+ validateCaptureMaxBytes(maxBytes);
394
+ const sanitized = sanitizeValue(value, 0, /* @__PURE__ */ new WeakSet());
395
+ let serialized;
396
+ try {
397
+ serialized = JSON.stringify(sanitized) ?? String(sanitized);
398
+ } catch {
399
+ return omitted("unserializable");
400
+ }
401
+ const originalBytes = utf8Bytes(serialized);
402
+ if (originalBytes <= maxBytes) {
403
+ return sanitized;
404
+ }
405
+ const preview = boundedPreview(serialized, originalBytes, maxBytes);
406
+ return {
407
+ anviaTraceValue: "truncated",
408
+ originalBytes,
409
+ preview
410
+ };
411
+ }
412
+ function sanitizeValue(value, depth, seen) {
413
+ if (depth > 16) {
414
+ return omitted("depth");
415
+ }
416
+ if (typeof value === "string") {
417
+ if (/^data:[^;,]+;base64,/i.test(value)) {
418
+ return omitted("base64", utf8Bytes(value));
419
+ }
420
+ return value;
421
+ }
422
+ if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
423
+ const byteLength = value.byteLength;
424
+ return omitted("binary", byteLength);
425
+ }
426
+ if (value === null || typeof value !== "object") {
427
+ return value;
428
+ }
429
+ if (seen.has(value)) {
430
+ return omitted("circular");
431
+ }
432
+ seen.add(value);
433
+ if (Array.isArray(value)) {
434
+ const result2 = value.map((entry) => sanitizeValue(entry, depth + 1, seen));
435
+ seen.delete(value);
436
+ return result2;
437
+ }
438
+ const record = value;
439
+ const result = {};
440
+ for (const [key, entry] of Object.entries(record)) {
441
+ if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" && typeof record.mediaType === "string")) {
442
+ result[key] = omitted("base64", utf8Bytes(entry));
443
+ continue;
444
+ }
445
+ result[key] = sanitizeValue(entry, depth + 1, seen);
446
+ }
447
+ seen.delete(value);
448
+ return result;
449
+ }
450
+ function omitted(reason, originalBytes) {
451
+ return {
452
+ anviaTraceValue: "omitted",
453
+ reason,
454
+ ...originalBytes === void 0 ? {} : { originalBytes }
455
+ };
456
+ }
457
+ function boundedPreview(value, originalBytes, maxBytes) {
458
+ let low = 0;
459
+ let high = value.length;
460
+ while (low < high) {
461
+ const middle = Math.ceil((low + high) / 2);
462
+ const candidate = {
463
+ anviaTraceValue: "truncated",
464
+ originalBytes,
465
+ preview: value.slice(0, middle)
466
+ };
467
+ if (utf8Bytes(JSON.stringify(candidate)) <= maxBytes) {
468
+ low = middle;
469
+ } else {
470
+ high = middle - 1;
471
+ }
472
+ }
473
+ return value.slice(0, low);
474
+ }
475
+ function utf8Bytes(value) {
476
+ return typeof Buffer === "undefined" ? new TextEncoder().encode(value).byteLength : Buffer.byteLength(value, "utf8");
477
+ }
478
+
479
+ // src/helpers.ts
480
+ function modelInputMessage(message) {
481
+ const { metadata: _metadata, ...result } = message;
482
+ return result;
483
+ }
484
+ function modelInputMessages(messages) {
485
+ return messages.map(modelInputMessage);
486
+ }
487
+ function modelParameters(request) {
488
+ const params = {};
489
+ if (request.temperature !== void 0) params.temperature = request.temperature;
490
+ if (request.maxTokens !== void 0) params.maxTokens = request.maxTokens;
491
+ if (request.toolChoice !== void 0) {
492
+ params.toolChoice = typeof request.toolChoice === "string" ? request.toolChoice : request.toolChoice.name;
493
+ }
494
+ return params;
495
+ }
496
+ function usageDetails(usage) {
497
+ if (usage.details !== void 0 && Object.keys(usage.details).length > 0) {
498
+ return { ...usage.details };
499
+ }
500
+ return {
501
+ input: usage.inputTokens,
502
+ output: usage.outputTokens,
503
+ total: usage.totalTokens
504
+ };
505
+ }
506
+ function usageDetailsFromRecord(usage) {
507
+ if (isRecord(usage.details)) {
508
+ const details = Object.fromEntries(
509
+ Object.entries(usage.details).filter(
510
+ (entry) => typeof entry[1] === "number" && Number.isFinite(entry[1]) && entry[1] >= 0
511
+ )
512
+ );
513
+ if (Object.keys(details).length > 0) {
514
+ return details;
515
+ }
516
+ }
517
+ return {
518
+ input: numberValue(usage.inputTokens) ?? 0,
519
+ output: numberValue(usage.outputTokens) ?? 0,
520
+ total: numberValue(usage.totalTokens) ?? (numberValue(usage.inputTokens) ?? 0) + (numberValue(usage.outputTokens) ?? 0)
521
+ };
522
+ }
523
+ function childMetadata(args, agentId, agentName, childTurn) {
524
+ return {
525
+ source: "agent_tool_event",
526
+ childAgentId: agentId,
527
+ childAgentName: agentName,
528
+ childTurn,
529
+ parentToolName: args.toolName,
530
+ parentInternalCallId: args.internalCallId,
531
+ parentToolCallId: args.toolCallId
532
+ };
533
+ }
534
+ function generationKey(agentId, turn) {
535
+ return `${agentId}:${turn}`;
536
+ }
537
+ function agentLabel(agentId, agentName) {
538
+ return (agentName ?? agentId).replaceAll(/\s+/g, "_");
539
+ }
540
+ function isRecord(value) {
541
+ return typeof value === "object" && value !== null && !Array.isArray(value);
542
+ }
543
+ function numberValue(value) {
544
+ return typeof value === "number" ? value : void 0;
545
+ }
546
+ function emptyToUndefined(value) {
547
+ return value === void 0 || value.length === 0 ? void 0 : value;
548
+ }
549
+ function errorMessage(error) {
550
+ return error instanceof Error ? error.message : String(error);
551
+ }
552
+
553
+ // src/config.ts
554
+ var langfuseResolvedConfigSymbol = /* @__PURE__ */ Symbol.for(
555
+ "@anvia/langfuse.resolvedConfig"
556
+ );
557
+ function resolveLangfuseConfig(options = {}, fallback) {
558
+ return {
559
+ publicKey: resolveStringOption(
560
+ options.publicKey,
561
+ fallback?.publicKey,
562
+ process.env.LANGFUSE_PUBLIC_KEY
563
+ ),
564
+ secretKey: resolveStringOption(
565
+ options.secretKey,
566
+ fallback?.secretKey,
567
+ process.env.LANGFUSE_SECRET_KEY
568
+ ),
569
+ baseUrl: resolveStringOption(options.baseUrl, fallback?.baseUrl, process.env.LANGFUSE_BASE_URL) ?? "https://cloud.langfuse.com",
570
+ environment: resolveStringOption(
571
+ options.environment,
572
+ fallback?.environment,
573
+ process.env.LANGFUSE_TRACING_ENVIRONMENT
574
+ ),
575
+ release: resolveStringOption(options.release, fallback?.release, process.env.LANGFUSE_RELEASE),
576
+ serviceName: resolveStringOption(
577
+ options.serviceName,
578
+ fallback?.serviceName,
579
+ process.env.LANGFUSE_SERVICE_NAME
580
+ ),
581
+ timeoutMs: options.timeoutMs ?? fallback?.timeoutMs ?? 3e4
582
+ };
583
+ }
584
+ function getResolvedLangfuseConfig(value) {
585
+ if (typeof value !== "object" || value === null) {
586
+ return void 0;
320
587
  }
321
- return body;
588
+ return value[langfuseResolvedConfigSymbol];
589
+ }
590
+ function resolveStringOption(option, fallback, envVar) {
591
+ return emptyToUndefined(option) ?? emptyToUndefined(fallback) ?? emptyToUndefined(envVar);
322
592
  }
323
593
 
324
594
  // src/dataset-client.ts
@@ -377,7 +647,8 @@ function createLangfuseDatasetClient(tracing, options = {}) {
377
647
  if (dataset.metadata !== void 0) result.metadata = dataset.metadata;
378
648
  return result;
379
649
  },
380
- async getDataset(name) {
650
+ async getDataset(options2) {
651
+ const { name } = options2;
381
652
  const items = [];
382
653
  let description;
383
654
  let metadata;
@@ -417,7 +688,8 @@ function createLangfuseDatasetClient(tracing, options = {}) {
417
688
  if (metadata !== void 0) dataset.metadata = metadata;
418
689
  return dataset;
419
690
  },
420
- async upsertItems(name, items) {
691
+ async upsertItems(options2) {
692
+ const { name, items } = options2;
421
693
  const url = `${baseUrl}/api/public/datasets/${encodeURIComponent(name)}/items`;
422
694
  await request(url, {
423
695
  method: "POST",
@@ -427,7 +699,7 @@ function createLangfuseDatasetClient(tracing, options = {}) {
427
699
  async runExperiment(opts) {
428
700
  let items = opts.items;
429
701
  if (items === void 0) {
430
- const dataset = await this.getDataset(opts.datasetName);
702
+ const dataset = await this.getDataset({ name: opts.datasetName });
431
703
  items = dataset.items;
432
704
  }
433
705
  if (items === void 0 || items.length === 0) {
@@ -508,7 +780,8 @@ import {
508
780
  } from "@anvia/core/evals";
509
781
  var DEFAULT_TRUNCATE_BYTES = 2048;
510
782
  function createLangfuseEvalReporter(tracing, options = {}) {
511
- const onMissingTrace = options.onMissingTrace ?? (options.strict === true ? "throw" : "ignore");
783
+ const onMissingTrace = options.onMissingTrace ?? "ignore";
784
+ const traceObserver = options.traceObserver ?? "langfuse";
512
785
  const truncateAt = options.truncateInputAt ?? DEFAULT_TRUNCATE_BYTES;
513
786
  const includeMessages = options.includeMessages ?? true;
514
787
  const includeContext = options.includeContext ?? false;
@@ -517,18 +790,20 @@ function createLangfuseEvalReporter(tracing, options = {}) {
517
790
  if (args.outcome.outcome === "invalid" && options.publishInvalid !== true) {
518
791
  return;
519
792
  }
520
- const trace = args.trace ?? resolveEvalTraceRef({
793
+ const trace2 = args.trace ?? resolveEvalTraceRef({
521
794
  output: args.output,
522
795
  input: args.case.input,
523
796
  metadata: args.case.metadata
524
797
  });
525
- if (trace?.traceId === void 0 || trace.traceId.length === 0) {
798
+ if (trace2?.traceId === void 0 || trace2.traceId.length === 0 || trace2.observer !== void 0 && trace2.observer !== traceObserver) {
526
799
  if (onMissingTrace === "throw") {
527
- throw new Error("Langfuse eval reporter requires traceId");
800
+ throw new Error(
801
+ `Langfuse eval reporter requires traceId from observer ${JSON.stringify(traceObserver)}`
802
+ );
528
803
  }
529
804
  if (onMissingTrace === "warn") {
530
805
  console.warn(
531
- "[anvia/langfuse] eval reporter dropped score because no traceId was found",
806
+ "[anvia/langfuse] eval reporter dropped score because no matching trace was found",
532
807
  { caseId: args.case.id, metric: args.metric.name }
533
808
  );
534
809
  }
@@ -547,11 +822,11 @@ function createLangfuseEvalReporter(tracing, options = {}) {
547
822
  });
548
823
  const configId = resolveConfigId(args.metric);
549
824
  const score = {
550
- traceId: trace.traceId,
825
+ traceId: trace2.traceId,
551
826
  name: args.metric.name,
552
827
  value: projection.value
553
828
  };
554
- if (trace.observationId !== void 0) score.observationId = trace.observationId;
829
+ if (trace2.observationId !== void 0) score.observationId = trace2.observationId;
555
830
  if (args.metric.dataType !== void 0) score.dataType = args.metric.dataType;
556
831
  if (configId !== void 0) score.configId = configId;
557
832
  if (projection.explanation !== void 0) score.comment = projection.explanation;
@@ -649,18 +924,20 @@ function readMessages(output) {
649
924
 
650
925
  // src/experiment-runner.ts
651
926
  import { runEvalSuite } from "@anvia/core/evals";
652
- async function runEvalAsExperiment(evalOptions, experimentOptions) {
927
+ async function runLangfuseEvalExperiment(client, options) {
928
+ const evalOptions = options.suite;
929
+ const experimentOptions = options.experiment;
653
930
  const clientOptions = {};
654
931
  if (experimentOptions.pageSize !== void 0) clientOptions.pageSize = experimentOptions.pageSize;
655
932
  if (experimentOptions.timeoutMs !== void 0)
656
933
  clientOptions.timeoutMs = experimentOptions.timeoutMs;
657
- const client = experimentOptions.client ?? createLangfuseDatasetClient(experimentOptions.tracing, clientOptions);
934
+ const datasetClient = client.datasetClient(clientOptions);
658
935
  const suiteOptions = experimentOptions.publishScores === true ? {
659
936
  ...evalOptions,
660
937
  reporters: [
661
938
  ...evalOptions.reporters ?? [],
662
939
  createLangfuseEvalReporter(
663
- experimentOptions.tracing,
940
+ client,
664
941
  experimentOptions.reporterOptions
665
942
  )
666
943
  ]
@@ -705,30 +982,30 @@ async function runEvalAsExperiment(evalOptions, experimentOptions) {
705
982
  };
706
983
  }
707
984
  const output = result.output ?? void 0;
708
- const trace = readTraceFromOutput(result.output);
709
- return { output, trace };
985
+ const trace2 = readTraceFromOutput(result.output);
986
+ return { output, trace: trace2 };
710
987
  }
711
988
  };
712
989
  if (experimentOptions.description !== void 0) {
713
990
  runOptions.description = experimentOptions.description;
714
991
  }
715
992
  if (experimentOptions.metadata !== void 0) runOptions.metadata = experimentOptions.metadata;
716
- const datasetRun = await client.runExperiment(runOptions);
993
+ const datasetRun = await datasetClient.runExperiment(runOptions);
717
994
  return { suite, datasetRun };
718
995
  }
719
996
  function readTraceFromOutput(output) {
720
997
  if (typeof output !== "object" || output === null || !("trace" in output)) {
721
998
  return void 0;
722
999
  }
723
- const trace = output.trace;
724
- if (typeof trace !== "object" || trace === null) {
1000
+ const trace2 = output.trace;
1001
+ if (typeof trace2 !== "object" || trace2 === null) {
725
1002
  return void 0;
726
1003
  }
727
- const traceId = trace.traceId;
1004
+ const traceId = trace2.traceId;
728
1005
  if (typeof traceId !== "string") {
729
1006
  return void 0;
730
1007
  }
731
- const observationId = trace.observationId;
1008
+ const observationId = trace2.observationId;
732
1009
  if (typeof observationId === "string") {
733
1010
  return { traceId, observationId };
734
1011
  }
@@ -761,7 +1038,8 @@ function createLangfusePromptClient(tracing, options = {}) {
761
1038
  }
762
1039
  return await response.json();
763
1040
  }
764
- async function getPrompt(name, opts = {}) {
1041
+ async function getPrompt(options2) {
1042
+ const { name, ...opts } = options2;
765
1043
  const key = `${name}::${opts.version ?? ""}::${opts.label ?? ""}`;
766
1044
  const ttl = opts.cacheTtlMs ?? defaultTtl;
767
1045
  if (opts.refresh !== true) {
@@ -790,18 +1068,18 @@ function createLangfusePromptClient(tracing, options = {}) {
790
1068
  cache.set(key, { prompt, expiresAt: Date.now() + ttl });
791
1069
  return prompt;
792
1070
  }
793
- function getPromptText(name, opts) {
794
- return getPrompt(name, opts).then((prompt) => {
1071
+ function getPromptText(options2) {
1072
+ return getPrompt(options2).then((prompt) => {
795
1073
  if (typeof prompt.prompt !== "string") {
796
- throw new Error(`Prompt ${name} is a chat prompt; expected text`);
1074
+ throw new Error(`Prompt ${options2.name} is a chat prompt; expected text`);
797
1075
  }
798
1076
  return prompt.prompt;
799
1077
  });
800
1078
  }
801
- function getPromptChat(name, opts) {
802
- return getPrompt(name, opts).then((prompt) => {
1079
+ function getPromptChat(options2) {
1080
+ return getPrompt(options2).then((prompt) => {
803
1081
  if (typeof prompt.prompt === "string") {
804
- throw new Error(`Prompt ${name} is a text prompt; expected chat`);
1082
+ throw new Error(`Prompt ${options2.name} is a text prompt; expected chat`);
805
1083
  }
806
1084
  return prompt.prompt;
807
1085
  });
@@ -809,332 +1087,94 @@ function createLangfusePromptClient(tracing, options = {}) {
809
1087
  function refresh() {
810
1088
  cache.clear();
811
1089
  }
812
- return { getPrompt, getPromptText, getPromptChat, refresh };
813
- }
814
- function normalizePrompt(raw, type) {
815
- if (type === "text") {
816
- if (typeof raw === "string") return raw;
817
- throw new Error("Expected text prompt to be a string");
818
- }
819
- if (!Array.isArray(raw)) {
820
- throw new Error("Expected chat prompt to be an array of messages");
821
- }
822
- return raw.map((entry) => {
823
- if (typeof entry !== "object" || entry === null) {
824
- throw new Error("Expected chat message to be an object");
825
- }
826
- const role = entry.role;
827
- const content = entry.content;
828
- if (typeof content !== "string") {
829
- throw new Error("Expected chat message content to be a string");
830
- }
831
- if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") {
832
- throw new Error(`Unexpected chat message role: ${String(role)}`);
833
- }
834
- return { role, content };
835
- });
836
- }
837
- function buildAuthHeader2(publicKey, secretKey) {
838
- if (publicKey === void 0 || secretKey === void 0) {
839
- return {};
840
- }
841
- const encoded = Buffer.from(`${publicKey}:${secretKey}`).toString("base64");
842
- return { Authorization: `Basic ${encoded}` };
843
- }
844
- async function readErrorBody2(response) {
845
- try {
846
- return await response.text();
847
- } catch {
848
- return "<unreadable>";
849
- }
850
- }
851
-
852
- // src/redaction.ts
853
- var DEFAULT_REPLACEMENT = "[REDACTED]";
854
- var MAX_DEPTH = 16;
855
- function createPiiRedactor(options = {}) {
856
- const patterns = options.patterns ?? DEFAULT_PATTERNS;
857
- const replacement = options.replacement ?? DEFAULT_REPLACEMENT;
858
- const compiled = patterns.map((p) => ({
859
- name: p.name,
860
- regex: cloneRegex(p.regex, "g")
861
- }));
862
- const patternNamesList = patterns.map((p) => p.name);
863
- function redactString(input) {
864
- if (typeof input !== "string") return input;
865
- let out = input;
866
- for (const { name, regex } of compiled) {
867
- out = applyPattern(out, name, regex, replacement);
868
- }
869
- return out;
870
- }
871
- function redactObject(input) {
872
- return redactValue(input, 0, redactString);
873
- }
874
- function redactMessages(input) {
875
- return input.map((message) => redactMessage(message, redactString));
876
- }
877
- function patternNames() {
878
- return patternNamesList;
879
- }
880
- return { redactString, redactObject, redactMessages, patternNames };
881
- }
882
- var DEFAULT_PATTERNS = [
883
- { name: "email", regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g },
884
- { name: "creditCard", regex: /\b(?:\d[ -]?){13,19}\b/g },
885
- { name: "ipv4", regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g },
886
- {
887
- name: "phone",
888
- regex: /(?<!\d)(?:\+\d{1,3}[\s.-]?)?(?:\(\d{2,4}\)[\s.-]?)?\d{3,4}[\s.-]?\d{3,4}(?:[\s.-]?\d{3,4})?(?!\d)/g
889
- },
890
- { name: "jwt", regex: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g },
891
- {
892
- name: "apiKey",
893
- regex: /\b(?:sk|pk|api|key|token)[-_][A-Za-z0-9]{16,}\b/gi
894
- }
895
- ];
896
- function applyPattern(input, name, regex, replacement) {
897
- if (name === "creditCard") {
898
- return redactCreditCards(input, replacement);
899
- }
900
- return input.replace(regex, replacement);
901
- }
902
- function redactCreditCards(input, replacement) {
903
- let out = "";
904
- let i = 0;
905
- while (i < input.length) {
906
- const ch = input.charAt(i);
907
- if (/\d/.test(ch)) {
908
- const { length, valid } = longestLuhnChunk(input.slice(i));
909
- if (valid) {
910
- out += replacement;
911
- i += length;
912
- continue;
913
- }
914
- }
915
- out += ch;
916
- i += 1;
917
- }
918
- return out;
919
- }
920
- function longestLuhnChunk(s) {
921
- let length = 0;
922
- let bestValid = 0;
923
- while (length < s.length && length < 40) {
924
- const ch = s.charAt(length);
925
- if (!/\d/.test(ch) && ch !== "-") break;
926
- length += 1;
927
- const candidate = s.slice(0, length).replace(/\D/g, "");
928
- if (candidate.length >= 13 && candidate.length <= 19) {
929
- if (startsWithKnownPrefix(candidate) && passesLuhn(candidate)) {
930
- bestValid = length;
931
- }
932
- }
933
- }
934
- return { length: bestValid, valid: bestValid > 0 };
935
- }
936
- function startsWithKnownPrefix(digits) {
937
- if (digits.startsWith("4")) return true;
938
- const two = digits.slice(0, 2);
939
- if (two === "51" || two === "52" || two === "53" || two === "54" || two === "55") return true;
940
- const four = digits.slice(0, 4);
941
- if (four === "2221" || four === "2720") return true;
942
- const twoAgain = digits.slice(0, 2);
943
- if (twoAgain === "34" || twoAgain === "37") return true;
944
- if (four === "6011" || twoAgain === "65") return true;
945
- return digits.startsWith("35");
946
- }
947
- function passesLuhn(digits) {
948
- if (!/^\d+$/.test(digits)) return false;
949
- let sum = 0;
950
- let alt = false;
951
- for (let i = digits.length - 1; i >= 0; i -= 1) {
952
- const raw = digits.charCodeAt(i) - 48;
953
- let value = raw;
954
- if (alt) {
955
- value *= 2;
956
- if (value > 9) value -= 9;
957
- }
958
- sum += value;
959
- alt = !alt;
960
- }
961
- return sum % 10 === 0;
962
- }
963
- function cloneRegex(source, flags) {
964
- return new RegExp(source.source, flags + source.flags.replace(/g/g, ""));
965
- }
966
- function redactValue(value, depth, redactStringFn) {
967
- if (depth > MAX_DEPTH) return value;
968
- if (typeof value === "string") {
969
- return isBase64DataUrl(value) ? value : redactStringFn(value);
970
- }
971
- if (Array.isArray(value))
972
- return value.map((entry) => redactValue(entry, depth + 1, redactStringFn));
973
- if (value !== null && typeof value === "object") {
974
- if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
975
- return value;
976
- }
977
- const record = value;
978
- const out = {};
979
- for (const [key, entry] of Object.entries(record)) {
980
- if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" || record.type === "encrypted" || record.type === "redacted")) {
981
- out[key] = entry;
982
- } else {
983
- out[key] = redactValue(entry, depth + 1, redactStringFn);
984
- }
985
- }
986
- return out;
987
- }
988
- return value;
1090
+ return { getPrompt, getPromptText, getPromptChat, refresh };
989
1091
  }
990
- function redactMessage(message, redactStringFn) {
991
- if (message.role === "system") {
992
- return { ...message, content: redactStringFn(message.content) };
1092
+ function normalizePrompt(raw, type) {
1093
+ if (type === "text") {
1094
+ if (typeof raw === "string") return raw;
1095
+ throw new Error("Expected text prompt to be a string");
993
1096
  }
994
- return {
995
- ...message,
996
- content: redactMessageContent(message.content, redactStringFn)
997
- };
998
- }
999
- function redactMessageContent(value, redactStringFn) {
1000
- return redactValue(value, 0, redactStringFn);
1001
- }
1002
- function isBase64DataUrl(value) {
1003
- return /^data:[^;,]+;base64,/i.test(value);
1097
+ if (!Array.isArray(raw)) {
1098
+ throw new Error("Expected chat prompt to be an array of messages");
1099
+ }
1100
+ return raw.map((entry) => {
1101
+ if (typeof entry !== "object" || entry === null) {
1102
+ throw new Error("Expected chat message to be an object");
1103
+ }
1104
+ const role = entry.role;
1105
+ const content = entry.content;
1106
+ if (typeof content !== "string") {
1107
+ throw new Error("Expected chat message content to be a string");
1108
+ }
1109
+ if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") {
1110
+ throw new Error(`Unexpected chat message role: ${String(role)}`);
1111
+ }
1112
+ return { role, content };
1113
+ });
1004
1114
  }
1005
-
1006
- // src/tracing.ts
1007
- import { LangfuseSpanProcessor } from "@langfuse/otel";
1008
- import {
1009
- LangfuseOtelSpanAttributes,
1010
- startObservation
1011
- } from "@langfuse/tracing";
1012
- import { resourceFromAttributes } from "@opentelemetry/resources";
1013
- import { NodeSDK } from "@opentelemetry/sdk-node";
1014
- import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
1015
-
1016
- // src/capture.ts
1017
- var DEFAULT_CAPTURE_MAX_BYTES = 262144;
1018
- var MIN_CAPTURE_MAX_BYTES = 96;
1019
- function validateCaptureMaxBytes(value) {
1020
- const resolved = value ?? DEFAULT_CAPTURE_MAX_BYTES;
1021
- if (!Number.isInteger(resolved) || resolved < MIN_CAPTURE_MAX_BYTES) {
1022
- throw new TypeError(
1023
- `Langfuse captureMaxBytes must be an integer of at least ${MIN_CAPTURE_MAX_BYTES}`
1024
- );
1115
+ function buildAuthHeader2(publicKey, secretKey) {
1116
+ if (publicKey === void 0 || secretKey === void 0) {
1117
+ return {};
1025
1118
  }
1026
- return resolved;
1119
+ const encoded = Buffer.from(`${publicKey}:${secretKey}`).toString("base64");
1120
+ return { Authorization: `Basic ${encoded}` };
1027
1121
  }
1028
- function sanitizeTraceValue(value, maxBytes) {
1029
- validateCaptureMaxBytes(maxBytes);
1030
- const sanitized = sanitizeValue(value, 0, /* @__PURE__ */ new WeakSet());
1031
- let serialized;
1122
+ async function readErrorBody2(response) {
1032
1123
  try {
1033
- serialized = JSON.stringify(sanitized) ?? String(sanitized);
1124
+ return await response.text();
1034
1125
  } catch {
1035
- return omitted("unserializable");
1036
- }
1037
- const originalBytes = utf8Bytes(serialized);
1038
- if (originalBytes <= maxBytes) {
1039
- return sanitized;
1126
+ return "<unreadable>";
1040
1127
  }
1041
- const preview = boundedPreview(serialized, originalBytes, maxBytes);
1042
- return {
1043
- anviaTraceValue: "truncated",
1044
- originalBytes,
1045
- preview
1046
- };
1047
1128
  }
1048
- function sanitizeValue(value, depth, seen) {
1049
- if (depth > 16) {
1050
- return omitted("depth");
1051
- }
1052
- if (typeof value === "string") {
1053
- if (/^data:[^;,]+;base64,/i.test(value)) {
1054
- return omitted("base64", utf8Bytes(value));
1055
- }
1056
- return value;
1129
+
1130
+ // src/tracing.ts
1131
+ var LangfuseObservationFactory = class {
1132
+ constructor(tracer) {
1133
+ this.tracer = tracer;
1057
1134
  }
1058
- if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
1059
- const byteLength = value.byteLength;
1060
- return omitted("binary", byteLength);
1135
+ tracer;
1136
+ agent(name, attributes, parent) {
1137
+ return new LangfuseAgent({ otelSpan: this.startSpan(name, parent), attributes });
1061
1138
  }
1062
- if (value === null || typeof value !== "object") {
1063
- return value;
1139
+ span(name, attributes, parent) {
1140
+ return new LangfuseSpan({ otelSpan: this.startSpan(name, parent), attributes });
1064
1141
  }
1065
- if (seen.has(value)) {
1066
- return omitted("circular");
1142
+ generation(name, attributes, parent) {
1143
+ return new LangfuseGeneration({ otelSpan: this.startSpan(name, parent), attributes });
1067
1144
  }
1068
- seen.add(value);
1069
- if (Array.isArray(value)) {
1070
- const result2 = value.map((entry) => sanitizeValue(entry, depth + 1, seen));
1071
- seen.delete(value);
1072
- return result2;
1145
+ tool(name, attributes, parent) {
1146
+ return new LangfuseTool({ otelSpan: this.startSpan(name, parent), attributes });
1073
1147
  }
1074
- const record = value;
1075
- const result = {};
1076
- for (const [key, entry] of Object.entries(record)) {
1077
- if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" && typeof record.mediaType === "string")) {
1078
- result[key] = omitted("base64", utf8Bytes(entry));
1079
- continue;
1080
- }
1081
- result[key] = sanitizeValue(entry, depth + 1, seen);
1148
+ guardrail(name, attributes, parent) {
1149
+ return new LangfuseGuardrail({ otelSpan: this.startSpan(name, parent), attributes });
1082
1150
  }
1083
- seen.delete(value);
1084
- return result;
1085
- }
1086
- function omitted(reason, originalBytes) {
1087
- return {
1088
- anviaTraceValue: "omitted",
1089
- reason,
1090
- ...originalBytes === void 0 ? {} : { originalBytes }
1091
- };
1092
- }
1093
- function boundedPreview(value, originalBytes, maxBytes) {
1094
- let low = 0;
1095
- let high = value.length;
1096
- while (low < high) {
1097
- const middle = Math.ceil((low + high) / 2);
1098
- const candidate = {
1099
- anviaTraceValue: "truncated",
1100
- originalBytes,
1101
- preview: value.slice(0, middle)
1102
- };
1103
- if (utf8Bytes(JSON.stringify(candidate)) <= maxBytes) {
1104
- low = middle;
1105
- } else {
1106
- high = middle - 1;
1107
- }
1151
+ event(name, attributes, parent, timestamp) {
1152
+ const endTime = timestamp ?? /* @__PURE__ */ new Date();
1153
+ return new LangfuseEvent({
1154
+ otelSpan: this.startSpan(name, parent, timestamp),
1155
+ attributes,
1156
+ timestamp: endTime
1157
+ });
1108
1158
  }
1109
- return value.slice(0, low);
1110
- }
1111
- function utf8Bytes(value) {
1112
- return typeof Buffer === "undefined" ? new TextEncoder().encode(value).byteLength : Buffer.byteLength(value, "utf8");
1113
- }
1114
-
1115
- // src/tracing.ts
1116
- var langfuse = {
1117
- create(options = {}) {
1118
- return new LangfuseAgentObserver(options);
1159
+ startSpan(name, parent, startTime) {
1160
+ const parentContext = parent === void 0 ? ROOT_CONTEXT : "otelSpan" in parent ? trace.setSpan(ROOT_CONTEXT, parent.otelSpan) : trace.setSpanContext(ROOT_CONTEXT, parent);
1161
+ return this.tracer.startSpan(name, startTime === void 0 ? {} : { startTime }, parentContext);
1119
1162
  }
1120
1163
  };
1121
- var LangfuseAgentObserver = class {
1122
- processor;
1123
- sdk;
1164
+ var LangfuseClient = class {
1124
1165
  [langfuseResolvedConfigSymbol];
1125
1166
  publicKey;
1126
1167
  secretKey;
1127
1168
  baseUrl;
1128
1169
  serviceName;
1129
1170
  timeoutMs;
1130
- queue;
1131
- currentHandle;
1132
- redactor;
1133
- redactInputs;
1134
- redactOutputs;
1135
- captureMode;
1136
- captureMaxBytes;
1137
- constructor(options) {
1171
+ options;
1172
+ resource;
1173
+ initialization;
1174
+ closePromise;
1175
+ closed = false;
1176
+ constructor(options = {}) {
1177
+ this.options = options;
1138
1178
  const resolvedConfig = resolveLangfuseConfig(options);
1139
1179
  this[langfuseResolvedConfigSymbol] = resolvedConfig;
1140
1180
  this.publicKey = resolvedConfig.publicKey;
@@ -1142,50 +1182,82 @@ var LangfuseAgentObserver = class {
1142
1182
  this.baseUrl = resolvedConfig.baseUrl;
1143
1183
  this.serviceName = resolvedConfig.serviceName;
1144
1184
  this.timeoutMs = resolvedConfig.timeoutMs;
1145
- const processorOptions = {
1146
- baseUrl: this.baseUrl
1185
+ }
1186
+ observer(options = {}) {
1187
+ this.assertOpen();
1188
+ return new LangfuseAgentObserver(this, resolveLangfuseCapture(options));
1189
+ }
1190
+ evalReporter(options = {}) {
1191
+ this.assertOpen();
1192
+ const reporter = createLangfuseEvalReporter(this, options);
1193
+ return {
1194
+ report: (args) => {
1195
+ this.assertOpen();
1196
+ return reporter.report(args);
1197
+ }
1147
1198
  };
1148
- if (this.publicKey !== void 0) processorOptions.publicKey = this.publicKey;
1149
- if (this.secretKey !== void 0) processorOptions.secretKey = this.secretKey;
1150
- if (resolvedConfig.environment !== void 0) {
1151
- processorOptions.environment = resolvedConfig.environment;
1152
- }
1153
- if (resolvedConfig.release !== void 0) processorOptions.release = resolvedConfig.release;
1154
- this.processor = new LangfuseSpanProcessor(processorOptions);
1155
- const sdkOptions = {
1156
- spanProcessors: [this.processor]
1199
+ }
1200
+ promptClient(options = {}) {
1201
+ this.assertOpen();
1202
+ const prompts = createLangfusePromptClient(this, options);
1203
+ return {
1204
+ getPrompt: (getOptions) => {
1205
+ this.assertOpen();
1206
+ return prompts.getPrompt(getOptions);
1207
+ },
1208
+ getPromptText: (getOptions) => {
1209
+ this.assertOpen();
1210
+ return prompts.getPromptText(getOptions);
1211
+ },
1212
+ getPromptChat: (getOptions) => {
1213
+ this.assertOpen();
1214
+ return prompts.getPromptChat(getOptions);
1215
+ },
1216
+ refresh: () => {
1217
+ this.assertOpen();
1218
+ prompts.refresh();
1219
+ }
1157
1220
  };
1158
- if (this.serviceName !== void 0) {
1159
- sdkOptions.resource = resourceFromAttributes({
1160
- [SEMRESATTRS_SERVICE_NAME]: this.serviceName
1161
- });
1162
- }
1163
- this.sdk = new NodeSDK(sdkOptions);
1164
- this.sdk.start();
1165
- const batchSize = options.scoreBatchSize ?? 0;
1166
- this.queue = batchSize > 0 && this.publicKey !== void 0 && this.secretKey !== void 0 ? new ScoreQueue({
1167
- baseUrl: this.baseUrl,
1168
- publicKey: this.publicKey,
1169
- secretKey: this.secretKey,
1170
- timeoutMs: this.timeoutMs,
1171
- batchSize,
1172
- flushIntervalMs: options.scoreFlushIntervalMs ?? 250,
1173
- maxRetries: options.scoreMaxRetries ?? 3
1174
- }) : null;
1175
- this.redactInputs = options.redactInputs;
1176
- this.redactOutputs = options.redactOutputs;
1177
- this.captureMode = options.captureMode ?? "safe";
1178
- this.captureMaxBytes = validateCaptureMaxBytes(options.captureMaxBytes);
1179
- this.redactor = options.redactInputs !== void 0 || options.redactOutputs !== void 0 ? createPiiRedactor(options.redaction) : void 0;
1180
- }
1181
- async startRun(args) {
1221
+ }
1222
+ datasetClient(options = {}) {
1223
+ this.assertOpen();
1224
+ const datasets = createLangfuseDatasetClient(this, options);
1225
+ const client = this;
1226
+ return {
1227
+ createDataset(dataset) {
1228
+ client.assertOpen();
1229
+ return datasets.createDataset(dataset);
1230
+ },
1231
+ getDataset(getOptions) {
1232
+ client.assertOpen();
1233
+ return datasets.getDataset(getOptions);
1234
+ },
1235
+ upsertItems(upsertOptions) {
1236
+ client.assertOpen();
1237
+ return datasets.upsertItems(upsertOptions);
1238
+ },
1239
+ runExperiment(experimentOptions) {
1240
+ client.assertOpen();
1241
+ return datasets.runExperiment(experimentOptions);
1242
+ }
1243
+ };
1244
+ }
1245
+ runEvalExperiment(options) {
1246
+ this.assertOpen();
1247
+ return runLangfuseEvalExperiment(this, options);
1248
+ }
1249
+ async startObservedRun(args, capture) {
1250
+ const resource = await this.resources();
1182
1251
  const traceId = args.trace?.traceId;
1183
- const capturedInput = this.captureInput({
1184
- instructions: args.instructions,
1185
- prompt: args.prompt,
1186
- history: args.history
1187
- });
1188
- const capturedTraceMetadata = this.captureInput(args.trace?.metadata ?? {});
1252
+ const capturedInput = captureInput(
1253
+ {
1254
+ instructions: args.instructions,
1255
+ prompt: args.prompt,
1256
+ history: args.history
1257
+ },
1258
+ capture
1259
+ );
1260
+ const capturedTraceMetadata = captureInput(args.trace?.metadata ?? {}, capture);
1189
1261
  const metadata = {
1190
1262
  agentName: args.agentName,
1191
1263
  agentDescription: args.agentDescription,
@@ -1201,69 +1273,41 @@ var LangfuseAgentObserver = class {
1201
1273
  input: capturedInput,
1202
1274
  metadata
1203
1275
  };
1204
- if (args.trace?.version !== void 0) {
1205
- rootAttributes.version = args.trace.version;
1206
- }
1207
- const root = startObservation(
1276
+ if (args.trace?.version !== void 0) rootAttributes.version = args.trace.version;
1277
+ const root = resource.observations.agent(
1208
1278
  args.agentName ?? "agent.run",
1209
1279
  rootAttributes,
1210
- traceId === void 0 ? { asType: "agent" } : {
1211
- asType: "agent",
1212
- parentSpanContext: {
1213
- traceId,
1214
- spanId: "0000000000000001",
1215
- traceFlags: 1
1216
- }
1280
+ traceId === void 0 ? void 0 : {
1281
+ traceId,
1282
+ spanId: "0000000000000001",
1283
+ traceFlags: TraceFlags.SAMPLED
1217
1284
  }
1218
1285
  );
1219
1286
  applyTraceAttributes(root, args, capturedTraceMetadata);
1220
- const promptRef = resolvePromptRef(args);
1221
- const runObserver = new LangfuseRunObserver(
1287
+ return new LangfuseRunObserver(
1222
1288
  root,
1223
- {
1224
- traceId: root.traceId,
1225
- observationId: root.id
1226
- },
1227
- promptRef,
1228
- {
1229
- redactor: this.redactor,
1230
- redactInputs: this.redactInputs,
1231
- redactOutputs: this.redactOutputs,
1232
- captureMode: this.captureMode,
1233
- captureMaxBytes: this.captureMaxBytes
1234
- }
1289
+ resource.observations,
1290
+ { traceId: root.traceId, observationId: root.id },
1291
+ resolvePromptRef(args),
1292
+ capture
1235
1293
  );
1236
- this.currentHandle = runObserver.getHandle();
1237
- runObserver.setCurrentHandle = (handle) => {
1238
- this.currentHandle = handle;
1239
- };
1240
- runObserver.clearCurrentHandle = () => {
1241
- if (this.currentHandle === runObserver.getHandle()) {
1242
- this.currentHandle = void 0;
1243
- }
1244
- };
1245
- return runObserver;
1246
1294
  }
1247
1295
  async flush() {
1248
- await this.queue?.flush();
1249
- await this.processor.forceFlush();
1296
+ this.assertOpen();
1297
+ const resource = this.resource ?? (this.initialization === void 0 ? void 0 : await this.initialization);
1298
+ if (resource === void 0) return;
1299
+ await resource.queue?.flush();
1300
+ await resource.processor.forceFlush();
1250
1301
  }
1251
- async shutdown() {
1252
- await this.queue?.shutdown();
1253
- await this.sdk.shutdown();
1302
+ close() {
1303
+ this.closePromise ??= this.closeResources();
1304
+ return this.closePromise;
1254
1305
  }
1255
- async flushScores() {
1256
- await this.queue?.flush();
1306
+ async [Symbol.asyncDispose]() {
1307
+ await this.close();
1257
1308
  }
1258
1309
  scoreQueueDepth() {
1259
- return this.queue?.depth() ?? 0;
1260
- }
1261
- getCurrentTrace() {
1262
- return this.currentHandle;
1263
- }
1264
- captureInput(value) {
1265
- const redacted = this.redactor === void 0 || this.redactInputs === void 0 ? value : applyRedaction(this.redactor, value, this.redactInputs);
1266
- return sanitizeTraceValue(redacted, this.captureMaxBytes);
1310
+ return this.resource?.queue?.depth() ?? 0;
1267
1311
  }
1268
1312
  async score(args) {
1269
1313
  if (args.traceId === void 0 || args.traceId.length === 0) {
@@ -1273,12 +1317,100 @@ var LangfuseAgentObserver = class {
1273
1317
  throw new Error("Langfuse score requires publicKey and secretKey");
1274
1318
  }
1275
1319
  assertScoreValue(args.value, args.dataType);
1276
- if (this.queue !== null) {
1277
- this.queue.enqueue(args);
1320
+ const resource = await this.resources();
1321
+ if (resource.queue !== null) {
1322
+ resource.queue.enqueue(args);
1278
1323
  return;
1279
1324
  }
1280
1325
  await this.sendScore(args);
1281
1326
  }
1327
+ resources() {
1328
+ this.assertOpen();
1329
+ if (this.resource !== void 0) return Promise.resolve(this.resource);
1330
+ this.initialization ??= this.createResources().then((resource) => {
1331
+ this.resource = resource;
1332
+ return resource;
1333
+ }).catch((error) => {
1334
+ this.initialization = void 0;
1335
+ throw error;
1336
+ });
1337
+ return this.initialization;
1338
+ }
1339
+ async createResources() {
1340
+ const resolvedConfig = this[langfuseResolvedConfigSymbol];
1341
+ const processorOptions = {
1342
+ baseUrl: this.baseUrl
1343
+ };
1344
+ if (this.publicKey !== void 0) processorOptions.publicKey = this.publicKey;
1345
+ if (this.secretKey !== void 0) processorOptions.secretKey = this.secretKey;
1346
+ if (resolvedConfig.environment !== void 0) {
1347
+ processorOptions.environment = resolvedConfig.environment;
1348
+ }
1349
+ if (resolvedConfig.release !== void 0) processorOptions.release = resolvedConfig.release;
1350
+ const processor = new LangfuseSpanProcessor(processorOptions);
1351
+ const providerOptions = {
1352
+ spanProcessors: [processor]
1353
+ };
1354
+ if (this.serviceName !== void 0) {
1355
+ providerOptions.resource = resourceFromAttributes({
1356
+ [SEMRESATTRS_SERVICE_NAME]: this.serviceName
1357
+ });
1358
+ }
1359
+ let provider;
1360
+ try {
1361
+ provider = new NodeTracerProvider(providerOptions);
1362
+ } catch (error) {
1363
+ await processor.shutdown().catch(() => void 0);
1364
+ throw error;
1365
+ }
1366
+ try {
1367
+ const batchSize = this.options.scores?.batchSize ?? 0;
1368
+ const queue = batchSize > 0 && this.publicKey !== void 0 && this.secretKey !== void 0 ? new ScoreQueue({
1369
+ baseUrl: this.baseUrl,
1370
+ publicKey: this.publicKey,
1371
+ secretKey: this.secretKey,
1372
+ timeoutMs: this.timeoutMs,
1373
+ batchSize,
1374
+ flushIntervalMs: this.options.scores?.flushIntervalMs ?? 250,
1375
+ maxAttempts: scoreMaxAttempts(this.options.scores?.retries)
1376
+ }) : null;
1377
+ return {
1378
+ processor,
1379
+ provider,
1380
+ observations: new LangfuseObservationFactory(
1381
+ provider.getTracer("@anvia/langfuse", "1.0.0")
1382
+ ),
1383
+ queue
1384
+ };
1385
+ } catch (error) {
1386
+ await provider.shutdown().catch(() => void 0);
1387
+ throw error;
1388
+ }
1389
+ }
1390
+ async closeResources() {
1391
+ this.closed = true;
1392
+ const pending = this.resource === void 0 ? this.initialization : Promise.resolve(this.resource);
1393
+ if (pending === void 0) return;
1394
+ let resource;
1395
+ try {
1396
+ resource = await pending;
1397
+ } catch {
1398
+ return;
1399
+ }
1400
+ const settled = await Promise.allSettled([
1401
+ resource.queue?.shutdown() ?? Promise.resolve(),
1402
+ resource.provider.shutdown()
1403
+ ]);
1404
+ const failures = settled.flatMap(
1405
+ (result) => result.status === "rejected" ? [result.reason] : []
1406
+ );
1407
+ if (failures.length > 0) {
1408
+ throw new AggregateError(failures, "Failed to close LangfuseClient.");
1409
+ }
1410
+ }
1411
+ assertOpen() {
1412
+ if (this.closed) throw new Error("LangfuseClient is closed.");
1413
+ }
1282
1414
  async sendScore(args) {
1283
1415
  const body = buildScoreBody2(args);
1284
1416
  const response = await fetch(`${this.baseUrl}/api/public/scores`, {
@@ -1297,6 +1429,37 @@ var LangfuseAgentObserver = class {
1297
1429
  }
1298
1430
  }
1299
1431
  };
1432
+ var LangfuseAgentObserver = class {
1433
+ constructor(client, capture) {
1434
+ this.client = client;
1435
+ this.capture = capture;
1436
+ }
1437
+ client;
1438
+ capture;
1439
+ startRun(args) {
1440
+ return this.client.startObservedRun(args, this.capture);
1441
+ }
1442
+ };
1443
+ function resolveLangfuseCapture(options) {
1444
+ return {
1445
+ redactor: options.redactInputs !== void 0 || options.redactOutputs !== void 0 ? createPiiRedactor(options.redaction) : void 0,
1446
+ redactInputs: options.redactInputs,
1447
+ redactOutputs: options.redactOutputs,
1448
+ captureMode: options.captureMode ?? "safe",
1449
+ captureMaxBytes: validateCaptureMaxBytes(options.captureMaxBytes)
1450
+ };
1451
+ }
1452
+ function captureInput(value, capture) {
1453
+ const redacted = capture.redactor === void 0 || capture.redactInputs === void 0 ? value : applyRedaction(capture.redactor, value, capture.redactInputs);
1454
+ return sanitizeTraceValue(redacted, capture.captureMaxBytes);
1455
+ }
1456
+ function scoreMaxAttempts(retries) {
1457
+ if (retries === void 0) return 3;
1458
+ if (!Number.isSafeInteger(retries.maxAttempts) || retries.maxAttempts < 1) {
1459
+ throw new TypeError("Langfuse score retries.maxAttempts must be a positive integer.");
1460
+ }
1461
+ return retries.maxAttempts;
1462
+ }
1300
1463
  function assertScoreValue(value, dataType) {
1301
1464
  if (dataType === "NUMERIC") {
1302
1465
  if (typeof value !== "number") {
@@ -1436,20 +1599,15 @@ function asMetadata(value) {
1436
1599
  return isRecord(value) ? value : { value };
1437
1600
  }
1438
1601
  function eventStartTime(value) {
1439
- if (value instanceof Date) {
1440
- return Number.isNaN(value.getTime()) ? void 0 : value;
1441
- }
1442
- if (typeof value !== "string") {
1443
- return void 0;
1444
- }
1602
+ if (value === void 0) return void 0;
1445
1603
  const parsed = new Date(value);
1446
1604
  return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
1447
1605
  }
1448
1606
  var LangfuseRunObserver = class {
1449
- constructor(root, trace, promptRef, redaction) {
1607
+ constructor(root, observations, trace2, promptRef, redaction) {
1450
1608
  this.root = root;
1451
- this.trace = trace;
1452
- this.handle = this.buildHandle();
1609
+ this.observations = observations;
1610
+ this.trace = trace2;
1453
1611
  this.promptRef = promptRef;
1454
1612
  this.redactor = redaction.redactor;
1455
1613
  this.redactInputs = redaction.redactInputs;
@@ -1458,13 +1616,9 @@ var LangfuseRunObserver = class {
1458
1616
  this.captureMaxBytes = redaction.captureMaxBytes;
1459
1617
  }
1460
1618
  root;
1619
+ observations;
1461
1620
  trace;
1462
1621
  turnSpans = /* @__PURE__ */ new Map();
1463
- // Assigned by LangfuseAgentObserver.startRun so that the run can
1464
- // publish trace-handle updates back to the agent observer.
1465
- setCurrentHandle;
1466
- clearCurrentHandle;
1467
- handle;
1468
1622
  promptRef;
1469
1623
  redactor;
1470
1624
  redactInputs;
@@ -1491,7 +1645,7 @@ var LangfuseRunObserver = class {
1491
1645
  safeInput.tools = args.request.tools;
1492
1646
  safeInput.providerTools = args.request.providerTools;
1493
1647
  safeInput.outputSchema = args.request.outputSchema;
1494
- safeInput.additionalParams = args.request.additionalParams;
1648
+ safeInput.providerOptions = args.request.providerOptions;
1495
1649
  }
1496
1650
  const metadata = {
1497
1651
  turn: args.turn,
@@ -1499,7 +1653,7 @@ var LangfuseRunObserver = class {
1499
1653
  toolNames: args.request.tools.map((tool) => tool.name),
1500
1654
  providerToolNames: args.request.providerTools?.map((tool) => tool.name) ?? [],
1501
1655
  hasOutputSchema: args.request.outputSchema !== void 0,
1502
- additionalParamKeys: isRecord(args.request.additionalParams) ? Object.keys(args.request.additionalParams) : []
1656
+ providerOptionKeys: isRecord(args.request.providerOptions) ? Object.keys(args.request.providerOptions) : []
1503
1657
  };
1504
1658
  if (this.captureMode === "full" && args.providerRequest !== void 0) {
1505
1659
  metadata.providerRequest = args.providerRequest;
@@ -1507,7 +1661,7 @@ var LangfuseRunObserver = class {
1507
1661
  if (args.modelInfo !== void 0) {
1508
1662
  const modelInfo = {
1509
1663
  provider: args.modelInfo.provider,
1510
- defaultModel: args.modelInfo.defaultModel
1664
+ modelId: args.modelInfo.modelId
1511
1665
  };
1512
1666
  if (args.modelInfo.capabilities !== void 0) {
1513
1667
  modelInfo.capabilities = args.modelInfo.capabilities;
@@ -1517,7 +1671,7 @@ var LangfuseRunObserver = class {
1517
1671
  Object.assign(metadata, promptMetadata(this.promptRef));
1518
1672
  const generationAttributes = {
1519
1673
  input: this.redactInputValue(safeInput),
1520
- model: args.request.model ?? args.modelInfo?.defaultModel ?? "default",
1674
+ model: args.modelInfo?.modelId ?? "unknown",
1521
1675
  modelParameters: modelParameters(args.request),
1522
1676
  metadata: asMetadata(this.redactInputValue(metadata))
1523
1677
  };
@@ -1528,9 +1682,11 @@ var LangfuseRunObserver = class {
1528
1682
  isFallback: false
1529
1683
  };
1530
1684
  }
1531
- const generation = turn.startObservation(`model.turn.${args.turn}`, generationAttributes, {
1532
- asType: "generation"
1533
- });
1685
+ const generation = this.observations.generation(
1686
+ `model.turn.${args.turn}`,
1687
+ generationAttributes,
1688
+ turn
1689
+ );
1534
1690
  return new LangfuseGenerationObserver(generation, this, /* @__PURE__ */ new Date());
1535
1691
  }
1536
1692
  startTool(args) {
@@ -1544,7 +1700,7 @@ var LangfuseRunObserver = class {
1544
1700
  if (args.toolDefinition !== void 0) metadata.toolDefinition = args.toolDefinition;
1545
1701
  if (args.toolMetadata !== void 0) metadata.toolMetadata = args.toolMetadata;
1546
1702
  }
1547
- const tool = turn.startObservation(
1703
+ const tool = this.observations.tool(
1548
1704
  `tool.${args.toolName}`,
1549
1705
  {
1550
1706
  input: this.redactInputValue({
@@ -1553,14 +1709,16 @@ var LangfuseRunObserver = class {
1553
1709
  }),
1554
1710
  metadata: asMetadata(this.redactInputValue(metadata))
1555
1711
  },
1556
- { asType: "tool" }
1712
+ turn
1557
1713
  );
1558
- return new LangfuseToolObserver(tool, this);
1714
+ return new LangfuseToolObserver(tool, this, this.observations);
1559
1715
  }
1560
1716
  end(args) {
1561
1717
  this.closeAllTurns();
1562
- const redactedOutput = this.redactOutputValue(args.output);
1718
+ const observedOutput = args.status === "completed" ? { status: args.status, output: args.output, text: args.text } : { status: args.status, stage: args.stage, text: args.text };
1719
+ const redactedOutput = this.redactOutputValue(observedOutput);
1563
1720
  const metadata = {
1721
+ status: args.status,
1564
1722
  usage: args.usage,
1565
1723
  messageCount: args.messages.length,
1566
1724
  sources: this.redactOutputValue(args.sources),
@@ -1573,7 +1731,6 @@ var LangfuseRunObserver = class {
1573
1731
  output: redactedOutput,
1574
1732
  metadata
1575
1733
  }).end();
1576
- this.clearCurrentHandle?.();
1577
1734
  }
1578
1735
  error(args) {
1579
1736
  this.closeAllTurns();
@@ -1593,7 +1750,6 @@ var LangfuseRunObserver = class {
1593
1750
  },
1594
1751
  metadata
1595
1752
  }).end();
1596
- this.clearCurrentHandle?.();
1597
1753
  }
1598
1754
  event(args) {
1599
1755
  const metadata = asMetadata(this.redactOutputValue(args.attributes ?? {}));
@@ -1602,43 +1758,19 @@ var LangfuseRunObserver = class {
1602
1758
  attributes.level = args.level;
1603
1759
  }
1604
1760
  const startTime = eventStartTime(args.timestamp);
1605
- this.root.startObservation(args.name, attributes, {
1606
- asType: "event",
1607
- ...startTime === void 0 ? {} : { startTime }
1608
- });
1609
- }
1610
- getHandle() {
1611
- return this.handle;
1612
- }
1613
- buildHandle() {
1614
- return {
1615
- traceId: this.trace.traceId ?? "",
1616
- observationId: this.trace.observationId ?? "",
1617
- addAttributes: (attributes) => {
1618
- this.root.update({ metadata: asMetadata(this.redactOutputValue(attributes)) });
1619
- this.setCurrentHandle?.(this.handle);
1620
- },
1621
- addEvent: (name, attributes) => {
1622
- this.root.startObservation(
1623
- name,
1624
- { metadata: asMetadata(this.redactOutputValue(attributes ?? {})) },
1625
- { asType: "event" }
1626
- );
1627
- this.setCurrentHandle?.(this.handle);
1628
- }
1629
- };
1761
+ this.observations.event(args.name, attributes, this.root, startTime);
1630
1762
  }
1631
1763
  turnSpan(turn) {
1632
1764
  const existing = this.turnSpans.get(turn);
1633
1765
  if (existing !== void 0) {
1634
1766
  return existing;
1635
1767
  }
1636
- const span = this.root.startObservation(
1768
+ const span = this.observations.span(
1637
1769
  `turn.${turn}`,
1638
1770
  {
1639
1771
  metadata: { turn }
1640
1772
  },
1641
- { asType: "span" }
1773
+ this.root
1642
1774
  );
1643
1775
  this.turnSpans.set(turn, span);
1644
1776
  return span;
@@ -1719,12 +1851,14 @@ function textFromObservedAssistantContent(content) {
1719
1851
  return content.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n");
1720
1852
  }
1721
1853
  var LangfuseToolObserver = class {
1722
- constructor(tool, run) {
1854
+ constructor(tool, run, observations) {
1723
1855
  this.tool = tool;
1724
1856
  this.run = run;
1857
+ this.observations = observations;
1725
1858
  }
1726
1859
  tool;
1727
1860
  run;
1861
+ observations;
1728
1862
  childAgents = /* @__PURE__ */ new Map();
1729
1863
  childGenerations = /* @__PURE__ */ new Map();
1730
1864
  childTools = [];
@@ -1741,7 +1875,7 @@ var LangfuseToolObserver = class {
1741
1875
  if (child.type === "turn_start") {
1742
1876
  const promptMessage = isRecord(child.prompt) ? child.prompt : void 0;
1743
1877
  const historyMessages = Array.isArray(child.history) ? child.history.filter(isRecord) : [];
1744
- agent.startObservation(
1878
+ this.observations.event(
1745
1879
  `${agentLabel(agentId, agentName)}.turn.${childTurn}.start`,
1746
1880
  {
1747
1881
  input: this.run.redactInputValue({
@@ -1752,7 +1886,7 @@ var LangfuseToolObserver = class {
1752
1886
  this.run.redactInputValue(childMetadata(args, agentId, agentName, childTurn))
1753
1887
  )
1754
1888
  },
1755
- { asType: "event" }
1889
+ agent
1756
1890
  );
1757
1891
  return;
1758
1892
  }
@@ -1769,15 +1903,15 @@ var LangfuseToolObserver = class {
1769
1903
  input.tools = request.tools;
1770
1904
  input.providerTools = request.providerTools;
1771
1905
  input.outputSchema = request.outputSchema;
1772
- input.additionalParams = request.additionalParams;
1906
+ input.providerOptions = request.providerOptions;
1773
1907
  }
1774
1908
  const toolNames = Array.isArray(request.tools) ? request.tools.filter(isRecord).map((tool) => tool.name).filter((name) => typeof name === "string") : [];
1775
1909
  const providerToolNames = Array.isArray(request.providerTools) ? request.providerTools.filter(isRecord).map((tool) => tool.name).filter((name) => typeof name === "string") : [];
1776
- const generation = agent.startObservation(
1910
+ const generation = this.observations.generation(
1777
1911
  `${agentLabel(agentId, agentName)}.model.turn.${childTurn}`,
1778
1912
  {
1779
1913
  input: this.run.redactInputValue(input),
1780
- model: typeof request.model === "string" ? request.model : typeof modelInfo?.defaultModel === "string" ? modelInfo.defaultModel : "default",
1914
+ model: typeof modelInfo?.modelId === "string" ? modelInfo.modelId : "unknown",
1781
1915
  modelParameters: modelParameters(request),
1782
1916
  metadata: asMetadata(
1783
1917
  this.run.redactInputValue({
@@ -1790,7 +1924,7 @@ var LangfuseToolObserver = class {
1790
1924
  })
1791
1925
  )
1792
1926
  },
1793
- { asType: "generation" }
1927
+ agent
1794
1928
  );
1795
1929
  this.childGenerations.set(generationKey(agentId, childTurn), {
1796
1930
  generation,
@@ -1830,7 +1964,7 @@ var LangfuseToolObserver = class {
1830
1964
  if (child.type === "source" || child.type === "provider_tool_call") {
1831
1965
  const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
1832
1966
  const parent = childGeneration?.generation ?? agent;
1833
- parent.startObservation(
1967
+ this.observations.event(
1834
1968
  `${agentLabel(agentId, agentName)}.${child.type}`,
1835
1969
  {
1836
1970
  output: this.run.redactOutputValue(
@@ -1840,12 +1974,12 @@ var LangfuseToolObserver = class {
1840
1974
  this.run.redactOutputValue(childMetadata(args, agentId, agentName, childTurn))
1841
1975
  )
1842
1976
  },
1843
- { asType: "event" }
1977
+ parent
1844
1978
  );
1845
1979
  return;
1846
1980
  }
1847
1981
  if (child.type === "guardrail_decision") {
1848
- agent.startObservation(
1982
+ this.observations.guardrail(
1849
1983
  `${agentLabel(agentId, agentName)}.guardrail`,
1850
1984
  {
1851
1985
  output: this.run.redactOutputValue(child.decision),
@@ -1853,7 +1987,7 @@ var LangfuseToolObserver = class {
1853
1987
  this.run.redactOutputValue(childMetadata(args, agentId, agentName, childTurn))
1854
1988
  )
1855
1989
  },
1856
- { asType: "guardrail" }
1990
+ agent
1857
1991
  ).end();
1858
1992
  return;
1859
1993
  }
@@ -1863,14 +1997,13 @@ var LangfuseToolObserver = class {
1863
1997
  output: this.run.redactOutputValue({ toolCall: child.toolCall })
1864
1998
  });
1865
1999
  const toolCall = child.toolCall;
1866
- const toolCallFunction = isRecord(toolCall.function) ? toolCall.function : void 0;
1867
- const toolName = typeof toolCallFunction?.name === "string" ? toolCallFunction.name : "tool";
1868
- const toolCallId = typeof toolCall.callId === "string" ? toolCall.callId : typeof toolCall.id === "string" ? toolCall.id : void 0;
1869
- const childTool = agent.startObservation(
2000
+ const toolName = typeof toolCall.toolName === "string" ? toolCall.toolName : "tool";
2001
+ const toolCallId = typeof toolCall.toolCallId === "string" ? toolCall.toolCallId : typeof toolCall.callId === "string" ? toolCall.callId : void 0;
2002
+ const childTool = this.observations.tool(
1870
2003
  `${agentLabel(agentId, agentName)}.${toolName}`,
1871
2004
  {
1872
2005
  input: this.run.redactInputValue({
1873
- args: toolCallFunction?.arguments ?? {},
2006
+ args: toolCall.input,
1874
2007
  toolCall
1875
2008
  }),
1876
2009
  metadata: asMetadata(
@@ -1881,7 +2014,7 @@ var LangfuseToolObserver = class {
1881
2014
  })
1882
2015
  )
1883
2016
  },
1884
- { asType: "tool" }
2017
+ agent
1885
2018
  );
1886
2019
  const childToolRecord = {
1887
2020
  agentId,
@@ -1917,13 +2050,18 @@ var LangfuseToolObserver = class {
1917
2050
  return;
1918
2051
  }
1919
2052
  if (child.type === "final") {
2053
+ const result = isRecord(child.result) ? child.result : {};
1920
2054
  const update = {
1921
- output: this.run.redactOutputValue(child.output)
2055
+ output: this.run.redactOutputValue({
2056
+ status: result.status,
2057
+ output: result.output,
2058
+ text: result.text
2059
+ })
1922
2060
  };
1923
2061
  const metadata = {};
1924
- if (isRecord(child.usage)) metadata.usage = child.usage;
1925
- if (this.run.isFullCapture() && Array.isArray(child.messages)) {
1926
- metadata.messages = this.run.redactTranscript(child.messages);
2062
+ if (isRecord(result.usage)) metadata.usage = result.usage;
2063
+ if (this.run.isFullCapture() && Array.isArray(result.messages)) {
2064
+ metadata.messages = this.run.redactTranscript(result.messages);
1927
2065
  }
1928
2066
  if (Object.keys(metadata).length > 0) update.metadata = metadata;
1929
2067
  agent.update(update).end();
@@ -1989,14 +2127,14 @@ var LangfuseToolObserver = class {
1989
2127
  if (existing !== void 0) {
1990
2128
  return existing;
1991
2129
  }
1992
- const agent = this.tool.startObservation(
2130
+ const agent = this.observations.agent(
1993
2131
  `${agentLabel(agentId, agentName)}.run`,
1994
2132
  {
1995
2133
  metadata: asMetadata(
1996
2134
  this.run.redactInputValue(childMetadata(args, agentId, agentName, args.turn))
1997
2135
  )
1998
2136
  },
1999
- { asType: "agent" }
2137
+ this.tool
2000
2138
  );
2001
2139
  this.childAgents.set(agentId, agent);
2002
2140
  return agent;
@@ -2032,12 +2170,8 @@ var LangfuseToolObserver = class {
2032
2170
  };
2033
2171
  export {
2034
2172
  DEFAULT_PATTERNS,
2173
+ LangfuseClient,
2035
2174
  LangfuseScoreError,
2036
- createLangfuseDatasetClient,
2037
- createLangfuseEvalReporter,
2038
- createLangfusePromptClient,
2039
- createPiiRedactor,
2040
- langfuse,
2041
- runEvalAsExperiment
2175
+ createPiiRedactor
2042
2176
  };
2043
2177
  //# sourceMappingURL=index.js.map