@anvia/langfuse 1.0.0-rc.1 → 1.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -448
- package/dist/index.d.ts +83 -46
- package/dist/index.js +764 -610
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,116 +1,155 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
60
|
-
|
|
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
|
|
63
|
-
|
|
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
|
|
66
|
-
|
|
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
|
|
69
|
-
return
|
|
112
|
+
function cloneRegex(source, flags) {
|
|
113
|
+
return new RegExp(source.source, flags + source.flags.replace(/g/g, ""));
|
|
70
114
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
|
107
|
-
|
|
108
|
-
return void 0;
|
|
109
|
-
}
|
|
110
|
-
return value[langfuseResolvedConfigSymbol];
|
|
148
|
+
function redactMessageContent(value, redactStringFn) {
|
|
149
|
+
return redactValue(value, 0, redactStringFn);
|
|
111
150
|
}
|
|
112
|
-
function
|
|
113
|
-
return
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
312
|
+
`Langfuse score batch failed after ${this.maxAttempts} attempts`,
|
|
277
313
|
scores,
|
|
278
314
|
lastError
|
|
279
315
|
);
|
|
@@ -301,24 +337,259 @@ var ScoreQueue = class {
|
|
|
301
337
|
throw permanentError;
|
|
302
338
|
}
|
|
303
339
|
}
|
|
304
|
-
};
|
|
305
|
-
function buildScoreBody(score) {
|
|
306
|
-
const body = {
|
|
307
|
-
traceId: score.traceId,
|
|
308
|
-
name: score.name,
|
|
309
|
-
value: score.value
|
|
340
|
+
};
|
|
341
|
+
function buildScoreBody(score) {
|
|
342
|
+
const body = {
|
|
343
|
+
traceId: score.traceId,
|
|
344
|
+
name: score.name,
|
|
345
|
+
value: score.value
|
|
346
|
+
};
|
|
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
|
+
const value = {
|
|
452
|
+
anviaTraceValue: "omitted",
|
|
453
|
+
reason
|
|
454
|
+
};
|
|
455
|
+
if (originalBytes !== void 0) value.originalBytes = originalBytes;
|
|
456
|
+
return value;
|
|
457
|
+
}
|
|
458
|
+
function boundedPreview(value, originalBytes, maxBytes) {
|
|
459
|
+
let low = 0;
|
|
460
|
+
let high = value.length;
|
|
461
|
+
while (low < high) {
|
|
462
|
+
const middle = Math.ceil((low + high) / 2);
|
|
463
|
+
const candidate = {
|
|
464
|
+
anviaTraceValue: "truncated",
|
|
465
|
+
originalBytes,
|
|
466
|
+
preview: value.slice(0, middle)
|
|
467
|
+
};
|
|
468
|
+
if (utf8Bytes(JSON.stringify(candidate)) <= maxBytes) {
|
|
469
|
+
low = middle;
|
|
470
|
+
} else {
|
|
471
|
+
high = middle - 1;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return value.slice(0, low);
|
|
475
|
+
}
|
|
476
|
+
function utf8Bytes(value) {
|
|
477
|
+
return typeof Buffer === "undefined" ? new TextEncoder().encode(value).byteLength : Buffer.byteLength(value, "utf8");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/helpers.ts
|
|
481
|
+
function modelInputMessage(message) {
|
|
482
|
+
const { metadata: _metadata, ...result } = message;
|
|
483
|
+
return result;
|
|
484
|
+
}
|
|
485
|
+
function modelInputMessages(messages) {
|
|
486
|
+
return messages.map(modelInputMessage);
|
|
487
|
+
}
|
|
488
|
+
function modelParameters(request) {
|
|
489
|
+
const params = {};
|
|
490
|
+
if (request.temperature !== void 0) params.temperature = request.temperature;
|
|
491
|
+
if (request.maxTokens !== void 0) params.maxTokens = request.maxTokens;
|
|
492
|
+
if (request.toolChoice !== void 0) {
|
|
493
|
+
params.toolChoice = typeof request.toolChoice === "string" ? request.toolChoice : request.toolChoice.name;
|
|
494
|
+
}
|
|
495
|
+
return params;
|
|
496
|
+
}
|
|
497
|
+
function usageDetails(usage) {
|
|
498
|
+
if (usage.details !== void 0 && Object.keys(usage.details).length > 0) {
|
|
499
|
+
return { ...usage.details };
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
input: usage.inputTokens,
|
|
503
|
+
output: usage.outputTokens,
|
|
504
|
+
total: usage.totalTokens
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function usageDetailsFromRecord(usage) {
|
|
508
|
+
if (isRecord(usage.details)) {
|
|
509
|
+
const details = Object.fromEntries(
|
|
510
|
+
Object.entries(usage.details).filter(
|
|
511
|
+
(entry) => typeof entry[1] === "number" && Number.isFinite(entry[1]) && entry[1] >= 0
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
if (Object.keys(details).length > 0) {
|
|
515
|
+
return details;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
input: numberValue(usage.inputTokens) ?? 0,
|
|
520
|
+
output: numberValue(usage.outputTokens) ?? 0,
|
|
521
|
+
total: numberValue(usage.totalTokens) ?? (numberValue(usage.inputTokens) ?? 0) + (numberValue(usage.outputTokens) ?? 0)
|
|
310
522
|
};
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
523
|
+
}
|
|
524
|
+
function childMetadata(args, agentId, agentName, childTurn) {
|
|
525
|
+
return {
|
|
526
|
+
source: "agent_tool_event",
|
|
527
|
+
childAgentId: agentId,
|
|
528
|
+
childAgentName: agentName,
|
|
529
|
+
childTurn,
|
|
530
|
+
parentToolName: args.toolName,
|
|
531
|
+
parentInternalCallId: args.internalCallId,
|
|
532
|
+
parentToolCallId: args.toolCallId
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function generationKey(agentId, turn) {
|
|
536
|
+
return `${agentId}:${turn}`;
|
|
537
|
+
}
|
|
538
|
+
function agentLabel(agentId, agentName) {
|
|
539
|
+
return (agentName ?? agentId).replaceAll(/\s+/g, "_");
|
|
540
|
+
}
|
|
541
|
+
function isRecord(value) {
|
|
542
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
543
|
+
}
|
|
544
|
+
function numberValue(value) {
|
|
545
|
+
return typeof value === "number" ? value : void 0;
|
|
546
|
+
}
|
|
547
|
+
function emptyToUndefined(value) {
|
|
548
|
+
return value === void 0 || value.length === 0 ? void 0 : value;
|
|
549
|
+
}
|
|
550
|
+
function errorMessage(error) {
|
|
551
|
+
return error instanceof Error ? error.message : String(error);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/config.ts
|
|
555
|
+
var langfuseResolvedConfigSymbol = /* @__PURE__ */ Symbol.for(
|
|
556
|
+
"@anvia/langfuse.resolvedConfig"
|
|
557
|
+
);
|
|
558
|
+
function resolveLangfuseConfig(options = {}, fallback) {
|
|
559
|
+
return {
|
|
560
|
+
publicKey: resolveStringOption(
|
|
561
|
+
options.publicKey,
|
|
562
|
+
fallback?.publicKey,
|
|
563
|
+
process.env.LANGFUSE_PUBLIC_KEY
|
|
564
|
+
),
|
|
565
|
+
secretKey: resolveStringOption(
|
|
566
|
+
options.secretKey,
|
|
567
|
+
fallback?.secretKey,
|
|
568
|
+
process.env.LANGFUSE_SECRET_KEY
|
|
569
|
+
),
|
|
570
|
+
baseUrl: resolveStringOption(options.baseUrl, fallback?.baseUrl, process.env.LANGFUSE_BASE_URL) ?? "https://cloud.langfuse.com",
|
|
571
|
+
environment: resolveStringOption(
|
|
572
|
+
options.environment,
|
|
573
|
+
fallback?.environment,
|
|
574
|
+
process.env.LANGFUSE_TRACING_ENVIRONMENT
|
|
575
|
+
),
|
|
576
|
+
release: resolveStringOption(options.release, fallback?.release, process.env.LANGFUSE_RELEASE),
|
|
577
|
+
serviceName: resolveStringOption(
|
|
578
|
+
options.serviceName,
|
|
579
|
+
fallback?.serviceName,
|
|
580
|
+
process.env.LANGFUSE_SERVICE_NAME
|
|
581
|
+
),
|
|
582
|
+
timeoutMs: options.timeoutMs ?? fallback?.timeoutMs ?? 3e4
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function getResolvedLangfuseConfig(value) {
|
|
586
|
+
if (typeof value !== "object" || value === null) {
|
|
587
|
+
return void 0;
|
|
320
588
|
}
|
|
321
|
-
return
|
|
589
|
+
return value[langfuseResolvedConfigSymbol];
|
|
590
|
+
}
|
|
591
|
+
function resolveStringOption(option, fallback, envVar) {
|
|
592
|
+
return emptyToUndefined(option) ?? emptyToUndefined(fallback) ?? emptyToUndefined(envVar);
|
|
322
593
|
}
|
|
323
594
|
|
|
324
595
|
// src/dataset-client.ts
|
|
@@ -377,7 +648,8 @@ function createLangfuseDatasetClient(tracing, options = {}) {
|
|
|
377
648
|
if (dataset.metadata !== void 0) result.metadata = dataset.metadata;
|
|
378
649
|
return result;
|
|
379
650
|
},
|
|
380
|
-
async getDataset(
|
|
651
|
+
async getDataset(options2) {
|
|
652
|
+
const { name } = options2;
|
|
381
653
|
const items = [];
|
|
382
654
|
let description;
|
|
383
655
|
let metadata;
|
|
@@ -417,7 +689,8 @@ function createLangfuseDatasetClient(tracing, options = {}) {
|
|
|
417
689
|
if (metadata !== void 0) dataset.metadata = metadata;
|
|
418
690
|
return dataset;
|
|
419
691
|
},
|
|
420
|
-
async upsertItems(
|
|
692
|
+
async upsertItems(options2) {
|
|
693
|
+
const { name, items } = options2;
|
|
421
694
|
const url = `${baseUrl}/api/public/datasets/${encodeURIComponent(name)}/items`;
|
|
422
695
|
await request(url, {
|
|
423
696
|
method: "POST",
|
|
@@ -427,7 +700,7 @@ function createLangfuseDatasetClient(tracing, options = {}) {
|
|
|
427
700
|
async runExperiment(opts) {
|
|
428
701
|
let items = opts.items;
|
|
429
702
|
if (items === void 0) {
|
|
430
|
-
const dataset = await this.getDataset(opts.datasetName);
|
|
703
|
+
const dataset = await this.getDataset({ name: opts.datasetName });
|
|
431
704
|
items = dataset.items;
|
|
432
705
|
}
|
|
433
706
|
if (items === void 0 || items.length === 0) {
|
|
@@ -508,7 +781,8 @@ import {
|
|
|
508
781
|
} from "@anvia/core/evals";
|
|
509
782
|
var DEFAULT_TRUNCATE_BYTES = 2048;
|
|
510
783
|
function createLangfuseEvalReporter(tracing, options = {}) {
|
|
511
|
-
const onMissingTrace = options.onMissingTrace ??
|
|
784
|
+
const onMissingTrace = options.onMissingTrace ?? "ignore";
|
|
785
|
+
const traceObserver = options.traceObserver ?? "langfuse";
|
|
512
786
|
const truncateAt = options.truncateInputAt ?? DEFAULT_TRUNCATE_BYTES;
|
|
513
787
|
const includeMessages = options.includeMessages ?? true;
|
|
514
788
|
const includeContext = options.includeContext ?? false;
|
|
@@ -517,18 +791,20 @@ function createLangfuseEvalReporter(tracing, options = {}) {
|
|
|
517
791
|
if (args.outcome.outcome === "invalid" && options.publishInvalid !== true) {
|
|
518
792
|
return;
|
|
519
793
|
}
|
|
520
|
-
const
|
|
794
|
+
const trace2 = args.trace ?? resolveEvalTraceRef({
|
|
521
795
|
output: args.output,
|
|
522
796
|
input: args.case.input,
|
|
523
797
|
metadata: args.case.metadata
|
|
524
798
|
});
|
|
525
|
-
if (
|
|
799
|
+
if (trace2?.traceId === void 0 || trace2.traceId.length === 0 || trace2.observer !== void 0 && trace2.observer !== traceObserver) {
|
|
526
800
|
if (onMissingTrace === "throw") {
|
|
527
|
-
throw new Error(
|
|
801
|
+
throw new Error(
|
|
802
|
+
`Langfuse eval reporter requires traceId from observer ${JSON.stringify(traceObserver)}`
|
|
803
|
+
);
|
|
528
804
|
}
|
|
529
805
|
if (onMissingTrace === "warn") {
|
|
530
806
|
console.warn(
|
|
531
|
-
"[anvia/langfuse] eval reporter dropped score because no
|
|
807
|
+
"[anvia/langfuse] eval reporter dropped score because no matching trace was found",
|
|
532
808
|
{ caseId: args.case.id, metric: args.metric.name }
|
|
533
809
|
);
|
|
534
810
|
}
|
|
@@ -547,11 +823,11 @@ function createLangfuseEvalReporter(tracing, options = {}) {
|
|
|
547
823
|
});
|
|
548
824
|
const configId = resolveConfigId(args.metric);
|
|
549
825
|
const score = {
|
|
550
|
-
traceId:
|
|
826
|
+
traceId: trace2.traceId,
|
|
551
827
|
name: args.metric.name,
|
|
552
828
|
value: projection.value
|
|
553
829
|
};
|
|
554
|
-
if (
|
|
830
|
+
if (trace2.observationId !== void 0) score.observationId = trace2.observationId;
|
|
555
831
|
if (args.metric.dataType !== void 0) score.dataType = args.metric.dataType;
|
|
556
832
|
if (configId !== void 0) score.configId = configId;
|
|
557
833
|
if (projection.explanation !== void 0) score.comment = projection.explanation;
|
|
@@ -649,18 +925,20 @@ function readMessages(output) {
|
|
|
649
925
|
|
|
650
926
|
// src/experiment-runner.ts
|
|
651
927
|
import { runEvalSuite } from "@anvia/core/evals";
|
|
652
|
-
async function
|
|
928
|
+
async function runLangfuseEvalExperiment(client, options) {
|
|
929
|
+
const evalOptions = options.suite;
|
|
930
|
+
const experimentOptions = options.experiment;
|
|
653
931
|
const clientOptions = {};
|
|
654
932
|
if (experimentOptions.pageSize !== void 0) clientOptions.pageSize = experimentOptions.pageSize;
|
|
655
933
|
if (experimentOptions.timeoutMs !== void 0)
|
|
656
934
|
clientOptions.timeoutMs = experimentOptions.timeoutMs;
|
|
657
|
-
const
|
|
935
|
+
const datasetClient = client.datasetClient(clientOptions);
|
|
658
936
|
const suiteOptions = experimentOptions.publishScores === true ? {
|
|
659
937
|
...evalOptions,
|
|
660
938
|
reporters: [
|
|
661
939
|
...evalOptions.reporters ?? [],
|
|
662
940
|
createLangfuseEvalReporter(
|
|
663
|
-
|
|
941
|
+
client,
|
|
664
942
|
experimentOptions.reporterOptions
|
|
665
943
|
)
|
|
666
944
|
]
|
|
@@ -705,30 +983,30 @@ async function runEvalAsExperiment(evalOptions, experimentOptions) {
|
|
|
705
983
|
};
|
|
706
984
|
}
|
|
707
985
|
const output = result.output ?? void 0;
|
|
708
|
-
const
|
|
709
|
-
return { output, trace };
|
|
986
|
+
const trace2 = readTraceFromOutput(result.output);
|
|
987
|
+
return { output, trace: trace2 };
|
|
710
988
|
}
|
|
711
989
|
};
|
|
712
990
|
if (experimentOptions.description !== void 0) {
|
|
713
991
|
runOptions.description = experimentOptions.description;
|
|
714
992
|
}
|
|
715
993
|
if (experimentOptions.metadata !== void 0) runOptions.metadata = experimentOptions.metadata;
|
|
716
|
-
const datasetRun = await
|
|
994
|
+
const datasetRun = await datasetClient.runExperiment(runOptions);
|
|
717
995
|
return { suite, datasetRun };
|
|
718
996
|
}
|
|
719
997
|
function readTraceFromOutput(output) {
|
|
720
998
|
if (typeof output !== "object" || output === null || !("trace" in output)) {
|
|
721
999
|
return void 0;
|
|
722
1000
|
}
|
|
723
|
-
const
|
|
724
|
-
if (typeof
|
|
1001
|
+
const trace2 = output.trace;
|
|
1002
|
+
if (typeof trace2 !== "object" || trace2 === null) {
|
|
725
1003
|
return void 0;
|
|
726
1004
|
}
|
|
727
|
-
const traceId =
|
|
1005
|
+
const traceId = trace2.traceId;
|
|
728
1006
|
if (typeof traceId !== "string") {
|
|
729
1007
|
return void 0;
|
|
730
1008
|
}
|
|
731
|
-
const observationId =
|
|
1009
|
+
const observationId = trace2.observationId;
|
|
732
1010
|
if (typeof observationId === "string") {
|
|
733
1011
|
return { traceId, observationId };
|
|
734
1012
|
}
|
|
@@ -761,7 +1039,8 @@ function createLangfusePromptClient(tracing, options = {}) {
|
|
|
761
1039
|
}
|
|
762
1040
|
return await response.json();
|
|
763
1041
|
}
|
|
764
|
-
async function getPrompt(
|
|
1042
|
+
async function getPrompt(options2) {
|
|
1043
|
+
const { name, ...opts } = options2;
|
|
765
1044
|
const key = `${name}::${opts.version ?? ""}::${opts.label ?? ""}`;
|
|
766
1045
|
const ttl = opts.cacheTtlMs ?? defaultTtl;
|
|
767
1046
|
if (opts.refresh !== true) {
|
|
@@ -790,18 +1069,18 @@ function createLangfusePromptClient(tracing, options = {}) {
|
|
|
790
1069
|
cache.set(key, { prompt, expiresAt: Date.now() + ttl });
|
|
791
1070
|
return prompt;
|
|
792
1071
|
}
|
|
793
|
-
function getPromptText(
|
|
794
|
-
return getPrompt(
|
|
1072
|
+
function getPromptText(options2) {
|
|
1073
|
+
return getPrompt(options2).then((prompt) => {
|
|
795
1074
|
if (typeof prompt.prompt !== "string") {
|
|
796
|
-
throw new Error(`Prompt ${name} is a chat prompt; expected text`);
|
|
1075
|
+
throw new Error(`Prompt ${options2.name} is a chat prompt; expected text`);
|
|
797
1076
|
}
|
|
798
1077
|
return prompt.prompt;
|
|
799
1078
|
});
|
|
800
1079
|
}
|
|
801
|
-
function getPromptChat(
|
|
802
|
-
return getPrompt(
|
|
1080
|
+
function getPromptChat(options2) {
|
|
1081
|
+
return getPrompt(options2).then((prompt) => {
|
|
803
1082
|
if (typeof prompt.prompt === "string") {
|
|
804
|
-
throw new Error(`Prompt ${name} is a text prompt; expected chat`);
|
|
1083
|
+
throw new Error(`Prompt ${options2.name} is a text prompt; expected chat`);
|
|
805
1084
|
}
|
|
806
1085
|
return prompt.prompt;
|
|
807
1086
|
});
|
|
@@ -828,313 +1107,75 @@ function normalizePrompt(raw, type) {
|
|
|
828
1107
|
if (typeof content !== "string") {
|
|
829
1108
|
throw new Error("Expected chat message content to be a string");
|
|
830
1109
|
}
|
|
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
|
-
}
|
|
1110
|
+
if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") {
|
|
1111
|
+
throw new Error(`Unexpected chat message role: ${String(role)}`);
|
|
985
1112
|
}
|
|
986
|
-
return
|
|
987
|
-
}
|
|
988
|
-
return value;
|
|
989
|
-
}
|
|
990
|
-
function redactMessage(message, redactStringFn) {
|
|
991
|
-
if (message.role === "system") {
|
|
992
|
-
return { ...message, content: redactStringFn(message.content) };
|
|
993
|
-
}
|
|
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);
|
|
1113
|
+
return { role, content };
|
|
1114
|
+
});
|
|
1004
1115
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
-
);
|
|
1116
|
+
function buildAuthHeader2(publicKey, secretKey) {
|
|
1117
|
+
if (publicKey === void 0 || secretKey === void 0) {
|
|
1118
|
+
return {};
|
|
1025
1119
|
}
|
|
1026
|
-
|
|
1120
|
+
const encoded = Buffer.from(`${publicKey}:${secretKey}`).toString("base64");
|
|
1121
|
+
return { Authorization: `Basic ${encoded}` };
|
|
1027
1122
|
}
|
|
1028
|
-
function
|
|
1029
|
-
validateCaptureMaxBytes(maxBytes);
|
|
1030
|
-
const sanitized = sanitizeValue(value, 0, /* @__PURE__ */ new WeakSet());
|
|
1031
|
-
let serialized;
|
|
1123
|
+
async function readErrorBody2(response) {
|
|
1032
1124
|
try {
|
|
1033
|
-
|
|
1125
|
+
return await response.text();
|
|
1034
1126
|
} catch {
|
|
1035
|
-
return
|
|
1036
|
-
}
|
|
1037
|
-
const originalBytes = utf8Bytes(serialized);
|
|
1038
|
-
if (originalBytes <= maxBytes) {
|
|
1039
|
-
return sanitized;
|
|
1127
|
+
return "<unreadable>";
|
|
1040
1128
|
}
|
|
1041
|
-
const preview = boundedPreview(serialized, originalBytes, maxBytes);
|
|
1042
|
-
return {
|
|
1043
|
-
anviaTraceValue: "truncated",
|
|
1044
|
-
originalBytes,
|
|
1045
|
-
preview
|
|
1046
|
-
};
|
|
1047
1129
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
if (/^data:[^;,]+;base64,/i.test(value)) {
|
|
1054
|
-
return omitted("base64", utf8Bytes(value));
|
|
1055
|
-
}
|
|
1056
|
-
return value;
|
|
1130
|
+
|
|
1131
|
+
// src/tracing.ts
|
|
1132
|
+
var LangfuseObservationFactory = class {
|
|
1133
|
+
constructor(tracer) {
|
|
1134
|
+
this.tracer = tracer;
|
|
1057
1135
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
return
|
|
1136
|
+
tracer;
|
|
1137
|
+
agent(name, attributes, parent) {
|
|
1138
|
+
return new LangfuseAgent({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1061
1139
|
}
|
|
1062
|
-
|
|
1063
|
-
return
|
|
1140
|
+
span(name, attributes, parent) {
|
|
1141
|
+
return new LangfuseSpan({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1064
1142
|
}
|
|
1065
|
-
|
|
1066
|
-
return
|
|
1143
|
+
generation(name, attributes, parent) {
|
|
1144
|
+
return new LangfuseGeneration({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1067
1145
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const result2 = value.map((entry) => sanitizeValue(entry, depth + 1, seen));
|
|
1071
|
-
seen.delete(value);
|
|
1072
|
-
return result2;
|
|
1146
|
+
tool(name, attributes, parent) {
|
|
1147
|
+
return new LangfuseTool({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1073
1148
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
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);
|
|
1149
|
+
guardrail(name, attributes, parent) {
|
|
1150
|
+
return new LangfuseGuardrail({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1082
1151
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
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
|
-
}
|
|
1152
|
+
event(name, attributes, parent, timestamp) {
|
|
1153
|
+
const endTime = timestamp ?? /* @__PURE__ */ new Date();
|
|
1154
|
+
return new LangfuseEvent({
|
|
1155
|
+
otelSpan: this.startSpan(name, parent, timestamp),
|
|
1156
|
+
attributes,
|
|
1157
|
+
timestamp: endTime
|
|
1158
|
+
});
|
|
1108
1159
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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);
|
|
1160
|
+
startSpan(name, parent, startTime) {
|
|
1161
|
+
const parentContext = parent === void 0 ? ROOT_CONTEXT : "otelSpan" in parent ? trace.setSpan(ROOT_CONTEXT, parent.otelSpan) : trace.setSpanContext(ROOT_CONTEXT, parent);
|
|
1162
|
+
return this.tracer.startSpan(name, startTime === void 0 ? {} : { startTime }, parentContext);
|
|
1119
1163
|
}
|
|
1120
1164
|
};
|
|
1121
|
-
var
|
|
1122
|
-
processor;
|
|
1123
|
-
sdk;
|
|
1165
|
+
var LangfuseClient = class {
|
|
1124
1166
|
[langfuseResolvedConfigSymbol];
|
|
1125
1167
|
publicKey;
|
|
1126
1168
|
secretKey;
|
|
1127
1169
|
baseUrl;
|
|
1128
1170
|
serviceName;
|
|
1129
1171
|
timeoutMs;
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
constructor(options) {
|
|
1172
|
+
options;
|
|
1173
|
+
resource;
|
|
1174
|
+
initialization;
|
|
1175
|
+
closePromise;
|
|
1176
|
+
closed = false;
|
|
1177
|
+
constructor(options = {}) {
|
|
1178
|
+
this.options = options;
|
|
1138
1179
|
const resolvedConfig = resolveLangfuseConfig(options);
|
|
1139
1180
|
this[langfuseResolvedConfigSymbol] = resolvedConfig;
|
|
1140
1181
|
this.publicKey = resolvedConfig.publicKey;
|
|
@@ -1142,50 +1183,82 @@ var LangfuseAgentObserver = class {
|
|
|
1142
1183
|
this.baseUrl = resolvedConfig.baseUrl;
|
|
1143
1184
|
this.serviceName = resolvedConfig.serviceName;
|
|
1144
1185
|
this.timeoutMs = resolvedConfig.timeoutMs;
|
|
1145
|
-
|
|
1146
|
-
|
|
1186
|
+
}
|
|
1187
|
+
observer(options = {}) {
|
|
1188
|
+
this.assertOpen();
|
|
1189
|
+
return new LangfuseAgentObserver(this, resolveLangfuseCapture(options));
|
|
1190
|
+
}
|
|
1191
|
+
evalReporter(options = {}) {
|
|
1192
|
+
this.assertOpen();
|
|
1193
|
+
const reporter = createLangfuseEvalReporter(this, options);
|
|
1194
|
+
return {
|
|
1195
|
+
report: (args) => {
|
|
1196
|
+
this.assertOpen();
|
|
1197
|
+
return reporter.report(args);
|
|
1198
|
+
}
|
|
1147
1199
|
};
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1200
|
+
}
|
|
1201
|
+
promptClient(options = {}) {
|
|
1202
|
+
this.assertOpen();
|
|
1203
|
+
const prompts = createLangfusePromptClient(this, options);
|
|
1204
|
+
return {
|
|
1205
|
+
getPrompt: (getOptions) => {
|
|
1206
|
+
this.assertOpen();
|
|
1207
|
+
return prompts.getPrompt(getOptions);
|
|
1208
|
+
},
|
|
1209
|
+
getPromptText: (getOptions) => {
|
|
1210
|
+
this.assertOpen();
|
|
1211
|
+
return prompts.getPromptText(getOptions);
|
|
1212
|
+
},
|
|
1213
|
+
getPromptChat: (getOptions) => {
|
|
1214
|
+
this.assertOpen();
|
|
1215
|
+
return prompts.getPromptChat(getOptions);
|
|
1216
|
+
},
|
|
1217
|
+
refresh: () => {
|
|
1218
|
+
this.assertOpen();
|
|
1219
|
+
prompts.refresh();
|
|
1220
|
+
}
|
|
1157
1221
|
};
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1222
|
+
}
|
|
1223
|
+
datasetClient(options = {}) {
|
|
1224
|
+
this.assertOpen();
|
|
1225
|
+
const datasets = createLangfuseDatasetClient(this, options);
|
|
1226
|
+
const client = this;
|
|
1227
|
+
return {
|
|
1228
|
+
createDataset(dataset) {
|
|
1229
|
+
client.assertOpen();
|
|
1230
|
+
return datasets.createDataset(dataset);
|
|
1231
|
+
},
|
|
1232
|
+
getDataset(getOptions) {
|
|
1233
|
+
client.assertOpen();
|
|
1234
|
+
return datasets.getDataset(getOptions);
|
|
1235
|
+
},
|
|
1236
|
+
upsertItems(upsertOptions) {
|
|
1237
|
+
client.assertOpen();
|
|
1238
|
+
return datasets.upsertItems(upsertOptions);
|
|
1239
|
+
},
|
|
1240
|
+
runExperiment(experimentOptions) {
|
|
1241
|
+
client.assertOpen();
|
|
1242
|
+
return datasets.runExperiment(experimentOptions);
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
runEvalExperiment(options) {
|
|
1247
|
+
this.assertOpen();
|
|
1248
|
+
return runLangfuseEvalExperiment(this, options);
|
|
1249
|
+
}
|
|
1250
|
+
async startObservedRun(args, capture) {
|
|
1251
|
+
const resource = await this.resources();
|
|
1182
1252
|
const traceId = args.trace?.traceId;
|
|
1183
|
-
const capturedInput =
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1253
|
+
const capturedInput = captureInput(
|
|
1254
|
+
{
|
|
1255
|
+
instructions: args.instructions,
|
|
1256
|
+
prompt: args.prompt,
|
|
1257
|
+
history: args.history
|
|
1258
|
+
},
|
|
1259
|
+
capture
|
|
1260
|
+
);
|
|
1261
|
+
const capturedTraceMetadata = captureInput(args.trace?.metadata ?? {}, capture);
|
|
1189
1262
|
const metadata = {
|
|
1190
1263
|
agentName: args.agentName,
|
|
1191
1264
|
agentDescription: args.agentDescription,
|
|
@@ -1201,69 +1274,41 @@ var LangfuseAgentObserver = class {
|
|
|
1201
1274
|
input: capturedInput,
|
|
1202
1275
|
metadata
|
|
1203
1276
|
};
|
|
1204
|
-
if (args.trace?.version !== void 0)
|
|
1205
|
-
|
|
1206
|
-
}
|
|
1207
|
-
const root = startObservation(
|
|
1277
|
+
if (args.trace?.version !== void 0) rootAttributes.version = args.trace.version;
|
|
1278
|
+
const root = resource.observations.agent(
|
|
1208
1279
|
args.agentName ?? "agent.run",
|
|
1209
1280
|
rootAttributes,
|
|
1210
|
-
traceId === void 0 ?
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
spanId: "0000000000000001",
|
|
1215
|
-
traceFlags: 1
|
|
1216
|
-
}
|
|
1281
|
+
traceId === void 0 ? void 0 : {
|
|
1282
|
+
traceId,
|
|
1283
|
+
spanId: "0000000000000001",
|
|
1284
|
+
traceFlags: TraceFlags.SAMPLED
|
|
1217
1285
|
}
|
|
1218
1286
|
);
|
|
1219
1287
|
applyTraceAttributes(root, args, capturedTraceMetadata);
|
|
1220
|
-
|
|
1221
|
-
const runObserver = new LangfuseRunObserver(
|
|
1288
|
+
return new LangfuseRunObserver(
|
|
1222
1289
|
root,
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
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
|
-
}
|
|
1290
|
+
resource.observations,
|
|
1291
|
+
{ traceId: root.traceId, observationId: root.id },
|
|
1292
|
+
resolvePromptRef(args),
|
|
1293
|
+
capture
|
|
1235
1294
|
);
|
|
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
1295
|
}
|
|
1247
1296
|
async flush() {
|
|
1248
|
-
|
|
1249
|
-
await this.
|
|
1297
|
+
this.assertOpen();
|
|
1298
|
+
const resource = this.resource ?? (this.initialization === void 0 ? void 0 : await this.initialization);
|
|
1299
|
+
if (resource === void 0) return;
|
|
1300
|
+
await resource.queue?.flush();
|
|
1301
|
+
await resource.processor.forceFlush();
|
|
1250
1302
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1303
|
+
close() {
|
|
1304
|
+
this.closePromise ??= this.closeResources();
|
|
1305
|
+
return this.closePromise;
|
|
1254
1306
|
}
|
|
1255
|
-
async
|
|
1256
|
-
await this.
|
|
1307
|
+
async [Symbol.asyncDispose]() {
|
|
1308
|
+
await this.close();
|
|
1257
1309
|
}
|
|
1258
1310
|
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);
|
|
1311
|
+
return this.resource?.queue?.depth() ?? 0;
|
|
1267
1312
|
}
|
|
1268
1313
|
async score(args) {
|
|
1269
1314
|
if (args.traceId === void 0 || args.traceId.length === 0) {
|
|
@@ -1273,12 +1318,100 @@ var LangfuseAgentObserver = class {
|
|
|
1273
1318
|
throw new Error("Langfuse score requires publicKey and secretKey");
|
|
1274
1319
|
}
|
|
1275
1320
|
assertScoreValue(args.value, args.dataType);
|
|
1276
|
-
|
|
1277
|
-
|
|
1321
|
+
const resource = await this.resources();
|
|
1322
|
+
if (resource.queue !== null) {
|
|
1323
|
+
resource.queue.enqueue(args);
|
|
1278
1324
|
return;
|
|
1279
1325
|
}
|
|
1280
1326
|
await this.sendScore(args);
|
|
1281
1327
|
}
|
|
1328
|
+
resources() {
|
|
1329
|
+
this.assertOpen();
|
|
1330
|
+
if (this.resource !== void 0) return Promise.resolve(this.resource);
|
|
1331
|
+
this.initialization ??= this.createResources().then((resource) => {
|
|
1332
|
+
this.resource = resource;
|
|
1333
|
+
return resource;
|
|
1334
|
+
}).catch((error) => {
|
|
1335
|
+
this.initialization = void 0;
|
|
1336
|
+
throw error;
|
|
1337
|
+
});
|
|
1338
|
+
return this.initialization;
|
|
1339
|
+
}
|
|
1340
|
+
async createResources() {
|
|
1341
|
+
const resolvedConfig = this[langfuseResolvedConfigSymbol];
|
|
1342
|
+
const processorOptions = {
|
|
1343
|
+
baseUrl: this.baseUrl
|
|
1344
|
+
};
|
|
1345
|
+
if (this.publicKey !== void 0) processorOptions.publicKey = this.publicKey;
|
|
1346
|
+
if (this.secretKey !== void 0) processorOptions.secretKey = this.secretKey;
|
|
1347
|
+
if (resolvedConfig.environment !== void 0) {
|
|
1348
|
+
processorOptions.environment = resolvedConfig.environment;
|
|
1349
|
+
}
|
|
1350
|
+
if (resolvedConfig.release !== void 0) processorOptions.release = resolvedConfig.release;
|
|
1351
|
+
const processor = new LangfuseSpanProcessor(processorOptions);
|
|
1352
|
+
const providerOptions = {
|
|
1353
|
+
spanProcessors: [processor]
|
|
1354
|
+
};
|
|
1355
|
+
if (this.serviceName !== void 0) {
|
|
1356
|
+
providerOptions.resource = resourceFromAttributes({
|
|
1357
|
+
[SEMRESATTRS_SERVICE_NAME]: this.serviceName
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
let provider;
|
|
1361
|
+
try {
|
|
1362
|
+
provider = new NodeTracerProvider(providerOptions);
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
await processor.shutdown().catch(() => void 0);
|
|
1365
|
+
throw error;
|
|
1366
|
+
}
|
|
1367
|
+
try {
|
|
1368
|
+
const batchSize = this.options.scores?.batchSize ?? 0;
|
|
1369
|
+
const queue = batchSize > 0 && this.publicKey !== void 0 && this.secretKey !== void 0 ? new ScoreQueue({
|
|
1370
|
+
baseUrl: this.baseUrl,
|
|
1371
|
+
publicKey: this.publicKey,
|
|
1372
|
+
secretKey: this.secretKey,
|
|
1373
|
+
timeoutMs: this.timeoutMs,
|
|
1374
|
+
batchSize,
|
|
1375
|
+
flushIntervalMs: this.options.scores?.flushIntervalMs ?? 250,
|
|
1376
|
+
maxAttempts: scoreMaxAttempts(this.options.scores?.retries)
|
|
1377
|
+
}) : null;
|
|
1378
|
+
return {
|
|
1379
|
+
processor,
|
|
1380
|
+
provider,
|
|
1381
|
+
observations: new LangfuseObservationFactory(
|
|
1382
|
+
provider.getTracer("@anvia/langfuse", "1.0.0")
|
|
1383
|
+
),
|
|
1384
|
+
queue
|
|
1385
|
+
};
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
await provider.shutdown().catch(() => void 0);
|
|
1388
|
+
throw error;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
async closeResources() {
|
|
1392
|
+
this.closed = true;
|
|
1393
|
+
const pending = this.resource === void 0 ? this.initialization : Promise.resolve(this.resource);
|
|
1394
|
+
if (pending === void 0) return;
|
|
1395
|
+
let resource;
|
|
1396
|
+
try {
|
|
1397
|
+
resource = await pending;
|
|
1398
|
+
} catch {
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
const settled = await Promise.allSettled([
|
|
1402
|
+
resource.queue?.shutdown() ?? Promise.resolve(),
|
|
1403
|
+
resource.provider.shutdown()
|
|
1404
|
+
]);
|
|
1405
|
+
const failures = settled.flatMap(
|
|
1406
|
+
(result) => result.status === "rejected" ? [result.reason] : []
|
|
1407
|
+
);
|
|
1408
|
+
if (failures.length > 0) {
|
|
1409
|
+
throw new AggregateError(failures, "Failed to close LangfuseClient.");
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
assertOpen() {
|
|
1413
|
+
if (this.closed) throw new Error("LangfuseClient is closed.");
|
|
1414
|
+
}
|
|
1282
1415
|
async sendScore(args) {
|
|
1283
1416
|
const body = buildScoreBody2(args);
|
|
1284
1417
|
const response = await fetch(`${this.baseUrl}/api/public/scores`, {
|
|
@@ -1297,6 +1430,37 @@ var LangfuseAgentObserver = class {
|
|
|
1297
1430
|
}
|
|
1298
1431
|
}
|
|
1299
1432
|
};
|
|
1433
|
+
var LangfuseAgentObserver = class {
|
|
1434
|
+
constructor(client, capture) {
|
|
1435
|
+
this.client = client;
|
|
1436
|
+
this.capture = capture;
|
|
1437
|
+
}
|
|
1438
|
+
client;
|
|
1439
|
+
capture;
|
|
1440
|
+
startRun(args) {
|
|
1441
|
+
return this.client.startObservedRun(args, this.capture);
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
function resolveLangfuseCapture(options) {
|
|
1445
|
+
return {
|
|
1446
|
+
redactor: options.redactInputs !== void 0 || options.redactOutputs !== void 0 ? createPiiRedactor(options.redaction) : void 0,
|
|
1447
|
+
redactInputs: options.redactInputs,
|
|
1448
|
+
redactOutputs: options.redactOutputs,
|
|
1449
|
+
captureMode: options.captureMode ?? "safe",
|
|
1450
|
+
captureMaxBytes: validateCaptureMaxBytes(options.captureMaxBytes)
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
function captureInput(value, capture) {
|
|
1454
|
+
const redacted = capture.redactor === void 0 || capture.redactInputs === void 0 ? value : applyRedaction(capture.redactor, value, capture.redactInputs);
|
|
1455
|
+
return sanitizeTraceValue(redacted, capture.captureMaxBytes);
|
|
1456
|
+
}
|
|
1457
|
+
function scoreMaxAttempts(retries) {
|
|
1458
|
+
if (retries === void 0) return 3;
|
|
1459
|
+
if (!Number.isSafeInteger(retries.maxAttempts) || retries.maxAttempts < 1) {
|
|
1460
|
+
throw new TypeError("Langfuse score retries.maxAttempts must be a positive integer.");
|
|
1461
|
+
}
|
|
1462
|
+
return retries.maxAttempts;
|
|
1463
|
+
}
|
|
1300
1464
|
function assertScoreValue(value, dataType) {
|
|
1301
1465
|
if (dataType === "NUMERIC") {
|
|
1302
1466
|
if (typeof value !== "number") {
|
|
@@ -1436,20 +1600,15 @@ function asMetadata(value) {
|
|
|
1436
1600
|
return isRecord(value) ? value : { value };
|
|
1437
1601
|
}
|
|
1438
1602
|
function eventStartTime(value) {
|
|
1439
|
-
if (value
|
|
1440
|
-
return Number.isNaN(value.getTime()) ? void 0 : value;
|
|
1441
|
-
}
|
|
1442
|
-
if (typeof value !== "string") {
|
|
1443
|
-
return void 0;
|
|
1444
|
-
}
|
|
1603
|
+
if (value === void 0) return void 0;
|
|
1445
1604
|
const parsed = new Date(value);
|
|
1446
1605
|
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
1447
1606
|
}
|
|
1448
1607
|
var LangfuseRunObserver = class {
|
|
1449
|
-
constructor(root,
|
|
1608
|
+
constructor(root, observations, trace2, promptRef, redaction) {
|
|
1450
1609
|
this.root = root;
|
|
1451
|
-
this.
|
|
1452
|
-
this.
|
|
1610
|
+
this.observations = observations;
|
|
1611
|
+
this.trace = trace2;
|
|
1453
1612
|
this.promptRef = promptRef;
|
|
1454
1613
|
this.redactor = redaction.redactor;
|
|
1455
1614
|
this.redactInputs = redaction.redactInputs;
|
|
@@ -1458,13 +1617,9 @@ var LangfuseRunObserver = class {
|
|
|
1458
1617
|
this.captureMaxBytes = redaction.captureMaxBytes;
|
|
1459
1618
|
}
|
|
1460
1619
|
root;
|
|
1620
|
+
observations;
|
|
1461
1621
|
trace;
|
|
1462
1622
|
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
1623
|
promptRef;
|
|
1469
1624
|
redactor;
|
|
1470
1625
|
redactInputs;
|
|
@@ -1491,7 +1646,7 @@ var LangfuseRunObserver = class {
|
|
|
1491
1646
|
safeInput.tools = args.request.tools;
|
|
1492
1647
|
safeInput.providerTools = args.request.providerTools;
|
|
1493
1648
|
safeInput.outputSchema = args.request.outputSchema;
|
|
1494
|
-
safeInput.
|
|
1649
|
+
safeInput.providerOptions = args.request.providerOptions;
|
|
1495
1650
|
}
|
|
1496
1651
|
const metadata = {
|
|
1497
1652
|
turn: args.turn,
|
|
@@ -1499,7 +1654,7 @@ var LangfuseRunObserver = class {
|
|
|
1499
1654
|
toolNames: args.request.tools.map((tool) => tool.name),
|
|
1500
1655
|
providerToolNames: args.request.providerTools?.map((tool) => tool.name) ?? [],
|
|
1501
1656
|
hasOutputSchema: args.request.outputSchema !== void 0,
|
|
1502
|
-
|
|
1657
|
+
providerOptionKeys: isRecord(args.request.providerOptions) ? Object.keys(args.request.providerOptions) : []
|
|
1503
1658
|
};
|
|
1504
1659
|
if (this.captureMode === "full" && args.providerRequest !== void 0) {
|
|
1505
1660
|
metadata.providerRequest = args.providerRequest;
|
|
@@ -1507,7 +1662,7 @@ var LangfuseRunObserver = class {
|
|
|
1507
1662
|
if (args.modelInfo !== void 0) {
|
|
1508
1663
|
const modelInfo = {
|
|
1509
1664
|
provider: args.modelInfo.provider,
|
|
1510
|
-
|
|
1665
|
+
modelId: args.modelInfo.modelId
|
|
1511
1666
|
};
|
|
1512
1667
|
if (args.modelInfo.capabilities !== void 0) {
|
|
1513
1668
|
modelInfo.capabilities = args.modelInfo.capabilities;
|
|
@@ -1517,7 +1672,7 @@ var LangfuseRunObserver = class {
|
|
|
1517
1672
|
Object.assign(metadata, promptMetadata(this.promptRef));
|
|
1518
1673
|
const generationAttributes = {
|
|
1519
1674
|
input: this.redactInputValue(safeInput),
|
|
1520
|
-
model: args.
|
|
1675
|
+
model: args.modelInfo?.modelId ?? "unknown",
|
|
1521
1676
|
modelParameters: modelParameters(args.request),
|
|
1522
1677
|
metadata: asMetadata(this.redactInputValue(metadata))
|
|
1523
1678
|
};
|
|
@@ -1528,9 +1683,11 @@ var LangfuseRunObserver = class {
|
|
|
1528
1683
|
isFallback: false
|
|
1529
1684
|
};
|
|
1530
1685
|
}
|
|
1531
|
-
const generation =
|
|
1532
|
-
|
|
1533
|
-
|
|
1686
|
+
const generation = this.observations.generation(
|
|
1687
|
+
`model.turn.${args.turn}`,
|
|
1688
|
+
generationAttributes,
|
|
1689
|
+
turn
|
|
1690
|
+
);
|
|
1534
1691
|
return new LangfuseGenerationObserver(generation, this, /* @__PURE__ */ new Date());
|
|
1535
1692
|
}
|
|
1536
1693
|
startTool(args) {
|
|
@@ -1544,7 +1701,7 @@ var LangfuseRunObserver = class {
|
|
|
1544
1701
|
if (args.toolDefinition !== void 0) metadata.toolDefinition = args.toolDefinition;
|
|
1545
1702
|
if (args.toolMetadata !== void 0) metadata.toolMetadata = args.toolMetadata;
|
|
1546
1703
|
}
|
|
1547
|
-
const tool =
|
|
1704
|
+
const tool = this.observations.tool(
|
|
1548
1705
|
`tool.${args.toolName}`,
|
|
1549
1706
|
{
|
|
1550
1707
|
input: this.redactInputValue({
|
|
@@ -1553,18 +1710,22 @@ var LangfuseRunObserver = class {
|
|
|
1553
1710
|
}),
|
|
1554
1711
|
metadata: asMetadata(this.redactInputValue(metadata))
|
|
1555
1712
|
},
|
|
1556
|
-
|
|
1713
|
+
turn
|
|
1557
1714
|
);
|
|
1558
|
-
return new LangfuseToolObserver(tool, this);
|
|
1715
|
+
return new LangfuseToolObserver(tool, this, this.observations);
|
|
1559
1716
|
}
|
|
1560
1717
|
end(args) {
|
|
1561
1718
|
this.closeAllTurns();
|
|
1562
|
-
const
|
|
1719
|
+
const observedOutput = args.status === "completed" ? { status: args.status, output: args.output, text: args.text } : args.status === "blocked" ? { status: args.status, stage: args.stage, text: args.text } : { status: args.status, interaction: args.interaction, text: args.text };
|
|
1720
|
+
const redactedOutput = this.redactOutputValue(observedOutput);
|
|
1563
1721
|
const metadata = {
|
|
1722
|
+
runId: args.runId,
|
|
1723
|
+
status: args.status,
|
|
1564
1724
|
usage: args.usage,
|
|
1565
1725
|
messageCount: args.messages.length,
|
|
1566
1726
|
sources: this.redactOutputValue(args.sources),
|
|
1567
|
-
providerToolCalls: this.redactOutputValue(args.providerToolCalls)
|
|
1727
|
+
providerToolCalls: this.redactOutputValue(args.providerToolCalls),
|
|
1728
|
+
resumedFrom: this.redactOutputValue(args.resumedFrom)
|
|
1568
1729
|
};
|
|
1569
1730
|
if (this.captureMode === "full") {
|
|
1570
1731
|
metadata.messages = this.redactTranscript(args.messages);
|
|
@@ -1573,7 +1734,6 @@ var LangfuseRunObserver = class {
|
|
|
1573
1734
|
output: redactedOutput,
|
|
1574
1735
|
metadata
|
|
1575
1736
|
}).end();
|
|
1576
|
-
this.clearCurrentHandle?.();
|
|
1577
1737
|
}
|
|
1578
1738
|
error(args) {
|
|
1579
1739
|
this.closeAllTurns();
|
|
@@ -1593,7 +1753,6 @@ var LangfuseRunObserver = class {
|
|
|
1593
1753
|
},
|
|
1594
1754
|
metadata
|
|
1595
1755
|
}).end();
|
|
1596
|
-
this.clearCurrentHandle?.();
|
|
1597
1756
|
}
|
|
1598
1757
|
event(args) {
|
|
1599
1758
|
const metadata = asMetadata(this.redactOutputValue(args.attributes ?? {}));
|
|
@@ -1602,43 +1761,19 @@ var LangfuseRunObserver = class {
|
|
|
1602
1761
|
attributes.level = args.level;
|
|
1603
1762
|
}
|
|
1604
1763
|
const startTime = eventStartTime(args.timestamp);
|
|
1605
|
-
this.
|
|
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
|
-
};
|
|
1764
|
+
this.observations.event(args.name, attributes, this.root, startTime);
|
|
1630
1765
|
}
|
|
1631
1766
|
turnSpan(turn) {
|
|
1632
1767
|
const existing = this.turnSpans.get(turn);
|
|
1633
1768
|
if (existing !== void 0) {
|
|
1634
1769
|
return existing;
|
|
1635
1770
|
}
|
|
1636
|
-
const span = this.
|
|
1771
|
+
const span = this.observations.span(
|
|
1637
1772
|
`turn.${turn}`,
|
|
1638
1773
|
{
|
|
1639
1774
|
metadata: { turn }
|
|
1640
1775
|
},
|
|
1641
|
-
|
|
1776
|
+
this.root
|
|
1642
1777
|
);
|
|
1643
1778
|
this.turnSpans.set(turn, span);
|
|
1644
1779
|
return span;
|
|
@@ -1719,12 +1854,14 @@ function textFromObservedAssistantContent(content) {
|
|
|
1719
1854
|
return content.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n");
|
|
1720
1855
|
}
|
|
1721
1856
|
var LangfuseToolObserver = class {
|
|
1722
|
-
constructor(tool, run) {
|
|
1857
|
+
constructor(tool, run, observations) {
|
|
1723
1858
|
this.tool = tool;
|
|
1724
1859
|
this.run = run;
|
|
1860
|
+
this.observations = observations;
|
|
1725
1861
|
}
|
|
1726
1862
|
tool;
|
|
1727
1863
|
run;
|
|
1864
|
+
observations;
|
|
1728
1865
|
childAgents = /* @__PURE__ */ new Map();
|
|
1729
1866
|
childGenerations = /* @__PURE__ */ new Map();
|
|
1730
1867
|
childTools = [];
|
|
@@ -1741,7 +1878,7 @@ var LangfuseToolObserver = class {
|
|
|
1741
1878
|
if (child.type === "turn_start") {
|
|
1742
1879
|
const promptMessage = isRecord(child.prompt) ? child.prompt : void 0;
|
|
1743
1880
|
const historyMessages = Array.isArray(child.history) ? child.history.filter(isRecord) : [];
|
|
1744
|
-
|
|
1881
|
+
this.observations.event(
|
|
1745
1882
|
`${agentLabel(agentId, agentName)}.turn.${childTurn}.start`,
|
|
1746
1883
|
{
|
|
1747
1884
|
input: this.run.redactInputValue({
|
|
@@ -1752,7 +1889,7 @@ var LangfuseToolObserver = class {
|
|
|
1752
1889
|
this.run.redactInputValue(childMetadata(args, agentId, agentName, childTurn))
|
|
1753
1890
|
)
|
|
1754
1891
|
},
|
|
1755
|
-
|
|
1892
|
+
agent
|
|
1756
1893
|
);
|
|
1757
1894
|
return;
|
|
1758
1895
|
}
|
|
@@ -1769,15 +1906,15 @@ var LangfuseToolObserver = class {
|
|
|
1769
1906
|
input.tools = request.tools;
|
|
1770
1907
|
input.providerTools = request.providerTools;
|
|
1771
1908
|
input.outputSchema = request.outputSchema;
|
|
1772
|
-
input.
|
|
1909
|
+
input.providerOptions = request.providerOptions;
|
|
1773
1910
|
}
|
|
1774
1911
|
const toolNames = Array.isArray(request.tools) ? request.tools.filter(isRecord).map((tool) => tool.name).filter((name) => typeof name === "string") : [];
|
|
1775
1912
|
const providerToolNames = Array.isArray(request.providerTools) ? request.providerTools.filter(isRecord).map((tool) => tool.name).filter((name) => typeof name === "string") : [];
|
|
1776
|
-
const generation =
|
|
1913
|
+
const generation = this.observations.generation(
|
|
1777
1914
|
`${agentLabel(agentId, agentName)}.model.turn.${childTurn}`,
|
|
1778
1915
|
{
|
|
1779
1916
|
input: this.run.redactInputValue(input),
|
|
1780
|
-
model: typeof
|
|
1917
|
+
model: typeof modelInfo?.modelId === "string" ? modelInfo.modelId : "unknown",
|
|
1781
1918
|
modelParameters: modelParameters(request),
|
|
1782
1919
|
metadata: asMetadata(
|
|
1783
1920
|
this.run.redactInputValue({
|
|
@@ -1790,7 +1927,7 @@ var LangfuseToolObserver = class {
|
|
|
1790
1927
|
})
|
|
1791
1928
|
)
|
|
1792
1929
|
},
|
|
1793
|
-
|
|
1930
|
+
agent
|
|
1794
1931
|
);
|
|
1795
1932
|
this.childGenerations.set(generationKey(agentId, childTurn), {
|
|
1796
1933
|
generation,
|
|
@@ -1830,7 +1967,7 @@ var LangfuseToolObserver = class {
|
|
|
1830
1967
|
if (child.type === "source" || child.type === "provider_tool_call") {
|
|
1831
1968
|
const childGeneration = this.childGenerations.get(generationKey(agentId, childTurn));
|
|
1832
1969
|
const parent = childGeneration?.generation ?? agent;
|
|
1833
|
-
|
|
1970
|
+
this.observations.event(
|
|
1834
1971
|
`${agentLabel(agentId, agentName)}.${child.type}`,
|
|
1835
1972
|
{
|
|
1836
1973
|
output: this.run.redactOutputValue(
|
|
@@ -1840,12 +1977,12 @@ var LangfuseToolObserver = class {
|
|
|
1840
1977
|
this.run.redactOutputValue(childMetadata(args, agentId, agentName, childTurn))
|
|
1841
1978
|
)
|
|
1842
1979
|
},
|
|
1843
|
-
|
|
1980
|
+
parent
|
|
1844
1981
|
);
|
|
1845
1982
|
return;
|
|
1846
1983
|
}
|
|
1847
1984
|
if (child.type === "guardrail_decision") {
|
|
1848
|
-
|
|
1985
|
+
this.observations.guardrail(
|
|
1849
1986
|
`${agentLabel(agentId, agentName)}.guardrail`,
|
|
1850
1987
|
{
|
|
1851
1988
|
output: this.run.redactOutputValue(child.decision),
|
|
@@ -1853,7 +1990,7 @@ var LangfuseToolObserver = class {
|
|
|
1853
1990
|
this.run.redactOutputValue(childMetadata(args, agentId, agentName, childTurn))
|
|
1854
1991
|
)
|
|
1855
1992
|
},
|
|
1856
|
-
|
|
1993
|
+
agent
|
|
1857
1994
|
).end();
|
|
1858
1995
|
return;
|
|
1859
1996
|
}
|
|
@@ -1863,14 +2000,13 @@ var LangfuseToolObserver = class {
|
|
|
1863
2000
|
output: this.run.redactOutputValue({ toolCall: child.toolCall })
|
|
1864
2001
|
});
|
|
1865
2002
|
const toolCall = child.toolCall;
|
|
1866
|
-
const
|
|
1867
|
-
const
|
|
1868
|
-
const
|
|
1869
|
-
const childTool = agent.startObservation(
|
|
2003
|
+
const toolName = typeof toolCall.toolName === "string" ? toolCall.toolName : "tool";
|
|
2004
|
+
const toolCallId = typeof toolCall.toolCallId === "string" ? toolCall.toolCallId : typeof toolCall.callId === "string" ? toolCall.callId : void 0;
|
|
2005
|
+
const childTool = this.observations.tool(
|
|
1870
2006
|
`${agentLabel(agentId, agentName)}.${toolName}`,
|
|
1871
2007
|
{
|
|
1872
2008
|
input: this.run.redactInputValue({
|
|
1873
|
-
args:
|
|
2009
|
+
args: toolCall.input,
|
|
1874
2010
|
toolCall
|
|
1875
2011
|
}),
|
|
1876
2012
|
metadata: asMetadata(
|
|
@@ -1881,7 +2017,7 @@ var LangfuseToolObserver = class {
|
|
|
1881
2017
|
})
|
|
1882
2018
|
)
|
|
1883
2019
|
},
|
|
1884
|
-
|
|
2020
|
+
agent
|
|
1885
2021
|
);
|
|
1886
2022
|
const childToolRecord = {
|
|
1887
2023
|
agentId,
|
|
@@ -1917,13 +2053,18 @@ var LangfuseToolObserver = class {
|
|
|
1917
2053
|
return;
|
|
1918
2054
|
}
|
|
1919
2055
|
if (child.type === "final") {
|
|
2056
|
+
const result = isRecord(child.result) ? child.result : {};
|
|
1920
2057
|
const update = {
|
|
1921
|
-
output: this.run.redactOutputValue(
|
|
2058
|
+
output: this.run.redactOutputValue({
|
|
2059
|
+
status: result.status,
|
|
2060
|
+
output: result.output,
|
|
2061
|
+
text: result.text
|
|
2062
|
+
})
|
|
1922
2063
|
};
|
|
1923
2064
|
const metadata = {};
|
|
1924
|
-
if (isRecord(
|
|
1925
|
-
if (this.run.isFullCapture() && Array.isArray(
|
|
1926
|
-
metadata.messages = this.run.redactTranscript(
|
|
2065
|
+
if (isRecord(result.usage)) metadata.usage = result.usage;
|
|
2066
|
+
if (this.run.isFullCapture() && Array.isArray(result.messages)) {
|
|
2067
|
+
metadata.messages = this.run.redactTranscript(result.messages);
|
|
1927
2068
|
}
|
|
1928
2069
|
if (Object.keys(metadata).length > 0) update.metadata = metadata;
|
|
1929
2070
|
agent.update(update).end();
|
|
@@ -1970,6 +2111,23 @@ var LangfuseToolObserver = class {
|
|
|
1970
2111
|
}
|
|
1971
2112
|
this.tool.update(attributes).end();
|
|
1972
2113
|
}
|
|
2114
|
+
suspend(args) {
|
|
2115
|
+
this.endOpenChildren();
|
|
2116
|
+
this.tool.update({
|
|
2117
|
+
output: this.run.redactOutputValue({
|
|
2118
|
+
status: "suspended",
|
|
2119
|
+
interaction: args.interaction
|
|
2120
|
+
}),
|
|
2121
|
+
metadata: {
|
|
2122
|
+
turn: args.turn,
|
|
2123
|
+
internalCallId: args.internalCallId,
|
|
2124
|
+
interactionId: args.interaction.id,
|
|
2125
|
+
interactionType: args.interaction.type
|
|
2126
|
+
},
|
|
2127
|
+
level: "WARNING",
|
|
2128
|
+
statusMessage: "Tool call suspended for human interaction"
|
|
2129
|
+
}).end();
|
|
2130
|
+
}
|
|
1973
2131
|
error(args) {
|
|
1974
2132
|
this.endOpenChildren();
|
|
1975
2133
|
const redactedError = this.run.redactOutputValue(errorMessage(args.error));
|
|
@@ -1989,14 +2147,14 @@ var LangfuseToolObserver = class {
|
|
|
1989
2147
|
if (existing !== void 0) {
|
|
1990
2148
|
return existing;
|
|
1991
2149
|
}
|
|
1992
|
-
const agent = this.
|
|
2150
|
+
const agent = this.observations.agent(
|
|
1993
2151
|
`${agentLabel(agentId, agentName)}.run`,
|
|
1994
2152
|
{
|
|
1995
2153
|
metadata: asMetadata(
|
|
1996
2154
|
this.run.redactInputValue(childMetadata(args, agentId, agentName, args.turn))
|
|
1997
2155
|
)
|
|
1998
2156
|
},
|
|
1999
|
-
|
|
2157
|
+
this.tool
|
|
2000
2158
|
);
|
|
2001
2159
|
this.childAgents.set(agentId, agent);
|
|
2002
2160
|
return agent;
|
|
@@ -2032,12 +2190,8 @@ var LangfuseToolObserver = class {
|
|
|
2032
2190
|
};
|
|
2033
2191
|
export {
|
|
2034
2192
|
DEFAULT_PATTERNS,
|
|
2193
|
+
LangfuseClient,
|
|
2035
2194
|
LangfuseScoreError,
|
|
2036
|
-
|
|
2037
|
-
createLangfuseEvalReporter,
|
|
2038
|
-
createLangfusePromptClient,
|
|
2039
|
-
createPiiRedactor,
|
|
2040
|
-
langfuse,
|
|
2041
|
-
runEvalAsExperiment
|
|
2195
|
+
createPiiRedactor
|
|
2042
2196
|
};
|
|
2043
2197
|
//# sourceMappingURL=index.js.map
|