@anvia/langfuse 0.6.1 → 1.0.0-rc.10
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 +120 -448
- package/dist/index.d.ts +83 -46
- package/dist/index.js +786 -632
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,120 +1,155 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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;
|
|
5
19
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return result;
|
|
9
|
-
}
|
|
10
|
-
function modelInputMessages(messages) {
|
|
11
|
-
return messages.map(modelInputMessage);
|
|
12
|
-
}
|
|
13
|
-
function modelParameters(request) {
|
|
14
|
-
const params = {};
|
|
15
|
-
if (request.temperature !== void 0) params.temperature = request.temperature;
|
|
16
|
-
if (request.maxTokens !== void 0) params.maxTokens = request.maxTokens;
|
|
17
|
-
if (request.toolChoice !== void 0) {
|
|
18
|
-
params.toolChoice = typeof request.toolChoice === "string" ? request.toolChoice : request.toolChoice.name;
|
|
20
|
+
function redactObject(input) {
|
|
21
|
+
return redactValue(input, 0, redactString);
|
|
19
22
|
}
|
|
20
|
-
|
|
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 };
|
|
21
30
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
25
43
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
44
|
+
];
|
|
45
|
+
function applyPattern(input, name, regex, replacement) {
|
|
46
|
+
if (name === "creditCard") {
|
|
47
|
+
return redactCreditCards(input, replacement);
|
|
48
|
+
}
|
|
49
|
+
return input.replace(regex, replacement);
|
|
31
50
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
}
|
|
41
63
|
}
|
|
64
|
+
out += ch;
|
|
65
|
+
i += 1;
|
|
42
66
|
}
|
|
43
|
-
return
|
|
44
|
-
input: numberValue(usage.inputTokens) ?? 0,
|
|
45
|
-
output: numberValue(usage.outputTokens) ?? 0,
|
|
46
|
-
total: numberValue(usage.totalTokens) ?? (numberValue(usage.inputTokens) ?? 0) + (numberValue(usage.outputTokens) ?? 0)
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function childMetadata(args, agentId, agentName, childTurn) {
|
|
50
|
-
return {
|
|
51
|
-
source: "agent_tool_event",
|
|
52
|
-
childAgentId: agentId,
|
|
53
|
-
childAgentName: agentName,
|
|
54
|
-
childTurn,
|
|
55
|
-
parentToolName: args.toolName,
|
|
56
|
-
parentInternalCallId: args.internalCallId,
|
|
57
|
-
parentToolCallId: args.toolCallId
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function generationKey(agentId, turn) {
|
|
61
|
-
return `${agentId}:${turn}`;
|
|
67
|
+
return out;
|
|
62
68
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
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 };
|
|
65
84
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
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");
|
|
68
95
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
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;
|
|
71
111
|
}
|
|
72
|
-
function
|
|
73
|
-
return
|
|
112
|
+
function cloneRegex(source, flags) {
|
|
113
|
+
return new RegExp(source.source, flags + source.flags.replace(/g/g, ""));
|
|
74
114
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
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;
|
|
77
138
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
function resolveLangfuseConfig(options = {}, fallback) {
|
|
139
|
+
function redactMessage(message, redactStringFn) {
|
|
140
|
+
if (message.role === "system") {
|
|
141
|
+
return { ...message, content: redactStringFn(message.content) };
|
|
142
|
+
}
|
|
84
143
|
return {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
fallback?.publicKey,
|
|
88
|
-
process.env.LANGFUSE_PUBLIC_KEY
|
|
89
|
-
),
|
|
90
|
-
secretKey: resolveStringOption(
|
|
91
|
-
options.secretKey,
|
|
92
|
-
fallback?.secretKey,
|
|
93
|
-
process.env.LANGFUSE_SECRET_KEY
|
|
94
|
-
),
|
|
95
|
-
baseUrl: resolveStringOption(options.baseUrl, fallback?.baseUrl, process.env.LANGFUSE_BASE_URL) ?? "https://cloud.langfuse.com",
|
|
96
|
-
environment: resolveStringOption(
|
|
97
|
-
options.environment,
|
|
98
|
-
fallback?.environment,
|
|
99
|
-
process.env.LANGFUSE_TRACING_ENVIRONMENT
|
|
100
|
-
),
|
|
101
|
-
release: resolveStringOption(options.release, fallback?.release, process.env.LANGFUSE_RELEASE),
|
|
102
|
-
serviceName: resolveStringOption(
|
|
103
|
-
options.serviceName,
|
|
104
|
-
fallback?.serviceName,
|
|
105
|
-
process.env.LANGFUSE_SERVICE_NAME
|
|
106
|
-
),
|
|
107
|
-
timeoutMs: options.timeoutMs ?? fallback?.timeoutMs ?? 3e4
|
|
144
|
+
...message,
|
|
145
|
+
content: redactMessageContent(message.content, redactStringFn)
|
|
108
146
|
};
|
|
109
147
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
return void 0;
|
|
113
|
-
}
|
|
114
|
-
return value[langfuseResolvedConfigSymbol];
|
|
148
|
+
function redactMessageContent(value, redactStringFn) {
|
|
149
|
+
return redactValue(value, 0, redactStringFn);
|
|
115
150
|
}
|
|
116
|
-
function
|
|
117
|
-
return
|
|
151
|
+
function isBase64DataUrl(value) {
|
|
152
|
+
return /^data:[^;,]+;base64,/i.test(value);
|
|
118
153
|
}
|
|
119
154
|
|
|
120
155
|
// src/scoring.ts
|
|
@@ -157,7 +192,7 @@ var ScoreQueue = class {
|
|
|
157
192
|
timeoutMs;
|
|
158
193
|
batchSize;
|
|
159
194
|
flushIntervalMs;
|
|
160
|
-
|
|
195
|
+
maxAttempts;
|
|
161
196
|
fetchImpl;
|
|
162
197
|
sleep;
|
|
163
198
|
setTimer;
|
|
@@ -169,7 +204,7 @@ var ScoreQueue = class {
|
|
|
169
204
|
this.timeoutMs = options.timeoutMs;
|
|
170
205
|
this.batchSize = options.batchSize;
|
|
171
206
|
this.flushIntervalMs = options.flushIntervalMs;
|
|
172
|
-
this.
|
|
207
|
+
this.maxAttempts = options.maxAttempts;
|
|
173
208
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
174
209
|
this.sleep = options.sleep ?? defaultSleep;
|
|
175
210
|
this.setTimer = options.setTimer ?? defaultSetTimer;
|
|
@@ -216,10 +251,7 @@ var ScoreQueue = class {
|
|
|
216
251
|
async shutdown() {
|
|
217
252
|
this.closed = true;
|
|
218
253
|
this.clearScheduledTimer();
|
|
219
|
-
|
|
220
|
-
await this.flush();
|
|
221
|
-
} catch {
|
|
222
|
-
}
|
|
254
|
+
await this.flush();
|
|
223
255
|
}
|
|
224
256
|
scheduleTimer() {
|
|
225
257
|
if (this.timer !== null) {
|
|
@@ -243,7 +275,7 @@ var ScoreQueue = class {
|
|
|
243
275
|
async sendBatch(scores) {
|
|
244
276
|
const body = scores.map((score) => buildScoreBody(score));
|
|
245
277
|
let lastError;
|
|
246
|
-
for (let attempt = 0; attempt < this.
|
|
278
|
+
for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {
|
|
247
279
|
try {
|
|
248
280
|
const response = await this.fetchImpl(`${this.baseUrl}/api/public/scores`, {
|
|
249
281
|
method: "POST",
|
|
@@ -272,12 +304,12 @@ var ScoreQueue = class {
|
|
|
272
304
|
}
|
|
273
305
|
lastError = error;
|
|
274
306
|
}
|
|
275
|
-
if (attempt < this.
|
|
307
|
+
if (attempt < this.maxAttempts - 1) {
|
|
276
308
|
await this.sleep(computeBackoff(attempt));
|
|
277
309
|
}
|
|
278
310
|
}
|
|
279
311
|
throw new RetryableLangfuseScoreError(
|
|
280
|
-
`Langfuse score batch failed after ${this.
|
|
312
|
+
`Langfuse score batch failed after ${this.maxAttempts} attempts`,
|
|
281
313
|
scores,
|
|
282
314
|
lastError
|
|
283
315
|
);
|
|
@@ -312,17 +344,252 @@ function buildScoreBody(score) {
|
|
|
312
344
|
name: score.name,
|
|
313
345
|
value: score.value
|
|
314
346
|
};
|
|
315
|
-
if (score.observationId !== void 0) body.observationId = score.observationId;
|
|
316
|
-
if (score.dataType !== void 0) body.dataType = score.dataType;
|
|
317
|
-
if (score.comment !== void 0) body.comment = score.comment;
|
|
318
|
-
if (score.metadata !== void 0) body.metadata = score.metadata;
|
|
319
|
-
const configId = score.configId ?? score.scoreConfigId;
|
|
320
|
-
if (configId !== void 0) body.configId = configId;
|
|
321
|
-
if (score.environment !== void 0) body.environment = score.environment;
|
|
322
|
-
if (score.timestamp !== void 0) {
|
|
323
|
-
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
|
+
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)
|
|
522
|
+
};
|
|
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;
|
|
324
588
|
}
|
|
325
|
-
return
|
|
589
|
+
return value[langfuseResolvedConfigSymbol];
|
|
590
|
+
}
|
|
591
|
+
function resolveStringOption(option, fallback, envVar) {
|
|
592
|
+
return emptyToUndefined(option) ?? emptyToUndefined(fallback) ?? emptyToUndefined(envVar);
|
|
326
593
|
}
|
|
327
594
|
|
|
328
595
|
// src/dataset-client.ts
|
|
@@ -381,7 +648,8 @@ function createLangfuseDatasetClient(tracing, options = {}) {
|
|
|
381
648
|
if (dataset.metadata !== void 0) result.metadata = dataset.metadata;
|
|
382
649
|
return result;
|
|
383
650
|
},
|
|
384
|
-
async getDataset(
|
|
651
|
+
async getDataset(options2) {
|
|
652
|
+
const { name } = options2;
|
|
385
653
|
const items = [];
|
|
386
654
|
let description;
|
|
387
655
|
let metadata;
|
|
@@ -421,7 +689,8 @@ function createLangfuseDatasetClient(tracing, options = {}) {
|
|
|
421
689
|
if (metadata !== void 0) dataset.metadata = metadata;
|
|
422
690
|
return dataset;
|
|
423
691
|
},
|
|
424
|
-
async upsertItems(
|
|
692
|
+
async upsertItems(options2) {
|
|
693
|
+
const { name, items } = options2;
|
|
425
694
|
const url = `${baseUrl}/api/public/datasets/${encodeURIComponent(name)}/items`;
|
|
426
695
|
await request(url, {
|
|
427
696
|
method: "POST",
|
|
@@ -431,7 +700,7 @@ function createLangfuseDatasetClient(tracing, options = {}) {
|
|
|
431
700
|
async runExperiment(opts) {
|
|
432
701
|
let items = opts.items;
|
|
433
702
|
if (items === void 0) {
|
|
434
|
-
const dataset = await this.getDataset(opts.datasetName);
|
|
703
|
+
const dataset = await this.getDataset({ name: opts.datasetName });
|
|
435
704
|
items = dataset.items;
|
|
436
705
|
}
|
|
437
706
|
if (items === void 0 || items.length === 0) {
|
|
@@ -512,7 +781,8 @@ import {
|
|
|
512
781
|
} from "@anvia/core/evals";
|
|
513
782
|
var DEFAULT_TRUNCATE_BYTES = 2048;
|
|
514
783
|
function createLangfuseEvalReporter(tracing, options = {}) {
|
|
515
|
-
const onMissingTrace = options.onMissingTrace ??
|
|
784
|
+
const onMissingTrace = options.onMissingTrace ?? "ignore";
|
|
785
|
+
const traceObserver = options.traceObserver ?? "langfuse";
|
|
516
786
|
const truncateAt = options.truncateInputAt ?? DEFAULT_TRUNCATE_BYTES;
|
|
517
787
|
const includeMessages = options.includeMessages ?? true;
|
|
518
788
|
const includeContext = options.includeContext ?? false;
|
|
@@ -521,18 +791,20 @@ function createLangfuseEvalReporter(tracing, options = {}) {
|
|
|
521
791
|
if (args.outcome.outcome === "invalid" && options.publishInvalid !== true) {
|
|
522
792
|
return;
|
|
523
793
|
}
|
|
524
|
-
const
|
|
794
|
+
const trace2 = args.trace ?? resolveEvalTraceRef({
|
|
525
795
|
output: args.output,
|
|
526
796
|
input: args.case.input,
|
|
527
797
|
metadata: args.case.metadata
|
|
528
798
|
});
|
|
529
|
-
if (
|
|
799
|
+
if (trace2?.traceId === void 0 || trace2.traceId.length === 0 || trace2.observer !== void 0 && trace2.observer !== traceObserver) {
|
|
530
800
|
if (onMissingTrace === "throw") {
|
|
531
|
-
throw new Error(
|
|
801
|
+
throw new Error(
|
|
802
|
+
`Langfuse eval reporter requires traceId from observer ${JSON.stringify(traceObserver)}`
|
|
803
|
+
);
|
|
532
804
|
}
|
|
533
805
|
if (onMissingTrace === "warn") {
|
|
534
806
|
console.warn(
|
|
535
|
-
"[anvia/langfuse] eval reporter dropped score because no
|
|
807
|
+
"[anvia/langfuse] eval reporter dropped score because no matching trace was found",
|
|
536
808
|
{ caseId: args.case.id, metric: args.metric.name }
|
|
537
809
|
);
|
|
538
810
|
}
|
|
@@ -551,11 +823,11 @@ function createLangfuseEvalReporter(tracing, options = {}) {
|
|
|
551
823
|
});
|
|
552
824
|
const configId = resolveConfigId(args.metric);
|
|
553
825
|
const score = {
|
|
554
|
-
traceId:
|
|
826
|
+
traceId: trace2.traceId,
|
|
555
827
|
name: args.metric.name,
|
|
556
828
|
value: projection.value
|
|
557
829
|
};
|
|
558
|
-
if (
|
|
830
|
+
if (trace2.observationId !== void 0) score.observationId = trace2.observationId;
|
|
559
831
|
if (args.metric.dataType !== void 0) score.dataType = args.metric.dataType;
|
|
560
832
|
if (configId !== void 0) score.configId = configId;
|
|
561
833
|
if (projection.explanation !== void 0) score.comment = projection.explanation;
|
|
@@ -653,18 +925,20 @@ function readMessages(output) {
|
|
|
653
925
|
|
|
654
926
|
// src/experiment-runner.ts
|
|
655
927
|
import { runEvalSuite } from "@anvia/core/evals";
|
|
656
|
-
async function
|
|
928
|
+
async function runLangfuseEvalExperiment(client, options) {
|
|
929
|
+
const evalOptions = options.suite;
|
|
930
|
+
const experimentOptions = options.experiment;
|
|
657
931
|
const clientOptions = {};
|
|
658
932
|
if (experimentOptions.pageSize !== void 0) clientOptions.pageSize = experimentOptions.pageSize;
|
|
659
933
|
if (experimentOptions.timeoutMs !== void 0)
|
|
660
934
|
clientOptions.timeoutMs = experimentOptions.timeoutMs;
|
|
661
|
-
const
|
|
935
|
+
const datasetClient = client.datasetClient(clientOptions);
|
|
662
936
|
const suiteOptions = experimentOptions.publishScores === true ? {
|
|
663
937
|
...evalOptions,
|
|
664
938
|
reporters: [
|
|
665
939
|
...evalOptions.reporters ?? [],
|
|
666
940
|
createLangfuseEvalReporter(
|
|
667
|
-
|
|
941
|
+
client,
|
|
668
942
|
experimentOptions.reporterOptions
|
|
669
943
|
)
|
|
670
944
|
]
|
|
@@ -709,30 +983,30 @@ async function runEvalAsExperiment(evalOptions, experimentOptions) {
|
|
|
709
983
|
};
|
|
710
984
|
}
|
|
711
985
|
const output = result.output ?? void 0;
|
|
712
|
-
const
|
|
713
|
-
return { output, trace };
|
|
986
|
+
const trace2 = readTraceFromOutput(result.output);
|
|
987
|
+
return { output, trace: trace2 };
|
|
714
988
|
}
|
|
715
989
|
};
|
|
716
990
|
if (experimentOptions.description !== void 0) {
|
|
717
991
|
runOptions.description = experimentOptions.description;
|
|
718
992
|
}
|
|
719
993
|
if (experimentOptions.metadata !== void 0) runOptions.metadata = experimentOptions.metadata;
|
|
720
|
-
const datasetRun = await
|
|
994
|
+
const datasetRun = await datasetClient.runExperiment(runOptions);
|
|
721
995
|
return { suite, datasetRun };
|
|
722
996
|
}
|
|
723
997
|
function readTraceFromOutput(output) {
|
|
724
998
|
if (typeof output !== "object" || output === null || !("trace" in output)) {
|
|
725
999
|
return void 0;
|
|
726
1000
|
}
|
|
727
|
-
const
|
|
728
|
-
if (typeof
|
|
1001
|
+
const trace2 = output.trace;
|
|
1002
|
+
if (typeof trace2 !== "object" || trace2 === null) {
|
|
729
1003
|
return void 0;
|
|
730
1004
|
}
|
|
731
|
-
const traceId =
|
|
1005
|
+
const traceId = trace2.traceId;
|
|
732
1006
|
if (typeof traceId !== "string") {
|
|
733
1007
|
return void 0;
|
|
734
1008
|
}
|
|
735
|
-
const observationId =
|
|
1009
|
+
const observationId = trace2.observationId;
|
|
736
1010
|
if (typeof observationId === "string") {
|
|
737
1011
|
return { traceId, observationId };
|
|
738
1012
|
}
|
|
@@ -765,7 +1039,8 @@ function createLangfusePromptClient(tracing, options = {}) {
|
|
|
765
1039
|
}
|
|
766
1040
|
return await response.json();
|
|
767
1041
|
}
|
|
768
|
-
async function getPrompt(
|
|
1042
|
+
async function getPrompt(options2) {
|
|
1043
|
+
const { name, ...opts } = options2;
|
|
769
1044
|
const key = `${name}::${opts.version ?? ""}::${opts.label ?? ""}`;
|
|
770
1045
|
const ttl = opts.cacheTtlMs ?? defaultTtl;
|
|
771
1046
|
if (opts.refresh !== true) {
|
|
@@ -794,352 +1069,113 @@ function createLangfusePromptClient(tracing, options = {}) {
|
|
|
794
1069
|
cache.set(key, { prompt, expiresAt: Date.now() + ttl });
|
|
795
1070
|
return prompt;
|
|
796
1071
|
}
|
|
797
|
-
function getPromptText(
|
|
798
|
-
return getPrompt(
|
|
1072
|
+
function getPromptText(options2) {
|
|
1073
|
+
return getPrompt(options2).then((prompt) => {
|
|
799
1074
|
if (typeof prompt.prompt !== "string") {
|
|
800
|
-
throw new Error(`Prompt ${name} is a chat prompt; expected text`);
|
|
1075
|
+
throw new Error(`Prompt ${options2.name} is a chat prompt; expected text`);
|
|
801
1076
|
}
|
|
802
1077
|
return prompt.prompt;
|
|
803
1078
|
});
|
|
804
1079
|
}
|
|
805
|
-
function getPromptChat(
|
|
806
|
-
return getPrompt(
|
|
1080
|
+
function getPromptChat(options2) {
|
|
1081
|
+
return getPrompt(options2).then((prompt) => {
|
|
807
1082
|
if (typeof prompt.prompt === "string") {
|
|
808
|
-
throw new Error(`Prompt ${name} is a text prompt; expected chat`);
|
|
1083
|
+
throw new Error(`Prompt ${options2.name} is a text prompt; expected chat`);
|
|
809
1084
|
}
|
|
810
1085
|
return prompt.prompt;
|
|
811
1086
|
});
|
|
812
1087
|
}
|
|
813
|
-
function refresh() {
|
|
814
|
-
cache.clear();
|
|
815
|
-
}
|
|
816
|
-
return { getPrompt, getPromptText, getPromptChat, refresh };
|
|
817
|
-
}
|
|
818
|
-
function normalizePrompt(raw, type) {
|
|
819
|
-
if (type === "text") {
|
|
820
|
-
if (typeof raw === "string") return raw;
|
|
821
|
-
throw new Error("Expected text prompt to be a string");
|
|
822
|
-
}
|
|
823
|
-
if (!Array.isArray(raw)) {
|
|
824
|
-
throw new Error("Expected chat prompt to be an array of messages");
|
|
825
|
-
}
|
|
826
|
-
return raw.map((entry) => {
|
|
827
|
-
if (typeof entry !== "object" || entry === null) {
|
|
828
|
-
throw new Error("Expected chat message to be an object");
|
|
829
|
-
}
|
|
830
|
-
const role = entry.role;
|
|
831
|
-
const content = entry.content;
|
|
832
|
-
if (typeof content !== "string") {
|
|
833
|
-
throw new Error("Expected chat message content to be a string");
|
|
834
|
-
}
|
|
835
|
-
if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") {
|
|
836
|
-
throw new Error(`Unexpected chat message role: ${String(role)}`);
|
|
837
|
-
}
|
|
838
|
-
return { role, content };
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
function buildAuthHeader2(publicKey, secretKey) {
|
|
842
|
-
if (publicKey === void 0 || secretKey === void 0) {
|
|
843
|
-
return {};
|
|
844
|
-
}
|
|
845
|
-
const encoded = Buffer.from(`${publicKey}:${secretKey}`).toString("base64");
|
|
846
|
-
return { Authorization: `Basic ${encoded}` };
|
|
847
|
-
}
|
|
848
|
-
async function readErrorBody2(response) {
|
|
849
|
-
try {
|
|
850
|
-
return await response.text();
|
|
851
|
-
} catch {
|
|
852
|
-
return "<unreadable>";
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
// src/redaction.ts
|
|
857
|
-
var DEFAULT_REPLACEMENT = "[REDACTED]";
|
|
858
|
-
var MAX_DEPTH = 16;
|
|
859
|
-
function createPiiRedactor(options = {}) {
|
|
860
|
-
const patterns = options.patterns ?? DEFAULT_PATTERNS;
|
|
861
|
-
const replacement = options.replacement ?? DEFAULT_REPLACEMENT;
|
|
862
|
-
const compiled = patterns.map((p) => ({
|
|
863
|
-
name: p.name,
|
|
864
|
-
regex: cloneRegex(p.regex, "g")
|
|
865
|
-
}));
|
|
866
|
-
const patternNamesList = patterns.map((p) => p.name);
|
|
867
|
-
function redactString(input) {
|
|
868
|
-
if (typeof input !== "string") return input;
|
|
869
|
-
let out = input;
|
|
870
|
-
for (const { name, regex } of compiled) {
|
|
871
|
-
out = applyPattern(out, name, regex, replacement);
|
|
872
|
-
}
|
|
873
|
-
return out;
|
|
874
|
-
}
|
|
875
|
-
function redactObject(input) {
|
|
876
|
-
return redactValue(input, 0, redactString);
|
|
877
|
-
}
|
|
878
|
-
function redactMessages(input) {
|
|
879
|
-
return input.map((message) => redactMessage(message, redactString));
|
|
880
|
-
}
|
|
881
|
-
function patternNames() {
|
|
882
|
-
return patternNamesList;
|
|
883
|
-
}
|
|
884
|
-
return { redactString, redactObject, redactMessages, patternNames };
|
|
885
|
-
}
|
|
886
|
-
var DEFAULT_PATTERNS = [
|
|
887
|
-
{ name: "email", regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g },
|
|
888
|
-
{ name: "creditCard", regex: /\b(?:\d[ -]?){13,19}\b/g },
|
|
889
|
-
{ name: "ipv4", regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g },
|
|
890
|
-
{
|
|
891
|
-
name: "phone",
|
|
892
|
-
regex: /(?<!\d)(?:\+\d{1,3}[\s.-]?)?(?:\(\d{2,4}\)[\s.-]?)?\d{3,4}[\s.-]?\d{3,4}(?:[\s.-]?\d{3,4})?(?!\d)/g
|
|
893
|
-
},
|
|
894
|
-
{ name: "jwt", regex: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g },
|
|
895
|
-
{
|
|
896
|
-
name: "apiKey",
|
|
897
|
-
regex: /\b(?:sk|pk|api|key|token)[-_][A-Za-z0-9]{16,}\b/gi
|
|
898
|
-
}
|
|
899
|
-
];
|
|
900
|
-
function applyPattern(input, name, regex, replacement) {
|
|
901
|
-
if (name === "creditCard") {
|
|
902
|
-
return redactCreditCards(input, replacement);
|
|
903
|
-
}
|
|
904
|
-
return input.replace(regex, replacement);
|
|
905
|
-
}
|
|
906
|
-
function redactCreditCards(input, replacement) {
|
|
907
|
-
let out = "";
|
|
908
|
-
let i = 0;
|
|
909
|
-
while (i < input.length) {
|
|
910
|
-
const ch = input.charAt(i);
|
|
911
|
-
if (/\d/.test(ch)) {
|
|
912
|
-
const { length, valid } = longestLuhnChunk(input.slice(i));
|
|
913
|
-
if (valid) {
|
|
914
|
-
out += replacement;
|
|
915
|
-
i += length;
|
|
916
|
-
continue;
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
out += ch;
|
|
920
|
-
i += 1;
|
|
921
|
-
}
|
|
922
|
-
return out;
|
|
923
|
-
}
|
|
924
|
-
function longestLuhnChunk(s) {
|
|
925
|
-
let length = 0;
|
|
926
|
-
let bestValid = 0;
|
|
927
|
-
while (length < s.length && length < 40) {
|
|
928
|
-
const ch = s.charAt(length);
|
|
929
|
-
if (!/\d/.test(ch) && ch !== "-") break;
|
|
930
|
-
length += 1;
|
|
931
|
-
const candidate = s.slice(0, length).replace(/\D/g, "");
|
|
932
|
-
if (candidate.length >= 13 && candidate.length <= 19) {
|
|
933
|
-
if (startsWithKnownPrefix(candidate) && passesLuhn(candidate)) {
|
|
934
|
-
bestValid = length;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
return { length: bestValid, valid: bestValid > 0 };
|
|
939
|
-
}
|
|
940
|
-
function startsWithKnownPrefix(digits) {
|
|
941
|
-
if (digits.startsWith("4")) return true;
|
|
942
|
-
const two = digits.slice(0, 2);
|
|
943
|
-
if (two === "51" || two === "52" || two === "53" || two === "54" || two === "55") return true;
|
|
944
|
-
const four = digits.slice(0, 4);
|
|
945
|
-
if (four === "2221" || four === "2720") return true;
|
|
946
|
-
const twoAgain = digits.slice(0, 2);
|
|
947
|
-
if (twoAgain === "34" || twoAgain === "37") return true;
|
|
948
|
-
if (four === "6011" || twoAgain === "65") return true;
|
|
949
|
-
return digits.startsWith("35");
|
|
950
|
-
}
|
|
951
|
-
function passesLuhn(digits) {
|
|
952
|
-
if (!/^\d+$/.test(digits)) return false;
|
|
953
|
-
let sum = 0;
|
|
954
|
-
let alt = false;
|
|
955
|
-
for (let i = digits.length - 1; i >= 0; i -= 1) {
|
|
956
|
-
const raw = digits.charCodeAt(i) - 48;
|
|
957
|
-
let value = raw;
|
|
958
|
-
if (alt) {
|
|
959
|
-
value *= 2;
|
|
960
|
-
if (value > 9) value -= 9;
|
|
961
|
-
}
|
|
962
|
-
sum += value;
|
|
963
|
-
alt = !alt;
|
|
964
|
-
}
|
|
965
|
-
return sum % 10 === 0;
|
|
966
|
-
}
|
|
967
|
-
function cloneRegex(source, flags) {
|
|
968
|
-
return new RegExp(source.source, flags + source.flags.replace(/g/g, ""));
|
|
969
|
-
}
|
|
970
|
-
function redactValue(value, depth, redactStringFn) {
|
|
971
|
-
if (depth > MAX_DEPTH) return value;
|
|
972
|
-
if (typeof value === "string") {
|
|
973
|
-
return isBase64DataUrl(value) ? value : redactStringFn(value);
|
|
974
|
-
}
|
|
975
|
-
if (Array.isArray(value))
|
|
976
|
-
return value.map((entry) => redactValue(entry, depth + 1, redactStringFn));
|
|
977
|
-
if (value !== null && typeof value === "object") {
|
|
978
|
-
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
|
|
979
|
-
return value;
|
|
980
|
-
}
|
|
981
|
-
const record = value;
|
|
982
|
-
const out = {};
|
|
983
|
-
for (const [key, entry] of Object.entries(record)) {
|
|
984
|
-
if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" || record.type === "encrypted" || record.type === "redacted")) {
|
|
985
|
-
out[key] = entry;
|
|
986
|
-
} else {
|
|
987
|
-
out[key] = redactValue(entry, depth + 1, redactStringFn);
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
return out;
|
|
991
|
-
}
|
|
992
|
-
return value;
|
|
993
|
-
}
|
|
994
|
-
function redactMessage(message, redactStringFn) {
|
|
995
|
-
if (message.role === "system") {
|
|
996
|
-
return { ...message, content: redactStringFn(message.content) };
|
|
997
|
-
}
|
|
998
|
-
return {
|
|
999
|
-
...message,
|
|
1000
|
-
content: redactMessageContent(message.content, redactStringFn)
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
function redactMessageContent(value, redactStringFn) {
|
|
1004
|
-
return redactValue(value, 0, redactStringFn);
|
|
1088
|
+
function refresh() {
|
|
1089
|
+
cache.clear();
|
|
1090
|
+
}
|
|
1091
|
+
return { getPrompt, getPromptText, getPromptChat, refresh };
|
|
1005
1092
|
}
|
|
1006
|
-
function
|
|
1007
|
-
|
|
1093
|
+
function normalizePrompt(raw, type) {
|
|
1094
|
+
if (type === "text") {
|
|
1095
|
+
if (typeof raw === "string") return raw;
|
|
1096
|
+
throw new Error("Expected text prompt to be a string");
|
|
1097
|
+
}
|
|
1098
|
+
if (!Array.isArray(raw)) {
|
|
1099
|
+
throw new Error("Expected chat prompt to be an array of messages");
|
|
1100
|
+
}
|
|
1101
|
+
return raw.map((entry) => {
|
|
1102
|
+
if (typeof entry !== "object" || entry === null) {
|
|
1103
|
+
throw new Error("Expected chat message to be an object");
|
|
1104
|
+
}
|
|
1105
|
+
const role = entry.role;
|
|
1106
|
+
const content = entry.content;
|
|
1107
|
+
if (typeof content !== "string") {
|
|
1108
|
+
throw new Error("Expected chat message content to be a string");
|
|
1109
|
+
}
|
|
1110
|
+
if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") {
|
|
1111
|
+
throw new Error(`Unexpected chat message role: ${String(role)}`);
|
|
1112
|
+
}
|
|
1113
|
+
return { role, content };
|
|
1114
|
+
});
|
|
1008
1115
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
import { LangfuseSpanProcessor } from "@langfuse/otel";
|
|
1013
|
-
import {
|
|
1014
|
-
LangfuseOtelSpanAttributes,
|
|
1015
|
-
startObservation
|
|
1016
|
-
} from "@langfuse/tracing";
|
|
1017
|
-
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
1018
|
-
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
1019
|
-
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
1020
|
-
|
|
1021
|
-
// src/capture.ts
|
|
1022
|
-
var DEFAULT_CAPTURE_MAX_BYTES = 262144;
|
|
1023
|
-
var MIN_CAPTURE_MAX_BYTES = 96;
|
|
1024
|
-
function validateCaptureMaxBytes(value) {
|
|
1025
|
-
const resolved = value ?? DEFAULT_CAPTURE_MAX_BYTES;
|
|
1026
|
-
if (!Number.isInteger(resolved) || resolved < MIN_CAPTURE_MAX_BYTES) {
|
|
1027
|
-
throw new TypeError(
|
|
1028
|
-
`Langfuse captureMaxBytes must be an integer of at least ${MIN_CAPTURE_MAX_BYTES}`
|
|
1029
|
-
);
|
|
1116
|
+
function buildAuthHeader2(publicKey, secretKey) {
|
|
1117
|
+
if (publicKey === void 0 || secretKey === void 0) {
|
|
1118
|
+
return {};
|
|
1030
1119
|
}
|
|
1031
|
-
|
|
1120
|
+
const encoded = Buffer.from(`${publicKey}:${secretKey}`).toString("base64");
|
|
1121
|
+
return { Authorization: `Basic ${encoded}` };
|
|
1032
1122
|
}
|
|
1033
|
-
function
|
|
1034
|
-
validateCaptureMaxBytes(maxBytes);
|
|
1035
|
-
const sanitized = sanitizeValue(value, 0, /* @__PURE__ */ new WeakSet());
|
|
1036
|
-
let serialized;
|
|
1123
|
+
async function readErrorBody2(response) {
|
|
1037
1124
|
try {
|
|
1038
|
-
|
|
1125
|
+
return await response.text();
|
|
1039
1126
|
} catch {
|
|
1040
|
-
return
|
|
1041
|
-
}
|
|
1042
|
-
const originalBytes = utf8Bytes(serialized);
|
|
1043
|
-
if (originalBytes <= maxBytes) {
|
|
1044
|
-
return sanitized;
|
|
1127
|
+
return "<unreadable>";
|
|
1045
1128
|
}
|
|
1046
|
-
const preview = boundedPreview(serialized, originalBytes, maxBytes);
|
|
1047
|
-
return {
|
|
1048
|
-
anviaTraceValue: "truncated",
|
|
1049
|
-
originalBytes,
|
|
1050
|
-
preview
|
|
1051
|
-
};
|
|
1052
1129
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
if (/^data:[^;,]+;base64,/i.test(value)) {
|
|
1059
|
-
return omitted("base64", utf8Bytes(value));
|
|
1060
|
-
}
|
|
1061
|
-
return value;
|
|
1130
|
+
|
|
1131
|
+
// src/tracing.ts
|
|
1132
|
+
var LangfuseObservationFactory = class {
|
|
1133
|
+
constructor(tracer) {
|
|
1134
|
+
this.tracer = tracer;
|
|
1062
1135
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
return
|
|
1136
|
+
tracer;
|
|
1137
|
+
agent(name, attributes, parent) {
|
|
1138
|
+
return new LangfuseAgent({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1066
1139
|
}
|
|
1067
|
-
|
|
1068
|
-
return
|
|
1140
|
+
span(name, attributes, parent) {
|
|
1141
|
+
return new LangfuseSpan({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1069
1142
|
}
|
|
1070
|
-
|
|
1071
|
-
return
|
|
1143
|
+
generation(name, attributes, parent) {
|
|
1144
|
+
return new LangfuseGeneration({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1072
1145
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
const result2 = value.map((entry) => sanitizeValue(entry, depth + 1, seen));
|
|
1076
|
-
seen.delete(value);
|
|
1077
|
-
return result2;
|
|
1146
|
+
tool(name, attributes, parent) {
|
|
1147
|
+
return new LangfuseTool({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1078
1148
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
for (const [key, entry] of Object.entries(record)) {
|
|
1082
|
-
if (key === "data" && typeof entry === "string" && (record.type === "base64" || record.type === "image" && typeof record.mediaType === "string")) {
|
|
1083
|
-
result[key] = omitted("base64", utf8Bytes(entry));
|
|
1084
|
-
continue;
|
|
1085
|
-
}
|
|
1086
|
-
result[key] = sanitizeValue(entry, depth + 1, seen);
|
|
1149
|
+
guardrail(name, attributes, parent) {
|
|
1150
|
+
return new LangfuseGuardrail({ otelSpan: this.startSpan(name, parent), attributes });
|
|
1087
1151
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
...originalBytes === void 0 ? {} : { originalBytes }
|
|
1096
|
-
};
|
|
1097
|
-
}
|
|
1098
|
-
function boundedPreview(value, originalBytes, maxBytes) {
|
|
1099
|
-
let low = 0;
|
|
1100
|
-
let high = value.length;
|
|
1101
|
-
while (low < high) {
|
|
1102
|
-
const middle = Math.ceil((low + high) / 2);
|
|
1103
|
-
const candidate = {
|
|
1104
|
-
anviaTraceValue: "truncated",
|
|
1105
|
-
originalBytes,
|
|
1106
|
-
preview: value.slice(0, middle)
|
|
1107
|
-
};
|
|
1108
|
-
if (utf8Bytes(JSON.stringify(candidate)) <= maxBytes) {
|
|
1109
|
-
low = middle;
|
|
1110
|
-
} else {
|
|
1111
|
-
high = middle - 1;
|
|
1112
|
-
}
|
|
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
|
+
});
|
|
1113
1159
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
return typeof Buffer === "undefined" ? new TextEncoder().encode(value).byteLength : Buffer.byteLength(value, "utf8");
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// src/tracing.ts
|
|
1121
|
-
var langfuse = {
|
|
1122
|
-
create(options = {}) {
|
|
1123
|
-
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);
|
|
1124
1163
|
}
|
|
1125
1164
|
};
|
|
1126
|
-
var
|
|
1127
|
-
processor;
|
|
1128
|
-
sdk;
|
|
1165
|
+
var LangfuseClient = class {
|
|
1129
1166
|
[langfuseResolvedConfigSymbol];
|
|
1130
1167
|
publicKey;
|
|
1131
1168
|
secretKey;
|
|
1132
1169
|
baseUrl;
|
|
1133
1170
|
serviceName;
|
|
1134
1171
|
timeoutMs;
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
constructor(options) {
|
|
1172
|
+
options;
|
|
1173
|
+
resource;
|
|
1174
|
+
initialization;
|
|
1175
|
+
closePromise;
|
|
1176
|
+
closed = false;
|
|
1177
|
+
constructor(options = {}) {
|
|
1178
|
+
this.options = options;
|
|
1143
1179
|
const resolvedConfig = resolveLangfuseConfig(options);
|
|
1144
1180
|
this[langfuseResolvedConfigSymbol] = resolvedConfig;
|
|
1145
1181
|
this.publicKey = resolvedConfig.publicKey;
|
|
@@ -1147,50 +1183,82 @@ var LangfuseAgentObserver = class {
|
|
|
1147
1183
|
this.baseUrl = resolvedConfig.baseUrl;
|
|
1148
1184
|
this.serviceName = resolvedConfig.serviceName;
|
|
1149
1185
|
this.timeoutMs = resolvedConfig.timeoutMs;
|
|
1150
|
-
|
|
1151
|
-
|
|
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
|
+
}
|
|
1152
1199
|
};
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
+
}
|
|
1162
1221
|
};
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
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();
|
|
1187
1252
|
const traceId = args.trace?.traceId;
|
|
1188
|
-
const capturedInput =
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
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);
|
|
1194
1262
|
const metadata = {
|
|
1195
1263
|
agentName: args.agentName,
|
|
1196
1264
|
agentDescription: args.agentDescription,
|
|
@@ -1206,69 +1274,41 @@ var LangfuseAgentObserver = class {
|
|
|
1206
1274
|
input: capturedInput,
|
|
1207
1275
|
metadata
|
|
1208
1276
|
};
|
|
1209
|
-
if (args.trace?.version !== void 0)
|
|
1210
|
-
|
|
1211
|
-
}
|
|
1212
|
-
const root = startObservation(
|
|
1277
|
+
if (args.trace?.version !== void 0) rootAttributes.version = args.trace.version;
|
|
1278
|
+
const root = resource.observations.agent(
|
|
1213
1279
|
args.agentName ?? "agent.run",
|
|
1214
1280
|
rootAttributes,
|
|
1215
|
-
traceId === void 0 ?
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
spanId: "0000000000000001",
|
|
1220
|
-
traceFlags: 1
|
|
1221
|
-
}
|
|
1281
|
+
traceId === void 0 ? void 0 : {
|
|
1282
|
+
traceId,
|
|
1283
|
+
spanId: "0000000000000001",
|
|
1284
|
+
traceFlags: TraceFlags.SAMPLED
|
|
1222
1285
|
}
|
|
1223
1286
|
);
|
|
1224
1287
|
applyTraceAttributes(root, args, capturedTraceMetadata);
|
|
1225
|
-
|
|
1226
|
-
const runObserver = new LangfuseRunObserver(
|
|
1288
|
+
return new LangfuseRunObserver(
|
|
1227
1289
|
root,
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
promptRef,
|
|
1233
|
-
{
|
|
1234
|
-
redactor: this.redactor,
|
|
1235
|
-
redactInputs: this.redactInputs,
|
|
1236
|
-
redactOutputs: this.redactOutputs,
|
|
1237
|
-
captureMode: this.captureMode,
|
|
1238
|
-
captureMaxBytes: this.captureMaxBytes
|
|
1239
|
-
}
|
|
1290
|
+
resource.observations,
|
|
1291
|
+
{ traceId: root.traceId, observationId: root.id },
|
|
1292
|
+
resolvePromptRef(args),
|
|
1293
|
+
capture
|
|
1240
1294
|
);
|
|
1241
|
-
this.currentHandle = runObserver.getHandle();
|
|
1242
|
-
runObserver.setCurrentHandle = (handle) => {
|
|
1243
|
-
this.currentHandle = handle;
|
|
1244
|
-
};
|
|
1245
|
-
runObserver.clearCurrentHandle = () => {
|
|
1246
|
-
if (this.currentHandle === runObserver.getHandle()) {
|
|
1247
|
-
this.currentHandle = void 0;
|
|
1248
|
-
}
|
|
1249
|
-
};
|
|
1250
|
-
return runObserver;
|
|
1251
1295
|
}
|
|
1252
1296
|
async flush() {
|
|
1253
|
-
|
|
1254
|
-
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();
|
|
1255
1302
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1303
|
+
close() {
|
|
1304
|
+
this.closePromise ??= this.closeResources();
|
|
1305
|
+
return this.closePromise;
|
|
1259
1306
|
}
|
|
1260
|
-
async
|
|
1261
|
-
await this.
|
|
1307
|
+
async [Symbol.asyncDispose]() {
|
|
1308
|
+
await this.close();
|
|
1262
1309
|
}
|
|
1263
1310
|
scoreQueueDepth() {
|
|
1264
|
-
return this.queue?.depth() ?? 0;
|
|
1265
|
-
}
|
|
1266
|
-
getCurrentTrace() {
|
|
1267
|
-
return this.currentHandle;
|
|
1268
|
-
}
|
|
1269
|
-
captureInput(value) {
|
|
1270
|
-
const redacted = this.redactor === void 0 || this.redactInputs === void 0 ? value : applyRedaction(this.redactor, value, this.redactInputs);
|
|
1271
|
-
return sanitizeTraceValue(redacted, this.captureMaxBytes);
|
|
1311
|
+
return this.resource?.queue?.depth() ?? 0;
|
|
1272
1312
|
}
|
|
1273
1313
|
async score(args) {
|
|
1274
1314
|
if (args.traceId === void 0 || args.traceId.length === 0) {
|
|
@@ -1278,12 +1318,100 @@ var LangfuseAgentObserver = class {
|
|
|
1278
1318
|
throw new Error("Langfuse score requires publicKey and secretKey");
|
|
1279
1319
|
}
|
|
1280
1320
|
assertScoreValue(args.value, args.dataType);
|
|
1281
|
-
|
|
1282
|
-
|
|
1321
|
+
const resource = await this.resources();
|
|
1322
|
+
if (resource.queue !== null) {
|
|
1323
|
+
resource.queue.enqueue(args);
|
|
1283
1324
|
return;
|
|
1284
1325
|
}
|
|
1285
1326
|
await this.sendScore(args);
|
|
1286
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
|
+
}
|
|
1287
1415
|
async sendScore(args) {
|
|
1288
1416
|
const body = buildScoreBody2(args);
|
|
1289
1417
|
const response = await fetch(`${this.baseUrl}/api/public/scores`, {
|
|
@@ -1302,6 +1430,37 @@ var LangfuseAgentObserver = class {
|
|
|
1302
1430
|
}
|
|
1303
1431
|
}
|
|
1304
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
|
+
}
|
|
1305
1464
|
function assertScoreValue(value, dataType) {
|
|
1306
1465
|
if (dataType === "NUMERIC") {
|
|
1307
1466
|
if (typeof value !== "number") {
|
|
@@ -1352,7 +1511,7 @@ function applyTraceAttributes(root, args, capturedMetadata) {
|
|
|
1352
1511
|
root.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_SESSION_ID, args.trace.sessionId);
|
|
1353
1512
|
}
|
|
1354
1513
|
if (args.trace?.tags !== void 0) {
|
|
1355
|
-
root.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_TAGS, args.trace.tags);
|
|
1514
|
+
root.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_TAGS, [...args.trace.tags]);
|
|
1356
1515
|
}
|
|
1357
1516
|
for (const [key, value] of Object.entries(
|
|
1358
1517
|
isRecord(capturedMetadata) ? capturedMetadata : { value: capturedMetadata }
|
|
@@ -1441,20 +1600,15 @@ function asMetadata(value) {
|
|
|
1441
1600
|
return isRecord(value) ? value : { value };
|
|
1442
1601
|
}
|
|
1443
1602
|
function eventStartTime(value) {
|
|
1444
|
-
if (value
|
|
1445
|
-
return Number.isNaN(value.getTime()) ? void 0 : value;
|
|
1446
|
-
}
|
|
1447
|
-
if (typeof value !== "string") {
|
|
1448
|
-
return void 0;
|
|
1449
|
-
}
|
|
1603
|
+
if (value === void 0) return void 0;
|
|
1450
1604
|
const parsed = new Date(value);
|
|
1451
1605
|
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
1452
1606
|
}
|
|
1453
1607
|
var LangfuseRunObserver = class {
|
|
1454
|
-
constructor(root,
|
|
1608
|
+
constructor(root, observations, trace2, promptRef, redaction) {
|
|
1455
1609
|
this.root = root;
|
|
1456
|
-
this.
|
|
1457
|
-
this.
|
|
1610
|
+
this.observations = observations;
|
|
1611
|
+
this.trace = trace2;
|
|
1458
1612
|
this.promptRef = promptRef;
|
|
1459
1613
|
this.redactor = redaction.redactor;
|
|
1460
1614
|
this.redactInputs = redaction.redactInputs;
|
|
@@ -1463,13 +1617,9 @@ var LangfuseRunObserver = class {
|
|
|
1463
1617
|
this.captureMaxBytes = redaction.captureMaxBytes;
|
|
1464
1618
|
}
|
|
1465
1619
|
root;
|
|
1620
|
+
observations;
|
|
1466
1621
|
trace;
|
|
1467
1622
|
turnSpans = /* @__PURE__ */ new Map();
|
|
1468
|
-
// Assigned by LangfuseAgentObserver.startRun so that the run can
|
|
1469
|
-
// publish trace-handle updates back to the agent observer.
|
|
1470
|
-
setCurrentHandle;
|
|
1471
|
-
clearCurrentHandle;
|
|
1472
|
-
handle;
|
|
1473
1623
|
promptRef;
|
|
1474
1624
|
redactor;
|
|
1475
1625
|
redactInputs;
|
|
@@ -1496,7 +1646,7 @@ var LangfuseRunObserver = class {
|
|
|
1496
1646
|
safeInput.tools = args.request.tools;
|
|
1497
1647
|
safeInput.providerTools = args.request.providerTools;
|
|
1498
1648
|
safeInput.outputSchema = args.request.outputSchema;
|
|
1499
|
-
safeInput.
|
|
1649
|
+
safeInput.providerOptions = args.request.providerOptions;
|
|
1500
1650
|
}
|
|
1501
1651
|
const metadata = {
|
|
1502
1652
|
turn: args.turn,
|
|
@@ -1504,7 +1654,7 @@ var LangfuseRunObserver = class {
|
|
|
1504
1654
|
toolNames: args.request.tools.map((tool) => tool.name),
|
|
1505
1655
|
providerToolNames: args.request.providerTools?.map((tool) => tool.name) ?? [],
|
|
1506
1656
|
hasOutputSchema: args.request.outputSchema !== void 0,
|
|
1507
|
-
|
|
1657
|
+
providerOptionKeys: isRecord(args.request.providerOptions) ? Object.keys(args.request.providerOptions) : []
|
|
1508
1658
|
};
|
|
1509
1659
|
if (this.captureMode === "full" && args.providerRequest !== void 0) {
|
|
1510
1660
|
metadata.providerRequest = args.providerRequest;
|
|
@@ -1512,7 +1662,7 @@ var LangfuseRunObserver = class {
|
|
|
1512
1662
|
if (args.modelInfo !== void 0) {
|
|
1513
1663
|
const modelInfo = {
|
|
1514
1664
|
provider: args.modelInfo.provider,
|
|
1515
|
-
|
|
1665
|
+
modelId: args.modelInfo.modelId
|
|
1516
1666
|
};
|
|
1517
1667
|
if (args.modelInfo.capabilities !== void 0) {
|
|
1518
1668
|
modelInfo.capabilities = args.modelInfo.capabilities;
|
|
@@ -1522,7 +1672,7 @@ var LangfuseRunObserver = class {
|
|
|
1522
1672
|
Object.assign(metadata, promptMetadata(this.promptRef));
|
|
1523
1673
|
const generationAttributes = {
|
|
1524
1674
|
input: this.redactInputValue(safeInput),
|
|
1525
|
-
model: args.
|
|
1675
|
+
model: args.modelInfo?.modelId ?? "unknown",
|
|
1526
1676
|
modelParameters: modelParameters(args.request),
|
|
1527
1677
|
metadata: asMetadata(this.redactInputValue(metadata))
|
|
1528
1678
|
};
|
|
@@ -1533,9 +1683,11 @@ var LangfuseRunObserver = class {
|
|
|
1533
1683
|
isFallback: false
|
|
1534
1684
|
};
|
|
1535
1685
|
}
|
|
1536
|
-
const generation =
|
|
1537
|
-
|
|
1538
|
-
|
|
1686
|
+
const generation = this.observations.generation(
|
|
1687
|
+
`model.turn.${args.turn}`,
|
|
1688
|
+
generationAttributes,
|
|
1689
|
+
turn
|
|
1690
|
+
);
|
|
1539
1691
|
return new LangfuseGenerationObserver(generation, this, /* @__PURE__ */ new Date());
|
|
1540
1692
|
}
|
|
1541
1693
|
startTool(args) {
|
|
@@ -1549,7 +1701,7 @@ var LangfuseRunObserver = class {
|
|
|
1549
1701
|
if (args.toolDefinition !== void 0) metadata.toolDefinition = args.toolDefinition;
|
|
1550
1702
|
if (args.toolMetadata !== void 0) metadata.toolMetadata = args.toolMetadata;
|
|
1551
1703
|
}
|
|
1552
|
-
const tool =
|
|
1704
|
+
const tool = this.observations.tool(
|
|
1553
1705
|
`tool.${args.toolName}`,
|
|
1554
1706
|
{
|
|
1555
1707
|
input: this.redactInputValue({
|
|
@@ -1558,18 +1710,22 @@ var LangfuseRunObserver = class {
|
|
|
1558
1710
|
}),
|
|
1559
1711
|
metadata: asMetadata(this.redactInputValue(metadata))
|
|
1560
1712
|
},
|
|
1561
|
-
|
|
1713
|
+
turn
|
|
1562
1714
|
);
|
|
1563
|
-
return new LangfuseToolObserver(tool, this);
|
|
1715
|
+
return new LangfuseToolObserver(tool, this, this.observations);
|
|
1564
1716
|
}
|
|
1565
1717
|
end(args) {
|
|
1566
1718
|
this.closeAllTurns();
|
|
1567
|
-
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);
|
|
1568
1721
|
const metadata = {
|
|
1722
|
+
runId: args.runId,
|
|
1723
|
+
status: args.status,
|
|
1569
1724
|
usage: args.usage,
|
|
1570
1725
|
messageCount: args.messages.length,
|
|
1571
1726
|
sources: this.redactOutputValue(args.sources),
|
|
1572
|
-
providerToolCalls: this.redactOutputValue(args.providerToolCalls)
|
|
1727
|
+
providerToolCalls: this.redactOutputValue(args.providerToolCalls),
|
|
1728
|
+
resumedFrom: this.redactOutputValue(args.resumedFrom)
|
|
1573
1729
|
};
|
|
1574
1730
|
if (this.captureMode === "full") {
|
|
1575
1731
|
metadata.messages = this.redactTranscript(args.messages);
|
|
@@ -1578,7 +1734,6 @@ var LangfuseRunObserver = class {
|
|
|
1578
1734
|
output: redactedOutput,
|
|
1579
1735
|
metadata
|
|
1580
1736
|
}).end();
|
|
1581
|
-
this.clearCurrentHandle?.();
|
|
1582
1737
|
}
|
|
1583
1738
|
error(args) {
|
|
1584
1739
|
this.closeAllTurns();
|
|
@@ -1598,7 +1753,6 @@ var LangfuseRunObserver = class {
|
|
|
1598
1753
|
},
|
|
1599
1754
|
metadata
|
|
1600
1755
|
}).end();
|
|
1601
|
-
this.clearCurrentHandle?.();
|
|
1602
1756
|
}
|
|
1603
1757
|
event(args) {
|
|
1604
1758
|
const metadata = asMetadata(this.redactOutputValue(args.attributes ?? {}));
|
|
@@ -1607,43 +1761,19 @@ var LangfuseRunObserver = class {
|
|
|
1607
1761
|
attributes.level = args.level;
|
|
1608
1762
|
}
|
|
1609
1763
|
const startTime = eventStartTime(args.timestamp);
|
|
1610
|
-
this.
|
|
1611
|
-
asType: "event",
|
|
1612
|
-
...startTime === void 0 ? {} : { startTime }
|
|
1613
|
-
});
|
|
1614
|
-
}
|
|
1615
|
-
getHandle() {
|
|
1616
|
-
return this.handle;
|
|
1617
|
-
}
|
|
1618
|
-
buildHandle() {
|
|
1619
|
-
return {
|
|
1620
|
-
traceId: this.trace.traceId ?? "",
|
|
1621
|
-
observationId: this.trace.observationId ?? "",
|
|
1622
|
-
addAttributes: (attributes) => {
|
|
1623
|
-
this.root.update({ metadata: asMetadata(this.redactOutputValue(attributes)) });
|
|
1624
|
-
this.setCurrentHandle?.(this.handle);
|
|
1625
|
-
},
|
|
1626
|
-
addEvent: (name, attributes) => {
|
|
1627
|
-
this.root.startObservation(
|
|
1628
|
-
name,
|
|
1629
|
-
{ metadata: asMetadata(this.redactOutputValue(attributes ?? {})) },
|
|
1630
|
-
{ asType: "event" }
|
|
1631
|
-
);
|
|
1632
|
-
this.setCurrentHandle?.(this.handle);
|
|
1633
|
-
}
|
|
1634
|
-
};
|
|
1764
|
+
this.observations.event(args.name, attributes, this.root, startTime);
|
|
1635
1765
|
}
|
|
1636
1766
|
turnSpan(turn) {
|
|
1637
1767
|
const existing = this.turnSpans.get(turn);
|
|
1638
1768
|
if (existing !== void 0) {
|
|
1639
1769
|
return existing;
|
|
1640
1770
|
}
|
|
1641
|
-
const span = this.
|
|
1771
|
+
const span = this.observations.span(
|
|
1642
1772
|
`turn.${turn}`,
|
|
1643
1773
|
{
|
|
1644
1774
|
metadata: { turn }
|
|
1645
1775
|
},
|
|
1646
|
-
|
|
1776
|
+
this.root
|
|
1647
1777
|
);
|
|
1648
1778
|
this.turnSpans.set(turn, span);
|
|
1649
1779
|
return span;
|
|
@@ -1686,7 +1816,9 @@ var LangfuseGenerationObserver = class {
|
|
|
1686
1816
|
});
|
|
1687
1817
|
}
|
|
1688
1818
|
end(args) {
|
|
1689
|
-
const redactedText = this.run.redactOutputValue(
|
|
1819
|
+
const redactedText = this.run.redactOutputValue(
|
|
1820
|
+
textFromObservedAssistantContent(args.response.choice)
|
|
1821
|
+
);
|
|
1690
1822
|
const redactedChoice = this.run.redactOutputValue(args.response.choice);
|
|
1691
1823
|
const metadata = { turn: args.turn };
|
|
1692
1824
|
if (args.firstDeltaMs !== void 0) metadata.firstDeltaMs = args.firstDeltaMs;
|
|
@@ -1718,13 +1850,18 @@ var LangfuseGenerationObserver = class {
|
|
|
1718
1850
|
}).end();
|
|
1719
1851
|
}
|
|
1720
1852
|
};
|
|
1853
|
+
function textFromObservedAssistantContent(content) {
|
|
1854
|
+
return content.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n");
|
|
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
|