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