@narumitw/pi-langfuse 0.50.5 → 0.51.0
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 +125 -62
- package/dist/chunks/chunk-IASLJKKB.js +316 -0
- package/dist/chunks/chunk-IASLJKKB.js.map +7 -0
- package/dist/chunks/runtime-6MIKLWNJ.js +352 -0
- package/dist/chunks/runtime-6MIKLWNJ.js.map +7 -0
- package/dist/config.d.ts +33 -0
- package/dist/git.d.ts +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1551 -0
- package/dist/index.js.map +7 -0
- package/dist/index.ts +594 -521
- package/dist/index.ts.map +4 -4
- package/dist/langfuse.d.ts +13 -0
- package/dist/pi-session.d.ts +43 -0
- package/dist/public-api.d.ts +4 -0
- package/dist/runtime-core.d.ts +26 -0
- package/dist/runtime.d.ts +22 -0
- package/dist/sanitizer.d.ts +2 -0
- package/dist/tracing.d.ts +202 -0
- package/package.json +63 -59
- package/src/config.ts +211 -232
- package/src/git.ts +60 -0
- package/src/index.ts +9 -0
- package/src/langfuse.ts +307 -563
- package/src/pi-session.ts +570 -0
- package/src/public-api.ts +17 -0
- package/src/runtime-core.ts +113 -0
- package/src/runtime.ts +393 -227
- package/src/sanitizer.ts +115 -117
- package/src/tracing.ts +828 -843
- package/dist/chunks/runtime-247EMSG3.js +0 -218
- package/dist/chunks/runtime-247EMSG3.js.map +0 -7
package/src/sanitizer.ts
CHANGED
|
@@ -8,140 +8,138 @@ const BASE64_DATA_URI_OMITTED = "[base64 data URI omitted]";
|
|
|
8
8
|
const OPAQUE_SIGNATURE_KEYS = new Set(["thinkingSignature", "textSignature", "thoughtSignature"]);
|
|
9
9
|
|
|
10
10
|
export function sanitizeTraceValue(value: unknown): unknown {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const budget = { remaining: MAX_CAPTURE_BYTES };
|
|
12
|
+
const sanitized = sanitize(value, new WeakSet<object>(), 0, budget);
|
|
13
|
+
return serializedBytes(sanitized) <= MAX_CAPTURE_BYTES ? sanitized : TRUNCATED;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function sanitize(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
if (value instanceof Error) {
|
|
39
|
-
return sanitize({ name: value.name, message: value.message }, active, depth, budget);
|
|
40
|
-
}
|
|
41
|
-
if (depth >= MAX_DEPTH) return consume("[maximum depth reached]", budget);
|
|
42
|
-
if (active.has(value)) return consume("[circular]", budget);
|
|
43
|
-
active.add(value);
|
|
16
|
+
function sanitize(value: unknown, active: WeakSet<object>, depth: number, budget: { remaining: number }): unknown {
|
|
17
|
+
if (budget.remaining <= byteLength(TRUNCATED)) return TRUNCATED;
|
|
18
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
19
|
+
return consume(value, budget);
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === "string") {
|
|
22
|
+
return consume(sanitizeString(value, Math.min(MAX_STRING_LENGTH, budget.remaining)), budget);
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === "bigint") return consume(value.toString(), budget);
|
|
25
|
+
if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (value instanceof Date) {
|
|
29
|
+
return consume(Number.isNaN(value.getTime()) ? "[invalid date]" : value.toISOString(), budget);
|
|
30
|
+
}
|
|
31
|
+
if (value instanceof Error) {
|
|
32
|
+
return sanitize({ name: value.name, message: value.message }, active, depth, budget);
|
|
33
|
+
}
|
|
34
|
+
if (depth >= MAX_DEPTH) return consume("[maximum depth reached]", budget);
|
|
35
|
+
if (active.has(value)) return consume("[circular]", budget);
|
|
36
|
+
active.add(value);
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
38
|
+
let result: unknown;
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
const items: unknown[] = [];
|
|
41
|
+
for (const item of value.slice(0, MAX_COLLECTION_LENGTH)) {
|
|
42
|
+
if (budget.remaining <= byteLength(TRUNCATED)) break;
|
|
43
|
+
items.push(sanitize(item, active, depth + 1, budget));
|
|
44
|
+
}
|
|
45
|
+
if (items.length < value.length) items.push(`[${value.length - items.length} items omitted]`);
|
|
46
|
+
result = items;
|
|
47
|
+
} else {
|
|
48
|
+
const record = value as Record<string, unknown>;
|
|
49
|
+
const output: Record<string, unknown> = {};
|
|
50
|
+
const objectType = readProperty(record, "type").value;
|
|
51
|
+
const redactData = objectType === "image" || objectType === "base64";
|
|
52
|
+
let visited = 0;
|
|
53
|
+
let processed = 0;
|
|
54
|
+
let omitted = false;
|
|
55
|
+
try {
|
|
56
|
+
for (const key in record) {
|
|
57
|
+
if (visited >= MAX_COLLECTION_LENGTH || budget.remaining <= byteLength(TRUNCATED)) {
|
|
58
|
+
omitted = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
visited += 1;
|
|
62
|
+
if (!Object.hasOwn(record, key) || OPAQUE_SIGNATURE_KEYS.has(key)) continue;
|
|
63
|
+
const keyBudget = Math.min(MAX_STRING_LENGTH, Math.max(0, budget.remaining - 4));
|
|
64
|
+
if (key.length > keyBudget) {
|
|
65
|
+
omitted = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
const keyBytes = byteLength(key);
|
|
69
|
+
if (keyBytes > keyBudget) {
|
|
70
|
+
omitted = true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
const sanitizedKey = sanitizeString(key, keyBudget);
|
|
74
|
+
budget.remaining -= byteLength(sanitizedKey) + 4;
|
|
75
|
+
const property = readProperty(record, key);
|
|
76
|
+
if (redactData && key === "data") {
|
|
77
|
+
output[sanitizedKey] = consume("[base64 omitted]", budget);
|
|
78
|
+
} else if (!property.ok) output[sanitizedKey] = consume("[unreadable property]", budget);
|
|
79
|
+
else output[sanitizedKey] = sanitize(property.value, active, depth + 1, budget);
|
|
80
|
+
processed += 1;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
if (processed === 0) {
|
|
84
|
+
active.delete(value);
|
|
85
|
+
return consume("[unreadable object]", budget);
|
|
86
|
+
}
|
|
87
|
+
omitted = true;
|
|
88
|
+
}
|
|
89
|
+
if (omitted) output.$truncated = "additional object entries omitted";
|
|
90
|
+
result = output;
|
|
91
|
+
}
|
|
92
|
+
active.delete(value);
|
|
93
|
+
return result;
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
function readProperty(
|
|
103
|
-
|
|
104
|
-
|
|
97
|
+
record: Record<string, unknown>,
|
|
98
|
+
key: string,
|
|
105
99
|
): { ok: true; value: unknown } | { ok: false; value?: undefined } {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
100
|
+
try {
|
|
101
|
+
return { ok: true, value: record[key] };
|
|
102
|
+
} catch {
|
|
103
|
+
return { ok: false };
|
|
104
|
+
}
|
|
111
105
|
}
|
|
112
106
|
|
|
113
107
|
function consume<T>(value: T, budget: { remaining: number }): T | string {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
108
|
+
const size = serializedBytes(value);
|
|
109
|
+
if (size > budget.remaining) {
|
|
110
|
+
budget.remaining -= byteLength(TRUNCATED);
|
|
111
|
+
return TRUNCATED;
|
|
112
|
+
}
|
|
113
|
+
budget.remaining -= size;
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function sanitizeString(value: string, maxBytes: number): string {
|
|
118
|
+
return truncateString(value, maxBytes).replace(BASE64_DATA_URI, BASE64_DATA_URI_OMITTED);
|
|
121
119
|
}
|
|
122
120
|
|
|
123
121
|
function truncateString(value: string, maxBytes: number): string {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
122
|
+
const boundedPrefix = value.slice(0, maxBytes);
|
|
123
|
+
if (boundedPrefix.length === value.length && byteLength(boundedPrefix) <= maxBytes) {
|
|
124
|
+
return boundedPrefix;
|
|
125
|
+
}
|
|
126
|
+
const suffix = "… [truncated]";
|
|
127
|
+
const target = Math.max(0, maxBytes - byteLength(suffix) - 2);
|
|
128
|
+
let bytes = 0;
|
|
129
|
+
let output = "";
|
|
130
|
+
for (const character of boundedPrefix) {
|
|
131
|
+
const size = byteLength(character);
|
|
132
|
+
if (bytes + size > target) break;
|
|
133
|
+
output += character;
|
|
134
|
+
bytes += size;
|
|
135
|
+
}
|
|
136
|
+
return `${output}${suffix}`;
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
function serializedBytes(value: unknown): number {
|
|
142
|
-
|
|
140
|
+
return byteLength(JSON.stringify(value) ?? "null");
|
|
143
141
|
}
|
|
144
142
|
|
|
145
143
|
function byteLength(value: string): number {
|
|
146
|
-
|
|
144
|
+
return Buffer.byteLength(value, "utf8");
|
|
147
145
|
}
|