@narumitw/pi-langfuse 0.14.1 → 0.15.1
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/package.json +2 -1
- package/src/tracing.ts +43 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-langfuse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Pi extension that traces LLM generations and tool activity to Langfuse.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"@langfuse/otel": "^4.0.0",
|
|
33
33
|
"@langfuse/tracing": "^4.0.0",
|
|
34
34
|
"@opentelemetry/api": "^1.9.0",
|
|
35
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
|
|
35
36
|
"@opentelemetry/sdk-trace-base": "^2.9.0",
|
|
36
37
|
"@opentelemetry/sdk-trace-node": "^2.9.0"
|
|
37
38
|
},
|
package/src/tracing.ts
CHANGED
|
@@ -358,9 +358,9 @@ function sanitize(
|
|
|
358
358
|
return consume(value, budget);
|
|
359
359
|
}
|
|
360
360
|
if (typeof value === "string") {
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
return consume(
|
|
361
|
+
const bounded = truncateString(value, Math.min(MAX_STRING_LENGTH, budget.remaining));
|
|
362
|
+
const redacted = bounded.replace(BASE64_DATA_URI, BASE64_DATA_URI_OMITTED);
|
|
363
|
+
return consume(redacted, budget);
|
|
364
364
|
}
|
|
365
365
|
if (typeof value === "bigint") return consume(value.toString(), budget);
|
|
366
366
|
if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") {
|
|
@@ -387,30 +387,46 @@ function sanitize(
|
|
|
387
387
|
result = items;
|
|
388
388
|
} else {
|
|
389
389
|
const record = value as Record<string, unknown>;
|
|
390
|
-
let keys: string[];
|
|
391
|
-
try {
|
|
392
|
-
keys = Object.keys(record);
|
|
393
|
-
} catch {
|
|
394
|
-
active.delete(value);
|
|
395
|
-
return consume("[unreadable object]", budget);
|
|
396
|
-
}
|
|
397
390
|
const output: Record<string, unknown> = {};
|
|
398
391
|
const objectType = readProperty(record, "type").value;
|
|
399
392
|
const redactData = objectType === "image" || objectType === "base64";
|
|
393
|
+
let visited = 0;
|
|
400
394
|
let processed = 0;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
395
|
+
let omitted = false;
|
|
396
|
+
try {
|
|
397
|
+
for (const key in record) {
|
|
398
|
+
if (visited >= MAX_COLLECTION_LENGTH || budget.remaining <= byteLength(TRUNCATED)) {
|
|
399
|
+
omitted = true;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
visited += 1;
|
|
403
|
+
const keyBudget = Math.min(MAX_STRING_LENGTH, Math.max(0, budget.remaining - 4));
|
|
404
|
+
if (key.length > keyBudget) {
|
|
405
|
+
omitted = true;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
const keyBytes = byteLength(key);
|
|
409
|
+
if (keyBytes > keyBudget) {
|
|
410
|
+
omitted = true;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
budget.remaining -= keyBytes + 4;
|
|
414
|
+
if (!Object.hasOwn(record, key)) continue;
|
|
415
|
+
const property = readProperty(record, key);
|
|
416
|
+
if (redactData && key === "data") {
|
|
417
|
+
output[key] = consume("[base64 omitted]", budget);
|
|
418
|
+
} else if (!property.ok) output[key] = consume("[unreadable property]", budget);
|
|
419
|
+
else output[key] = sanitize(property.value, active, depth + 1, budget);
|
|
420
|
+
processed += 1;
|
|
421
|
+
}
|
|
422
|
+
} catch {
|
|
423
|
+
if (processed === 0) {
|
|
424
|
+
active.delete(value);
|
|
425
|
+
return consume("[unreadable object]", budget);
|
|
426
|
+
}
|
|
427
|
+
omitted = true;
|
|
413
428
|
}
|
|
429
|
+
if (omitted) output["$truncated"] = "additional object entries omitted";
|
|
414
430
|
result = output;
|
|
415
431
|
}
|
|
416
432
|
active.delete(value);
|
|
@@ -439,12 +455,15 @@ function consume<T>(value: T, budget: { remaining: number }): T | string {
|
|
|
439
455
|
}
|
|
440
456
|
|
|
441
457
|
function truncateString(value: string, maxBytes: number): string {
|
|
442
|
-
|
|
458
|
+
const boundedPrefix = value.slice(0, maxBytes);
|
|
459
|
+
if (boundedPrefix.length === value.length && byteLength(boundedPrefix) <= maxBytes) {
|
|
460
|
+
return boundedPrefix;
|
|
461
|
+
}
|
|
443
462
|
const suffix = "… [truncated]";
|
|
444
463
|
const target = Math.max(0, maxBytes - byteLength(suffix) - 2);
|
|
445
464
|
let bytes = 0;
|
|
446
465
|
let output = "";
|
|
447
|
-
for (const character of
|
|
466
|
+
for (const character of boundedPrefix) {
|
|
448
467
|
const size = byteLength(character);
|
|
449
468
|
if (bytes + size > target) break;
|
|
450
469
|
output += character;
|