@narumitw/pi-langfuse 0.51.2 → 0.52.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 +46 -3
- package/dist/chunks/chunk-N7WSLB5Q.ts +88 -0
- package/dist/chunks/chunk-N7WSLB5Q.ts.map +7 -0
- package/dist/chunks/{runtime-6ZHLVC3A.ts → runtime-GLRAF7FE.ts} +2 -2
- package/dist/chunks/runtime-GLRAF7FE.ts.map +7 -0
- package/dist/config.d.ts +1 -1
- package/dist/durable-projection.d.ts +9 -0
- package/dist/durable-recorder.d.ts +49 -0
- package/dist/durable.d.ts +25 -0
- package/dist/durable.js +638 -0
- package/dist/durable.js.map +7 -0
- package/dist/index.js +248 -159
- package/dist/index.js.map +4 -4
- package/dist/index.ts +244 -16
- package/dist/index.ts.map +4 -4
- package/dist/library-chunks/chunk-B64FXCNE.js +84 -0
- package/dist/library-chunks/chunk-B64FXCNE.js.map +7 -0
- package/dist/library-chunks/chunk-ETFOPFEP.js +10 -0
- package/dist/library-chunks/chunk-ETFOPFEP.js.map +7 -0
- package/dist/library-chunks/chunk-JPFN6GGS.js +153 -0
- package/dist/library-chunks/chunk-JPFN6GGS.js.map +7 -0
- package/dist/library-chunks/{runtime-A6SQ6VEH.js → runtime-6KYOZVOE.js} +5 -3
- package/dist/library-chunks/runtime-6KYOZVOE.js.map +7 -0
- package/dist/public-api.d.ts +1 -3
- package/dist/runtime-api.d.ts +3 -0
- package/dist/runtime-defaults.d.ts +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/sanitizer.d.ts +1 -1
- package/docs/durable.md +126 -0
- package/package.json +22 -2
- package/src/config.ts +4 -1
- package/src/durable-projection.ts +106 -0
- package/src/durable-recorder.ts +493 -0
- package/src/durable.ts +195 -0
- package/src/public-api.ts +6 -12
- package/src/runtime-api.ts +8 -0
- package/src/runtime-core.ts +2 -1
- package/src/runtime-defaults.ts +1 -0
- package/src/runtime.ts +2 -1
- package/src/sanitizer.ts +16 -4
- package/dist/chunks/chunk-DBQAWNKG.ts +0 -316
- package/dist/chunks/chunk-DBQAWNKG.ts.map +0 -7
- package/dist/chunks/runtime-6ZHLVC3A.ts.map +0 -7
- package/dist/library-chunks/chunk-SAJ76IYU.js +0 -316
- package/dist/library-chunks/chunk-SAJ76IYU.js.map +0 -7
- package/dist/library-chunks/runtime-A6SQ6VEH.js.map +0 -7
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- the generated entry uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
|
|
4
|
+
// src/runtime-core.ts
|
|
5
|
+
var RUNTIME_INTERNAL_KEY = /* @__PURE__ */ Symbol.for("@narumitw/pi-langfuse/runtime-internal/v2");
|
|
6
|
+
var ManagedLangfuseRuntime = class {
|
|
7
|
+
constructor(backend) {
|
|
8
|
+
this.backend = backend;
|
|
9
|
+
}
|
|
10
|
+
backend;
|
|
11
|
+
state = "open";
|
|
12
|
+
sessionDisposers = /* @__PURE__ */ new Set();
|
|
13
|
+
flushQueue = Promise.resolve();
|
|
14
|
+
shutdownPromise;
|
|
15
|
+
get closed() {
|
|
16
|
+
return this.state !== "open";
|
|
17
|
+
}
|
|
18
|
+
registerSession(disposer) {
|
|
19
|
+
if (this.state !== "open") throw new Error("Langfuse runtime is shutting down or already closed.");
|
|
20
|
+
this.sessionDisposers.add(disposer);
|
|
21
|
+
return () => this.sessionDisposers.delete(disposer);
|
|
22
|
+
}
|
|
23
|
+
flush() {
|
|
24
|
+
if (this.state !== "open") {
|
|
25
|
+
return Promise.reject(new Error("Langfuse runtime is shutting down or already closed."));
|
|
26
|
+
}
|
|
27
|
+
const operation = this.flushQueue.then(() => this.backend.forceFlush());
|
|
28
|
+
this.flushQueue = operation.then(
|
|
29
|
+
() => void 0,
|
|
30
|
+
() => void 0
|
|
31
|
+
);
|
|
32
|
+
return operation;
|
|
33
|
+
}
|
|
34
|
+
shutdown() {
|
|
35
|
+
if (this.shutdownPromise) return this.shutdownPromise;
|
|
36
|
+
this.state = "closing";
|
|
37
|
+
this.shutdownPromise = Promise.resolve().then(() => this.shutdownNow());
|
|
38
|
+
return this.shutdownPromise;
|
|
39
|
+
}
|
|
40
|
+
async shutdownNow() {
|
|
41
|
+
const errors = [];
|
|
42
|
+
for (const dispose of [...this.sessionDisposers]) {
|
|
43
|
+
try {
|
|
44
|
+
await dispose("Langfuse runtime shut down before the Pi session was disposed.");
|
|
45
|
+
} catch (error) {
|
|
46
|
+
errors.push(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.sessionDisposers.clear();
|
|
50
|
+
await this.flushQueue;
|
|
51
|
+
try {
|
|
52
|
+
await this.backend.forceFlush();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
errors.push(error);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
await this.backend.shutdown();
|
|
58
|
+
} catch (error) {
|
|
59
|
+
errors.push(error);
|
|
60
|
+
} finally {
|
|
61
|
+
this.state = "closed";
|
|
62
|
+
}
|
|
63
|
+
if (errors.length === 1) throw errors[0];
|
|
64
|
+
if (errors.length > 1) throw new AggregateError(errors, "Langfuse runtime shutdown failed.");
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
function getLangfuseRuntimeInternal(runtime) {
|
|
68
|
+
const internal = runtime[RUNTIME_INTERNAL_KEY];
|
|
69
|
+
if (!internal) throw new Error("Langfuse runtime was not created by @narumitw/pi-langfuse.");
|
|
70
|
+
return internal;
|
|
71
|
+
}
|
|
72
|
+
function createLangfuseRuntimeFromBackend(backend) {
|
|
73
|
+
return registerLangfuseRuntime(new ManagedLangfuseRuntime(backend));
|
|
74
|
+
}
|
|
75
|
+
function registerLangfuseRuntime(runtime) {
|
|
76
|
+
Object.defineProperty(runtime, RUNTIME_INTERNAL_KEY, { value: runtime });
|
|
77
|
+
return runtime;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export {
|
|
81
|
+
getLangfuseRuntimeInternal,
|
|
82
|
+
createLangfuseRuntimeFromBackend
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=chunk-B64FXCNE.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/runtime-core.ts"],
|
|
4
|
+
"sourcesContent": ["import type { TraceBackend } from \"./tracing.js\";\n\nexport interface LangfuseRuntimeConfig {\n publicKey?: string;\n secretKey?: string;\n baseUrl?: string;\n environment?: string;\n release?: string;\n}\n\nexport interface CreateLangfuseRuntimeOptions {\n config?: LangfuseRuntimeConfig;\n env?: NodeJS.ProcessEnv | false;\n}\n\nexport interface LangfuseRuntime {\n readonly closed: boolean;\n flush(): Promise<void>;\n shutdown(): Promise<void>;\n}\n\ntype RuntimeSessionDisposer = (statusMessage: string) => void | Promise<void>;\n\nexport interface LangfuseRuntimeInternal extends LangfuseRuntime {\n readonly backend: TraceBackend;\n registerSession(disposer: RuntimeSessionDisposer): () => void;\n}\n\nconst RUNTIME_INTERNAL_KEY = Symbol.for(\"@narumitw/pi-langfuse/runtime-internal/v2\");\n\nclass ManagedLangfuseRuntime implements LangfuseRuntimeInternal {\n private state: \"open\" | \"closing\" | \"closed\" = \"open\";\n private readonly sessionDisposers = new Set<RuntimeSessionDisposer>();\n private flushQueue = Promise.resolve();\n private shutdownPromise: Promise<void> | undefined;\n\n constructor(readonly backend: TraceBackend) {}\n\n get closed(): boolean {\n return this.state !== \"open\";\n }\n\n registerSession(disposer: RuntimeSessionDisposer): () => void {\n if (this.state !== \"open\") throw new Error(\"Langfuse runtime is shutting down or already closed.\");\n this.sessionDisposers.add(disposer);\n return () => this.sessionDisposers.delete(disposer);\n }\n\n flush(): Promise<void> {\n if (this.state !== \"open\") {\n return Promise.reject(new Error(\"Langfuse runtime is shutting down or already closed.\"));\n }\n const operation = this.flushQueue.then(() => this.backend.forceFlush());\n this.flushQueue = operation.then(\n () => undefined,\n () => undefined,\n );\n return operation;\n }\n\n shutdown(): Promise<void> {\n if (this.shutdownPromise) return this.shutdownPromise;\n this.state = \"closing\";\n // Publish the promise before invoking controller callbacks, which can reenter shutdown().\n this.shutdownPromise = Promise.resolve().then(() => this.shutdownNow());\n return this.shutdownPromise;\n }\n\n private async shutdownNow(): Promise<void> {\n const errors: unknown[] = [];\n for (const dispose of [...this.sessionDisposers]) {\n try {\n await dispose(\"Langfuse runtime shut down before the Pi session was disposed.\");\n } catch (error) {\n errors.push(error);\n }\n }\n this.sessionDisposers.clear();\n\n await this.flushQueue;\n try {\n await this.backend.forceFlush();\n } catch (error) {\n errors.push(error);\n }\n try {\n await this.backend.shutdown();\n } catch (error) {\n errors.push(error);\n } finally {\n this.state = \"closed\";\n }\n\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) throw new AggregateError(errors, \"Langfuse runtime shutdown failed.\");\n }\n}\n\nexport function getLangfuseRuntimeInternal(runtime: LangfuseRuntime): LangfuseRuntimeInternal {\n const internal = (runtime as LangfuseRuntime & { [key: symbol]: LangfuseRuntimeInternal | undefined })[\n RUNTIME_INTERNAL_KEY\n ];\n if (!internal) throw new Error(\"Langfuse runtime was not created by @narumitw/pi-langfuse.\");\n return internal;\n}\n\nexport function createLangfuseRuntimeFromBackend(backend: TraceBackend): LangfuseRuntime {\n return registerLangfuseRuntime(new ManagedLangfuseRuntime(backend));\n}\n\nexport function registerLangfuseRuntime(runtime: LangfuseRuntimeInternal): LangfuseRuntimeInternal {\n Object.defineProperty(runtime, RUNTIME_INTERNAL_KEY, { value: runtime });\n return runtime;\n}\n"],
|
|
5
|
+
"mappings": ";;;;AA4BA,IAAM,uBAAuB,uBAAO,IAAI,2CAA2C;AAEnF,IAAM,yBAAN,MAAgE;AAAA,EAM9D,YAAqB,SAAuB;AAAvB;AAAA,EAAwB;AAAA,EAAxB;AAAA,EALb,QAAuC;AAAA,EAC9B,mBAAmB,oBAAI,IAA4B;AAAA,EAC5D,aAAa,QAAQ,QAAQ;AAAA,EAC7B;AAAA,EAIR,IAAI,SAAkB;AACpB,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA,EAEA,gBAAgB,UAA8C;AAC5D,QAAI,KAAK,UAAU,OAAQ,OAAM,IAAI,MAAM,sDAAsD;AACjG,SAAK,iBAAiB,IAAI,QAAQ;AAClC,WAAO,MAAM,KAAK,iBAAiB,OAAO,QAAQ;AAAA,EACpD;AAAA,EAEA,QAAuB;AACrB,QAAI,KAAK,UAAU,QAAQ;AACzB,aAAO,QAAQ,OAAO,IAAI,MAAM,sDAAsD,CAAC;AAAA,IACzF;AACA,UAAM,YAAY,KAAK,WAAW,KAAK,MAAM,KAAK,QAAQ,WAAW,CAAC;AACtE,SAAK,aAAa,UAAU;AAAA,MAC1B,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AAAA,EAEA,WAA0B;AACxB,QAAI,KAAK,gBAAiB,QAAO,KAAK;AACtC,SAAK,QAAQ;AAEb,SAAK,kBAAkB,QAAQ,QAAQ,EAAE,KAAK,MAAM,KAAK,YAAY,CAAC;AACtE,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,cAA6B;AACzC,UAAM,SAAoB,CAAC;AAC3B,eAAW,WAAW,CAAC,GAAG,KAAK,gBAAgB,GAAG;AAChD,UAAI;AACF,cAAM,QAAQ,gEAAgE;AAAA,MAChF,SAAS,OAAO;AACd,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AACA,SAAK,iBAAiB,MAAM;AAE5B,UAAM,KAAK;AACX,QAAI;AACF,YAAM,KAAK,QAAQ,WAAW;AAAA,IAChC,SAAS,OAAO;AACd,aAAO,KAAK,KAAK;AAAA,IACnB;AACA,QAAI;AACF,YAAM,KAAK,QAAQ,SAAS;AAAA,IAC9B,SAAS,OAAO;AACd,aAAO,KAAK,KAAK;AAAA,IACnB,UAAE;AACA,WAAK,QAAQ;AAAA,IACf;AAEA,QAAI,OAAO,WAAW,EAAG,OAAM,OAAO,CAAC;AACvC,QAAI,OAAO,SAAS,EAAG,OAAM,IAAI,eAAe,QAAQ,mCAAmC;AAAA,EAC7F;AACF;AAEO,SAAS,2BAA2B,SAAmD;AAC5F,QAAM,WAAY,QAChB,oBACF;AACA,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,4DAA4D;AAC3F,SAAO;AACT;AAEO,SAAS,iCAAiC,SAAwC;AACvF,SAAO,wBAAwB,IAAI,uBAAuB,OAAO,CAAC;AACpE;AAEO,SAAS,wBAAwB,SAA2D;AACjG,SAAO,eAAe,SAAS,sBAAsB,EAAE,OAAO,QAAQ,CAAC;AACvE,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- the generated entry uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
|
|
4
|
+
// src/runtime-defaults.ts
|
|
5
|
+
var DEFAULT_BASE_URL = "https://us.cloud.langfuse.com";
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
DEFAULT_BASE_URL
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=chunk-ETFOPFEP.js.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- the generated entry uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
|
|
4
|
+
// src/runtime-api.ts
|
|
5
|
+
async function createLangfuseRuntime(options = {}) {
|
|
6
|
+
const runtime = await import("./runtime-6KYOZVOE.js");
|
|
7
|
+
return runtime.createLangfuseRuntime(options);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/sanitizer.ts
|
|
11
|
+
var MAX_CAPTURE_BYTES = 64 * 1024;
|
|
12
|
+
var MAX_STRING_LENGTH = 5e4;
|
|
13
|
+
var MAX_COLLECTION_LENGTH = 200;
|
|
14
|
+
var MAX_DEPTH = 12;
|
|
15
|
+
var TRUNCATED = "[truncated: content budget exceeded]";
|
|
16
|
+
var BASE64_DATA_URI = /data:[^,\s"'`]*;base64,[^\s"'`]*/gi;
|
|
17
|
+
var BASE64_DATA_URI_OMITTED = "[base64 data URI omitted]";
|
|
18
|
+
var OPAQUE_SIGNATURE_KEYS = /* @__PURE__ */ new Set(["thinkingSignature", "textSignature", "thoughtSignature"]);
|
|
19
|
+
function sanitizeTraceValue(value, redactCredentialFields = false) {
|
|
20
|
+
const budget = { remaining: MAX_CAPTURE_BYTES, redactCredentialFields };
|
|
21
|
+
const sanitized = sanitize(value, /* @__PURE__ */ new WeakSet(), 0, budget);
|
|
22
|
+
return serializedBytes(sanitized) <= MAX_CAPTURE_BYTES ? sanitized : TRUNCATED;
|
|
23
|
+
}
|
|
24
|
+
function sanitize(value, active, depth, budget) {
|
|
25
|
+
if (budget.remaining <= byteLength(TRUNCATED)) return TRUNCATED;
|
|
26
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
27
|
+
return consume(value, budget);
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === "string") {
|
|
30
|
+
return consume(sanitizeString(value, Math.min(MAX_STRING_LENGTH, budget.remaining)), budget);
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === "bigint") return consume(value.toString(), budget);
|
|
33
|
+
if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
if (value instanceof Date) {
|
|
37
|
+
return consume(Number.isNaN(value.getTime()) ? "[invalid date]" : value.toISOString(), budget);
|
|
38
|
+
}
|
|
39
|
+
if (value instanceof Error) {
|
|
40
|
+
return sanitize({ name: value.name, message: value.message }, active, depth, budget);
|
|
41
|
+
}
|
|
42
|
+
if (depth >= MAX_DEPTH) return consume("[maximum depth reached]", budget);
|
|
43
|
+
if (active.has(value)) return consume("[circular]", budget);
|
|
44
|
+
active.add(value);
|
|
45
|
+
let result;
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
const items = [];
|
|
48
|
+
for (const item of value.slice(0, MAX_COLLECTION_LENGTH)) {
|
|
49
|
+
if (budget.remaining <= byteLength(TRUNCATED)) break;
|
|
50
|
+
items.push(sanitize(item, active, depth + 1, budget));
|
|
51
|
+
}
|
|
52
|
+
if (items.length < value.length) items.push(`[${value.length - items.length} items omitted]`);
|
|
53
|
+
result = items;
|
|
54
|
+
} else {
|
|
55
|
+
const record = value;
|
|
56
|
+
const output = {};
|
|
57
|
+
const objectType = readProperty(record, "type").value;
|
|
58
|
+
const redactData = objectType === "image" || objectType === "base64";
|
|
59
|
+
let visited = 0;
|
|
60
|
+
let processed = 0;
|
|
61
|
+
let omitted = false;
|
|
62
|
+
try {
|
|
63
|
+
for (const key in record) {
|
|
64
|
+
if (visited >= MAX_COLLECTION_LENGTH || budget.remaining <= byteLength(TRUNCATED)) {
|
|
65
|
+
omitted = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
visited += 1;
|
|
69
|
+
if (!Object.hasOwn(record, key) || OPAQUE_SIGNATURE_KEYS.has(key)) continue;
|
|
70
|
+
const keyBudget = Math.min(MAX_STRING_LENGTH, Math.max(0, budget.remaining - 4));
|
|
71
|
+
if (key.length > keyBudget) {
|
|
72
|
+
omitted = true;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
const keyBytes = byteLength(key);
|
|
76
|
+
if (keyBytes > keyBudget) {
|
|
77
|
+
omitted = true;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
const sanitizedKey = sanitizeString(key, keyBudget);
|
|
81
|
+
budget.remaining -= byteLength(sanitizedKey) + 4;
|
|
82
|
+
const property = readProperty(record, key);
|
|
83
|
+
if (budget.redactCredentialFields && /^(?:(?:x[-_])?api[-_]?(?:key|token|secret)|authorization|proxy[-_]?authorization|cookies?|password|credentials?|secret|token|bearer|client[-_]?secret|private[-_]?key|secret[-_]?key|public[-_]?key|access[-_]?token|refresh[-_]?token)$/iu.test(
|
|
84
|
+
key
|
|
85
|
+
)) {
|
|
86
|
+
output[sanitizedKey] = consume("[credential field omitted]", budget);
|
|
87
|
+
} else if (redactData && key === "data") {
|
|
88
|
+
output[sanitizedKey] = consume("[base64 omitted]", budget);
|
|
89
|
+
} else if (!property.ok) output[sanitizedKey] = consume("[unreadable property]", budget);
|
|
90
|
+
else output[sanitizedKey] = sanitize(property.value, active, depth + 1, budget);
|
|
91
|
+
processed += 1;
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
if (processed === 0) {
|
|
95
|
+
active.delete(value);
|
|
96
|
+
return consume("[unreadable object]", budget);
|
|
97
|
+
}
|
|
98
|
+
omitted = true;
|
|
99
|
+
}
|
|
100
|
+
if (omitted) output.$truncated = "additional object entries omitted";
|
|
101
|
+
result = output;
|
|
102
|
+
}
|
|
103
|
+
active.delete(value);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
function readProperty(record, key) {
|
|
107
|
+
try {
|
|
108
|
+
return { ok: true, value: record[key] };
|
|
109
|
+
} catch {
|
|
110
|
+
return { ok: false };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function consume(value, budget) {
|
|
114
|
+
const size = serializedBytes(value);
|
|
115
|
+
if (size > budget.remaining) {
|
|
116
|
+
budget.remaining -= byteLength(TRUNCATED);
|
|
117
|
+
return TRUNCATED;
|
|
118
|
+
}
|
|
119
|
+
budget.remaining -= size;
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
function sanitizeString(value, maxBytes) {
|
|
123
|
+
return truncateString(value, maxBytes).replace(BASE64_DATA_URI, BASE64_DATA_URI_OMITTED);
|
|
124
|
+
}
|
|
125
|
+
function truncateString(value, maxBytes) {
|
|
126
|
+
const boundedPrefix = value.slice(0, maxBytes);
|
|
127
|
+
if (boundedPrefix.length === value.length && byteLength(boundedPrefix) <= maxBytes) {
|
|
128
|
+
return boundedPrefix;
|
|
129
|
+
}
|
|
130
|
+
const suffix = "\u2026 [truncated]";
|
|
131
|
+
const target = Math.max(0, maxBytes - byteLength(suffix) - 2);
|
|
132
|
+
let bytes = 0;
|
|
133
|
+
let output = "";
|
|
134
|
+
for (const character of boundedPrefix) {
|
|
135
|
+
const size = byteLength(character);
|
|
136
|
+
if (bytes + size > target) break;
|
|
137
|
+
output += character;
|
|
138
|
+
bytes += size;
|
|
139
|
+
}
|
|
140
|
+
return `${output}${suffix}`;
|
|
141
|
+
}
|
|
142
|
+
function serializedBytes(value) {
|
|
143
|
+
return byteLength(JSON.stringify(value) ?? "null");
|
|
144
|
+
}
|
|
145
|
+
function byteLength(value) {
|
|
146
|
+
return Buffer.byteLength(value, "utf8");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
sanitizeTraceValue,
|
|
151
|
+
createLangfuseRuntime
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=chunk-JPFN6GGS.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/runtime-api.ts", "../../src/sanitizer.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CreateLangfuseRuntimeOptions, LangfuseRuntime } from \"./runtime-core.js\";\n\nexport type { CreateLangfuseRuntimeOptions, LangfuseRuntime, LangfuseRuntimeConfig } from \"./runtime-core.js\";\n\nexport async function createLangfuseRuntime(options: CreateLangfuseRuntimeOptions = {}): Promise<LangfuseRuntime> {\n const runtime = await import(\"./runtime.js\");\n return runtime.createLangfuseRuntime(options);\n}\n", "export const MAX_CAPTURE_BYTES = 64 * 1024;\nconst MAX_STRING_LENGTH = 50_000;\nconst MAX_COLLECTION_LENGTH = 200;\nconst MAX_DEPTH = 12;\nconst TRUNCATED = \"[truncated: content budget exceeded]\";\nconst BASE64_DATA_URI = /data:[^,\\s\"'`]*;base64,[^\\s\"'`]*/gi;\nconst BASE64_DATA_URI_OMITTED = \"[base64 data URI omitted]\";\nconst OPAQUE_SIGNATURE_KEYS = new Set([\"thinkingSignature\", \"textSignature\", \"thoughtSignature\"]);\n\nexport function sanitizeTraceValue(value: unknown, redactCredentialFields = false): unknown {\n const budget = { remaining: MAX_CAPTURE_BYTES, redactCredentialFields };\n const sanitized = sanitize(value, new WeakSet<object>(), 0, budget);\n return serializedBytes(sanitized) <= MAX_CAPTURE_BYTES ? sanitized : TRUNCATED;\n}\n\nfunction sanitize(\n value: unknown,\n active: WeakSet<object>,\n depth: number,\n budget: { remaining: number; redactCredentialFields?: boolean },\n): unknown {\n if (budget.remaining <= byteLength(TRUNCATED)) return TRUNCATED;\n if (value === null || typeof value === \"number\" || typeof value === \"boolean\") {\n return consume(value, budget);\n }\n if (typeof value === \"string\") {\n return consume(sanitizeString(value, Math.min(MAX_STRING_LENGTH, budget.remaining)), budget);\n }\n if (typeof value === \"bigint\") return consume(value.toString(), budget);\n if (typeof value === \"undefined\" || typeof value === \"function\" || typeof value === \"symbol\") {\n return undefined;\n }\n if (value instanceof Date) {\n return consume(Number.isNaN(value.getTime()) ? \"[invalid date]\" : value.toISOString(), budget);\n }\n if (value instanceof Error) {\n return sanitize({ name: value.name, message: value.message }, active, depth, budget);\n }\n if (depth >= MAX_DEPTH) return consume(\"[maximum depth reached]\", budget);\n if (active.has(value)) return consume(\"[circular]\", budget);\n active.add(value);\n\n let result: unknown;\n if (Array.isArray(value)) {\n const items: unknown[] = [];\n for (const item of value.slice(0, MAX_COLLECTION_LENGTH)) {\n if (budget.remaining <= byteLength(TRUNCATED)) break;\n items.push(sanitize(item, active, depth + 1, budget));\n }\n if (items.length < value.length) items.push(`[${value.length - items.length} items omitted]`);\n result = items;\n } else {\n const record = value as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n const objectType = readProperty(record, \"type\").value;\n const redactData = objectType === \"image\" || objectType === \"base64\";\n let visited = 0;\n let processed = 0;\n let omitted = false;\n try {\n for (const key in record) {\n if (visited >= MAX_COLLECTION_LENGTH || budget.remaining <= byteLength(TRUNCATED)) {\n omitted = true;\n break;\n }\n visited += 1;\n if (!Object.hasOwn(record, key) || OPAQUE_SIGNATURE_KEYS.has(key)) continue;\n const keyBudget = Math.min(MAX_STRING_LENGTH, Math.max(0, budget.remaining - 4));\n if (key.length > keyBudget) {\n omitted = true;\n break;\n }\n const keyBytes = byteLength(key);\n if (keyBytes > keyBudget) {\n omitted = true;\n break;\n }\n const sanitizedKey = sanitizeString(key, keyBudget);\n budget.remaining -= byteLength(sanitizedKey) + 4;\n const property = readProperty(record, key);\n if (\n budget.redactCredentialFields &&\n /^(?:(?:x[-_])?api[-_]?(?:key|token|secret)|authorization|proxy[-_]?authorization|cookies?|password|credentials?|secret|token|bearer|client[-_]?secret|private[-_]?key|secret[-_]?key|public[-_]?key|access[-_]?token|refresh[-_]?token)$/iu.test(\n key,\n )\n ) {\n output[sanitizedKey] = consume(\"[credential field omitted]\", budget);\n } else if (redactData && key === \"data\") {\n output[sanitizedKey] = consume(\"[base64 omitted]\", budget);\n } else if (!property.ok) output[sanitizedKey] = consume(\"[unreadable property]\", budget);\n else output[sanitizedKey] = sanitize(property.value, active, depth + 1, budget);\n processed += 1;\n }\n } catch {\n if (processed === 0) {\n active.delete(value);\n return consume(\"[unreadable object]\", budget);\n }\n omitted = true;\n }\n if (omitted) output.$truncated = \"additional object entries omitted\";\n result = output;\n }\n active.delete(value);\n return result;\n}\n\nfunction readProperty(\n record: Record<string, unknown>,\n key: string,\n): { ok: true; value: unknown } | { ok: false; value?: undefined } {\n try {\n return { ok: true, value: record[key] };\n } catch {\n return { ok: false };\n }\n}\n\nfunction consume<T>(value: T, budget: { remaining: number }): T | string {\n const size = serializedBytes(value);\n if (size > budget.remaining) {\n budget.remaining -= byteLength(TRUNCATED);\n return TRUNCATED;\n }\n budget.remaining -= size;\n return value;\n}\n\nfunction sanitizeString(value: string, maxBytes: number): string {\n return truncateString(value, maxBytes).replace(BASE64_DATA_URI, BASE64_DATA_URI_OMITTED);\n}\n\nfunction truncateString(value: string, maxBytes: number): string {\n const boundedPrefix = value.slice(0, maxBytes);\n if (boundedPrefix.length === value.length && byteLength(boundedPrefix) <= maxBytes) {\n return boundedPrefix;\n }\n const suffix = \"\u2026 [truncated]\";\n const target = Math.max(0, maxBytes - byteLength(suffix) - 2);\n let bytes = 0;\n let output = \"\";\n for (const character of boundedPrefix) {\n const size = byteLength(character);\n if (bytes + size > target) break;\n output += character;\n bytes += size;\n }\n return `${output}${suffix}`;\n}\n\nfunction serializedBytes(value: unknown): number {\n return byteLength(JSON.stringify(value) ?? \"null\");\n}\n\nfunction byteLength(value: string): number {\n return Buffer.byteLength(value, \"utf8\");\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
2
|
// @ts-nocheck -- the generated entry uses a .ts extension for Pi's Jiti loader.
|
|
3
3
|
import {
|
|
4
|
-
DEFAULT_BASE_URL
|
|
4
|
+
DEFAULT_BASE_URL
|
|
5
|
+
} from "./chunk-ETFOPFEP.js";
|
|
6
|
+
import {
|
|
5
7
|
createLangfuseRuntimeFromBackend,
|
|
6
8
|
getLangfuseRuntimeInternal
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-B64FXCNE.js";
|
|
8
10
|
|
|
9
11
|
// src/runtime.ts
|
|
10
12
|
import { createHash } from "node:crypto";
|
|
@@ -349,4 +351,4 @@ export {
|
|
|
349
351
|
maskSecrets,
|
|
350
352
|
resolveLangfuseRuntimeConfig
|
|
351
353
|
};
|
|
352
|
-
//# sourceMappingURL=runtime-
|
|
354
|
+
//# sourceMappingURL=runtime-6KYOZVOE.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/runtime.ts"],
|
|
4
|
+
"sourcesContent": ["import { createHash } from \"node:crypto\";\nimport { LangfuseSpanProcessor } from \"@langfuse/otel\";\nimport {\n type LangfuseObservation,\n type LangfuseObservationAttributes,\n LangfuseOtelSpanAttributes,\n setLangfuseTracerProvider,\n startObservation,\n} from \"@langfuse/tracing\";\nimport type { SpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { LangfuseConfig } from \"./config.js\";\nimport {\n type CreateLangfuseRuntimeOptions,\n createLangfuseRuntimeFromBackend,\n getLangfuseRuntimeInternal,\n type LangfuseRuntime,\n type LangfuseRuntimeInternal,\n} from \"./runtime-core.js\";\nimport { DEFAULT_BASE_URL } from \"./runtime-defaults.js\";\nimport type { Observation, ObservationAttributes, ObservationType, TraceBackend } from \"./tracing.js\";\n\nexport type {\n CreateLangfuseRuntimeOptions,\n LangfuseRuntime,\n LangfuseRuntimeConfig,\n} from \"./runtime-core.js\";\n\nconst RUNTIME_KEY = Symbol.for(\"@narumitw/pi-langfuse/runtime/v2\");\nconst LEGACY_RUNTIME_KEY = Symbol.for(\"@narumitw/pi-langfuse/runtime/v1\");\n\ninterface ResolvedRuntimeConfig {\n publicKey: string;\n secretKey: string;\n baseUrl: string;\n environment?: string;\n release?: string;\n}\n\ninterface SharedRuntime {\n fingerprint: string;\n runtime: LangfuseRuntimeInternal;\n shutdown: true;\n}\n\ntype GlobalWithRuntime = typeof globalThis & {\n [RUNTIME_KEY]?: Promise<SharedRuntime>;\n [LEGACY_RUNTIME_KEY]?: unknown;\n};\n\nexport interface RuntimeFactories {\n createProcessor(config: ResolvedRuntimeConfig): SpanProcessor;\n createProvider(processor: SpanProcessor): NodeTracerProvider;\n selectProvider(provider: NodeTracerProvider): void;\n}\n\nclass ProductionObservation implements Observation {\n readonly traceId: string;\n\n constructor(\n readonly native: LangfuseObservation,\n private readonly secrets: readonly string[],\n ) {\n this.traceId = native.traceId;\n }\n\n update(attributes: ObservationAttributes): Observation {\n const { sessionId, userId, ...observationAttributes } = maskObservationAttributes(attributes, this.secrets);\n this.native.updateOtelSpanAttributes(observationAttributes as LangfuseObservationAttributes);\n applySessionId(this.native, sessionId);\n applyUserId(this.native, userId);\n return this;\n }\n\n updateTrace(attributes: ObservationAttributes): Observation {\n const { input, output, metadata, name, sessionId, userId, tags, version } = maskObservationAttributes(\n attributes,\n this.secrets,\n );\n if (input !== undefined || output !== undefined) {\n this.native.setTraceIO({ input, output });\n }\n if (name !== undefined) {\n this.native.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_NAME, name);\n }\n applySessionId(this.native, sessionId);\n applyUserId(this.native, userId);\n if (tags !== undefined) {\n this.native.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_TAGS, tags);\n }\n if (version !== undefined) {\n this.native.otelSpan.setAttribute(LangfuseOtelSpanAttributes.VERSION, version);\n }\n for (const [key, value] of Object.entries(metadata ?? {})) {\n const serialized = serializeMetadataValue(value);\n if (serialized !== undefined) {\n this.native.otelSpan.setAttribute(`${LangfuseOtelSpanAttributes.TRACE_METADATA}.${key}`, serialized);\n }\n }\n return this;\n }\n\n end(endTime?: number): Observation {\n this.native.end(endTime);\n return this;\n }\n}\n\nclass ProductionTraceBackend implements TraceBackend {\n constructor(\n private readonly provider: NodeTracerProvider,\n private readonly processor: SpanProcessor,\n private readonly secrets: readonly string[],\n ) {}\n\n start(\n name: string,\n attributes: ObservationAttributes,\n options: { asType: ObservationType; parent?: Observation; startTime?: Date },\n ): Observation {\n const maskedName = maskSecretString(name, this.secrets);\n const { sessionId, userId, ...observationAttributes } = maskObservationAttributes(attributes, this.secrets);\n const parent = options.parent;\n const native =\n parent instanceof ProductionObservation\n ? startChild(parent.native, maskedName, observationAttributes, options.asType, options.startTime)\n : startRoot(maskedName, observationAttributes, options.asType, options.startTime);\n applySessionId(native, sessionId);\n applyUserId(native, userId);\n return new ProductionObservation(native, this.secrets);\n }\n\n async forceFlush(): Promise<void> {\n await this.processor.forceFlush();\n }\n\n async shutdown(): Promise<void> {\n await this.provider.shutdown();\n }\n}\n\nexport async function createLangfuseRuntime(options: CreateLangfuseRuntimeOptions = {}): Promise<LangfuseRuntime> {\n return createSharedRuntime(resolveLangfuseRuntimeConfig(options), defaultFactories);\n}\n\nexport async function createProductionBackend(\n config: LangfuseConfig,\n factoryOverrides: Partial<RuntimeFactories> = {},\n): Promise<TraceBackend> {\n const runtime = await createSharedRuntime(resolveLangfuseRuntimeConfig({ config, env: false }), {\n ...defaultFactories,\n ...factoryOverrides,\n });\n return getLangfuseRuntimeInternal(runtime).backend;\n}\n\nasync function createSharedRuntime(\n config: ResolvedRuntimeConfig,\n factories: RuntimeFactories,\n): Promise<LangfuseRuntime> {\n const globalRuntime = globalThis as GlobalWithRuntime;\n const fingerprint = configFingerprint(config);\n const existing = globalRuntime[RUNTIME_KEY];\n const legacyRuntime = globalRuntime[LEGACY_RUNTIME_KEY];\n if (legacyRuntime !== undefined && legacyRuntime !== existing) {\n throw new Error(\"An older Langfuse runtime is already loaded; restart the process before enabling this version.\");\n }\n if (existing) {\n if (legacyRuntime === undefined) globalRuntime[LEGACY_RUNTIME_KEY] = existing;\n const shared = await existing;\n if (shared.runtime.closed) {\n throw new Error(\"Langfuse tracing was already shut down; restart the process to enable it again.\");\n }\n if (shared.fingerprint !== fingerprint) {\n throw new Error(\"Langfuse configuration changed; restart the process to apply the new credentials.\");\n }\n return shared.runtime;\n }\n\n const initializing = initializeRuntime(config, fingerprint, factories);\n globalRuntime[RUNTIME_KEY] = initializing;\n globalRuntime[LEGACY_RUNTIME_KEY] = initializing;\n try {\n return (await initializing).runtime;\n } catch (error) {\n if (globalRuntime[RUNTIME_KEY] === initializing) delete globalRuntime[RUNTIME_KEY];\n if (globalRuntime[LEGACY_RUNTIME_KEY] === initializing) delete globalRuntime[LEGACY_RUNTIME_KEY];\n throw error;\n }\n}\n\nasync function initializeRuntime(\n config: ResolvedRuntimeConfig,\n fingerprint: string,\n factories: RuntimeFactories,\n): Promise<SharedRuntime> {\n const processor = factories.createProcessor(config);\n let provider: NodeTracerProvider | undefined;\n try {\n provider = factories.createProvider(processor);\n factories.selectProvider(provider);\n } catch (error) {\n await (provider?.shutdown() ?? processor.shutdown()).catch(() => undefined);\n throw error;\n }\n const runtime = getLangfuseRuntimeInternal(\n createLangfuseRuntimeFromBackend(\n new ProductionTraceBackend(provider, processor, [config.publicKey, config.secretKey]),\n ),\n );\n return { fingerprint, runtime, shutdown: true };\n}\n\nconst defaultFactories: RuntimeFactories = {\n createProcessor: (config) => {\n const secrets = [config.secretKey, config.publicKey];\n return new LangfuseSpanProcessor({\n publicKey: config.publicKey,\n secretKey: config.secretKey,\n baseUrl: config.baseUrl,\n environment: config.environment ?? \"\",\n release: config.release ?? \"\",\n flushAt: 512,\n flushInterval: 5,\n timeout: 5,\n mask: ({ data }) => maskSecrets(data, secrets),\n shouldExportSpan: ({ otelSpan }) => typeof otelSpan.attributes[\"langfuse.observation.type\"] === \"string\",\n });\n },\n createProvider: (processor) => new NodeTracerProvider({ spanProcessors: [processor] }),\n selectProvider: setLangfuseTracerProvider,\n};\n\nexport function resolveLangfuseRuntimeConfig(options: CreateLangfuseRuntimeOptions): ResolvedRuntimeConfig {\n const env = options.env === false ? undefined : (options.env ?? process.env);\n const publicKey = requiredSetting(\"publicKey\", options.config?.publicKey, env?.LANGFUSE_PUBLIC_KEY);\n const secretKey = requiredSetting(\"secretKey\", options.config?.secretKey, env?.LANGFUSE_SECRET_KEY);\n const explicitBaseUrl = options.config?.baseUrl;\n if (explicitBaseUrl !== undefined && !normalizeString(explicitBaseUrl)) {\n throw new Error(\"Langfuse baseUrl must be a non-empty string.\");\n }\n const rawBaseUrl = selectedSetting(explicitBaseUrl, env?.LANGFUSE_BASE_URL) ?? DEFAULT_BASE_URL;\n const baseUrl = normalizeBaseUrl(rawBaseUrl);\n if (!baseUrl) {\n throw new Error(\"Langfuse baseUrl must use HTTP or HTTPS without credentials, a query, or a fragment.\");\n }\n const environment = optionalSetting(\"environment\", options.config?.environment, env?.LANGFUSE_TRACING_ENVIRONMENT);\n if (environment && (environment.length > 40 || !/^(?!langfuse)[a-z0-9_-]+$/u.test(environment))) {\n throw new Error(\n \"Langfuse environment must be at most 40 lowercase letters, numbers, hyphens, or underscores and must not start with langfuse.\",\n );\n }\n const release = optionalSetting(\"release\", options.config?.release, env?.LANGFUSE_RELEASE);\n return { publicKey, secretKey, baseUrl, ...(environment ? { environment } : {}), ...(release ? { release } : {}) };\n}\n\nfunction requiredSetting(name: string, explicit: string | undefined, environment: string | undefined): string {\n const value = selectedSetting(explicit, environment);\n if (!value) throw new Error(`Langfuse ${name} is required.`);\n return value;\n}\n\nfunction optionalSetting(\n name: string,\n explicit: string | undefined,\n environment: string | undefined,\n): string | undefined {\n const selected = explicit !== undefined ? explicit : environment;\n if (selected === undefined) return undefined;\n const value = normalizeString(selected);\n if (!value) throw new Error(`Langfuse ${name} must be a non-empty string.`);\n return value;\n}\n\nfunction selectedSetting(explicit: string | undefined, environment: string | undefined): string | undefined {\n if (explicit !== undefined) return normalizeString(explicit);\n return normalizeString(environment);\n}\n\nfunction normalizeBaseUrl(value: string): string | undefined {\n try {\n const url = new URL(value);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n if (url.username || url.password || url.search || url.hash) return undefined;\n return url.toString().replace(/\\/+$/, \"\");\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction serializeMetadataValue(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\nfunction maskObservationAttributes(\n attributes: ObservationAttributes,\n secrets: readonly string[],\n): ObservationAttributes {\n return {\n ...attributes,\n ...(attributes.metadata ? { metadata: maskSecrets(attributes.metadata, secrets) as Record<string, unknown> } : {}),\n ...(attributes.name ? { name: maskSecretString(attributes.name, secrets) } : {}),\n ...(attributes.sessionId ? { sessionId: maskSecretString(attributes.sessionId, secrets) } : {}),\n ...(attributes.userId ? { userId: maskSecretString(attributes.userId, secrets) } : {}),\n ...(attributes.tags ? { tags: attributes.tags.map((tag) => maskSecretString(tag, secrets)) } : {}),\n ...(attributes.statusMessage ? { statusMessage: maskSecretString(attributes.statusMessage, secrets) } : {}),\n ...(attributes.model ? { model: maskSecretString(attributes.model, secrets) } : {}),\n ...(attributes.version ? { version: maskSecretString(attributes.version, secrets) } : {}),\n ...(attributes.modelParameters\n ? {\n modelParameters: Object.fromEntries(\n Object.entries(attributes.modelParameters).map(([key, value]) => [\n maskSecretString(key, secrets),\n typeof value === \"string\" ? maskSecretString(value, secrets) : value,\n ]),\n ),\n }\n : {}),\n };\n}\n\nfunction maskSecretString(value: string, secrets: readonly string[]): string {\n return maskSecrets(value, secrets) as string;\n}\n\nfunction applySessionId(observation: LangfuseObservation, sessionId: string | undefined): void {\n if (sessionId !== undefined) {\n observation.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_SESSION_ID, sessionId);\n }\n}\n\nfunction applyUserId(observation: LangfuseObservation, userId: string | undefined): void {\n if (userId !== undefined) {\n observation.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_USER_ID, userId);\n }\n}\n\nfunction startRoot(\n name: string,\n attributes: ObservationAttributes,\n type: ObservationType,\n startTime?: Date,\n): LangfuseObservation {\n if (type === \"agent\") {\n return startObservation(name, attributes as LangfuseObservationAttributes, { asType: \"agent\", startTime });\n }\n if (type === \"generation\") {\n return startObservation(name, attributes as LangfuseObservationAttributes, {\n asType: \"generation\",\n startTime,\n });\n }\n if (type === \"tool\") {\n return startObservation(name, attributes as LangfuseObservationAttributes, { asType: \"tool\", startTime });\n }\n return startObservation(name, attributes as LangfuseObservationAttributes, { asType: \"span\", startTime });\n}\n\nfunction startChild(\n parent: LangfuseObservation,\n name: string,\n attributes: ObservationAttributes,\n type: ObservationType,\n startTime?: Date,\n): LangfuseObservation {\n if (startTime) {\n const parentSpanContext = parent.otelSpan.spanContext();\n if (type === \"agent\") {\n return startObservation(name, attributes as LangfuseObservationAttributes, {\n asType: \"agent\",\n parentSpanContext,\n startTime,\n });\n }\n if (type === \"generation\") {\n return startObservation(name, attributes as LangfuseObservationAttributes, {\n asType: \"generation\",\n parentSpanContext,\n startTime,\n });\n }\n if (type === \"tool\") {\n return startObservation(name, attributes as LangfuseObservationAttributes, {\n asType: \"tool\",\n parentSpanContext,\n startTime,\n });\n }\n return startObservation(name, attributes as LangfuseObservationAttributes, {\n asType: \"span\",\n parentSpanContext,\n startTime,\n });\n }\n if (type === \"agent\") {\n return parent.startObservation(name, attributes as LangfuseObservationAttributes, { asType: \"agent\" });\n }\n if (type === \"generation\") {\n return parent.startObservation(name, attributes as LangfuseObservationAttributes, { asType: \"generation\" });\n }\n if (type === \"tool\") {\n return parent.startObservation(name, attributes as LangfuseObservationAttributes, { asType: \"tool\" });\n }\n return parent.startObservation(name, attributes as LangfuseObservationAttributes, { asType: \"span\" });\n}\n\nfunction configFingerprint(config: ResolvedRuntimeConfig): string {\n return createHash(\"sha256\")\n .update(\n `${config.publicKey}\\0${config.secretKey}\\0${config.baseUrl}\\0${config.environment ?? \"\"}\\0${config.release ?? \"\"}`,\n )\n .digest(\"hex\");\n}\n\nexport function maskSecrets(data: unknown, secrets: readonly string[]): unknown {\n return maskSecretValue(data, secrets, new WeakSet<object>());\n}\n\nfunction maskSecretValue(data: unknown, secrets: readonly string[], active: WeakSet<object>): unknown {\n if (typeof data === \"string\") {\n let masked = data.replace(/\\b(?:sk|pk)-lf-[A-Za-z0-9_-]+\\b/g, \"[LANGFUSE_KEY_REDACTED]\");\n for (const secret of secrets) {\n if (secret) masked = masked.replaceAll(secret, \"[LANGFUSE_KEY_REDACTED]\");\n }\n return masked;\n }\n if (!data || typeof data !== \"object\") return data;\n if (active.has(data)) return \"[circular]\";\n\n active.add(data);\n try {\n if (Array.isArray(data)) {\n return data.map((item) => maskSecretValue(item, secrets, active));\n }\n return Object.fromEntries(\n Object.entries(data).map(([key, value]) => [\n maskSecretString(key, secrets),\n maskSecretValue(value, secrets, active),\n ]),\n );\n } finally {\n active.delete(data);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/public-api.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
import type { CreateLangfuseRuntimeOptions, LangfuseRuntime } from "./runtime-core.js";
|
|
2
1
|
export { createPiLangfuseSession, type PiLangfuseSession, type PiLangfuseSessionOptions, } from "./pi-session.js";
|
|
3
|
-
export type
|
|
4
|
-
export declare function createLangfuseRuntime(options?: CreateLangfuseRuntimeOptions): Promise<LangfuseRuntime>;
|
|
2
|
+
export { type CreateLangfuseRuntimeOptions, createLangfuseRuntime, type LangfuseRuntime, type LangfuseRuntimeConfig, } from "./runtime-api.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CreateLangfuseRuntimeOptions, LangfuseRuntime } from "./runtime-core.js";
|
|
2
|
+
export type { CreateLangfuseRuntimeOptions, LangfuseRuntime, LangfuseRuntimeConfig } from "./runtime-core.js";
|
|
3
|
+
export declare function createLangfuseRuntime(options?: CreateLangfuseRuntimeOptions): Promise<LangfuseRuntime>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DEFAULT_BASE_URL = "https://us.cloud.langfuse.com";
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
2
2
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
3
|
-
import {
|
|
3
|
+
import type { LangfuseConfig } from "./config.js";
|
|
4
4
|
import { type CreateLangfuseRuntimeOptions, type LangfuseRuntime } from "./runtime-core.js";
|
|
5
5
|
import type { TraceBackend } from "./tracing.js";
|
|
6
6
|
export type { CreateLangfuseRuntimeOptions, LangfuseRuntime, LangfuseRuntimeConfig, } from "./runtime-core.js";
|
package/dist/sanitizer.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const MAX_CAPTURE_BYTES: number;
|
|
2
|
-
export declare function sanitizeTraceValue(value: unknown): unknown;
|
|
2
|
+
export declare function sanitizeTraceValue(value: unknown, redactCredentialFields?: boolean): unknown;
|
package/docs/durable.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Durable tracing
|
|
2
|
+
|
|
3
|
+
Import `createPiLangfuseDurableConversation` from `@narumitw/pi-langfuse/durable` to observe one existing conversation. This entry uses public durable APIs, the existing Langfuse backend, sanitizer and shared-runtime ownership; it does not create an `AgentSession`, load coding-agent registration, or install a durable extension.
|
|
4
|
+
|
|
5
|
+
> **Experimental compatibility:** this adapter targets exactly `@earendil-works/pi-durable@1.0.4`. Its upstream APIs can change without notice. Do not upgrade durable without the checks below.
|
|
6
|
+
|
|
7
|
+
## Compatibility
|
|
8
|
+
|
|
9
|
+
| Interface | Supported dependency | Node.js | Verification |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| Root default extension and `createPiLangfuseSession` | Existing coding-agent peer contract (`*`); regression suite uses 1.1.0 | Follow coding-agent's engine floor (currently >=22.19.0) | Existing extension, session, runtime, root-export and Jiti tests |
|
|
12
|
+
| `/durable` | pi-durable 1.0.4; Chord ^1.0.4; pi-ai compatible with durable's ^1.0.4 dependency | >=22.19.0 | Real Harness, synthetic provider, Memory/JSONL storage and packed consumer tests |
|
|
13
|
+
|
|
14
|
+
The coding-agent, durable and Chord peers are optional because each entry only needs its own runtime. Install durable and Chord explicitly for `/durable`. They are host peers rather than adapter-owned execution dependencies so the host's experimental Harness and Context APIs stay aligned. The adapter and host must resolve the same durable module instance: public `watchEvents(harness, ...)` is bound to that instance's Harness observation state. Separate npm roots or bundled runtime copies do not guarantee this identity; use one host install scope, and check `active`/`onError` when attaching. Durable behavior and packed-consumer checks pass on Node 22.19.0, 22.23.1 and 26.5.0.
|
|
15
|
+
The Langfuse SDK independently requires Node >=20, but that is not sufficient for these Pi runtimes. The current tested host combination is durable 1.0.4, Chord 1.1.0 and Pi AI 1.1.0. Pi AI's nominal transcript types must resolve consistently with the host's durable dependency resolution; do not pass a Models collection from an incompatible Pi AI release.
|
|
16
|
+
|
|
17
|
+
For an upstream upgrade, inspect the release README and public root exports/types, then review `watchEvents`, snapshot overflow, `Submission.status()`, read-only transactions, committed message projections and cancellation semantics. Update the exact durable peer and test dependency together. Run the focused durable and coding-agent suites, `npm run check`, `npm test`, `npm run package:pack -- langfuse`, packed consumer tests on supported Node versions, and the package-directory Pi loading smoke. Never replace removed public APIs with private imports or coding-agent event casts.
|
|
18
|
+
|
|
19
|
+
## Minimal host example
|
|
20
|
+
|
|
21
|
+
Install `@narumitw/pi-langfuse`, `@earendil-works/pi-durable@1.0.4`, `@earendil-works/chord@^1.0.4` and a compatible `@earendil-works/pi-ai`.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { BACKGROUND_CONTEXT, withCancel } from "@earendil-works/chord/context";
|
|
25
|
+
import {
|
|
26
|
+
createLangfuseRuntime,
|
|
27
|
+
createPiLangfuseDurableConversation,
|
|
28
|
+
} from "@narumitw/pi-langfuse/durable";
|
|
29
|
+
|
|
30
|
+
// The host already owns harness, conversation, provider credentials and authorization.
|
|
31
|
+
const runtime = await createLangfuseRuntime({
|
|
32
|
+
config: { environment: "production" },
|
|
33
|
+
// Uses standard Langfuse environment variables unless env: false is supplied.
|
|
34
|
+
});
|
|
35
|
+
const observation = withCancel(BACKGROUND_CONTEXT);
|
|
36
|
+
const tracing = await createPiLangfuseDurableConversation(runtime, {
|
|
37
|
+
harness,
|
|
38
|
+
conversationId: conversation.id,
|
|
39
|
+
context: observation.context,
|
|
40
|
+
sessionId: hostConversationId, // Globally meaningful host session/conversation identity.
|
|
41
|
+
userId: accountId,
|
|
42
|
+
applicationId: "support-service",
|
|
43
|
+
captureContent: false,
|
|
44
|
+
submissionCorrelation: (record) => ({ requestId: record.requestId }),
|
|
45
|
+
onError: (stage) => reportTracingStage(stage), // Fixed diagnostic stage, not secret-bearing error prose.
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Only the host submits, waits, resumes, aborts, retries or otherwise drives execution.
|
|
49
|
+
const submission = await conversation.submit({
|
|
50
|
+
type: "input", content: userPrompt, requestId: incomingRequestId,
|
|
51
|
+
}, executionContext);
|
|
52
|
+
const result = await submission.wait(executionContext);
|
|
53
|
+
// Optional: reconcile committed status and answer after a gap or late request attachment.
|
|
54
|
+
await tracing.observeSubmission(submission.id);
|
|
55
|
+
|
|
56
|
+
// Ends observation only. Neither call cancels or resumes durable work.
|
|
57
|
+
observation.cancel();
|
|
58
|
+
await tracing.dispose();
|
|
59
|
+
|
|
60
|
+
// Once all application conversations/controllers are finished:
|
|
61
|
+
await harness.close(BACKGROUND_CONTEXT); // Host-owned durable lifecycle.
|
|
62
|
+
await runtime.shutdown(); // Host-owned shared exporter lifecycle.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The root-export coding-agent example in the [README](../README.md#-host-application-embedding) remains unchanged. A process can share the same runtime across both adapters.
|
|
66
|
+
|
|
67
|
+
## API and ownership
|
|
68
|
+
|
|
69
|
+
`createPiLangfuseDurableConversation(runtime, options)` returns a controller with `active`, `closed`, `observeSubmission(id, correlation?)`, and idempotent `dispose()`. The host supplies an existing `Harness`, conversation ID and observation `Context`. Failed initialization returns an inactive controller and reports a fixed `onError` stage. Observation, projection, exporter and callback failures do not propagate into agent/tool execution or response delivery. Explicit `createLangfuseRuntime()`, `runtime.flush()` and `runtime.shutdown()` remain fallible administrative APIs; handle their errors separately from application responses.
|
|
70
|
+
|
|
71
|
+
For custom exporters and deterministic tests, `/durable` also exports `createLangfuseRuntimeFromBackend(backend)` and the `TraceBackend`, `Observation`, `ObservationAttributes` and `ObservationType` types. The managed runtime owns that backend's flush/shutdown lifecycle; controllers borrow it. A custom backend must preserve parent relationships and provide its own exporter-level credential masking; the adapter still applies its bounded content projections before invoking it.
|
|
72
|
+
|
|
73
|
+
One controller owns one conversation attachment. Duplicate simultaneous attachments through the same loaded adapter are rejected fail-open, without detaching the original controller. Use one loaded package instance and one controller per conversation. A new attachment after disposal or process restart starts a new observation trace, not a continuation of a persisted tracing checkpoint.
|
|
74
|
+
|
|
75
|
+
Disposal cancels its independently derived observation context, joins initialization and observer detachment, and closes open descendants as observation-interrupted warnings. It never flushes or shuts down a shared runtime. Runtime shutdown rejects new attachments, disposes its controllers before exporter shutdown, and drains completed spans. Harness closure or observation cancellation also detaches the controller and resolves `closed`. Neither disposal nor attachment changes paused recovery or execution cancellation.
|
|
76
|
+
|
|
77
|
+
The host owns storage opening/closing, model/provider setup, submission deduplication, execution contexts, authorization, tenant boundaries and recovery decisions. Tracing performs status reads and read-only `harness.commit(tx => tx.entry(id))` transactions; these append no entries and do not enable scheduling. It never calls `Submission.wait()`, `resume()`, execution abort, submit, retry, compact or finalize APIs. Host callbacks own any asynchronous work they start; callback rejections are contained, but the adapter cannot cancel arbitrary callback-owned work.
|
|
78
|
+
|
|
79
|
+
## Correlation and observation hierarchy
|
|
80
|
+
|
|
81
|
+
```mermaid
|
|
82
|
+
flowchart TD
|
|
83
|
+
C[Conversation attachment / trace] --> S[Submission spans: durable IDs and host request correlation]
|
|
84
|
+
C --> R[Run: primary submission and input IDs]
|
|
85
|
+
R --> G[Observed generation rounds / provider attempts]
|
|
86
|
+
R --> T[Tool calls / committed result entries]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A conversation span is open for the attachment's lifetime. Runs end on `run_end`; submissions end only from committed `done` or `unanswered` status. `turn_end` is not submission completion. A steer can join an existing run, and multiple submissions can share one committed answer. Queued follow-ups start separate runs. A run is keyed by its primary input submission, not by generation task IDs that change between rounds.
|
|
90
|
+
|
|
91
|
+
`sessionId` is the host conversation/session dimension; its fallback is the numeric durable conversation ID, which is not globally unique across storage sessions. `applicationId`, `userId`, bounded `metadata`, `traceName` and `onTraceId` belong to the conversation attachment. Metadata keys beginning with `pi.` are reserved. IDs and custom metadata are exported even with content capture disabled; use pseudonymous identities where appropriate.
|
|
92
|
+
|
|
93
|
+
`submissionCorrelation(record)` snapshots request/user/application correlation when a submission is first observed. Its default request ID is the durable record's `requestId`, and user/application IDs inherit conversation defaults. `observeSubmission(id, correlation)` can supply or update correlation while that submission span is still open; completed spans are immutable and repeated observations do not create another span. It ignores submissions from another conversation. For race-free primary-run correlation, supply `submissionCorrelation` at attachment rather than relying on a post-submit override. The primary submission's correlation applies to its run and generation/tool children; steered submissions keep independent correlation on their own spans, without changing the run's primary identity.
|
|
94
|
+
Per-submission users are observation metadata (`pi.user.id`), not Langfuse's trace-wide user dimension. The native Langfuse `userId` remains the conversation's user so a steer/request cannot rewrite another request's trace-wide identity.
|
|
95
|
+
|
|
96
|
+
## Content and privacy
|
|
97
|
+
|
|
98
|
+
`captureContent` defaults to `true`, matching the existing session API; pass `false` when content must remain local. Capture uses the shared bounded sanitizer: each input/output and metadata projection has a cumulative 64 KiB serialized UTF-8 budget, and scalar names/model/user/session fields are bounded separately. Bounds apply only to tracing copies, never durable entries, provider context, tool execution inputs/results or caller responses.
|
|
99
|
+
|
|
100
|
+
| Durable data | Trace projection |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| Submitted input and final answer | Committed model messages from submission `entry` and `answer` IDs; no reconstruction from streamed fragments |
|
|
103
|
+
| Assistant generation | Committed `EntryRecord.model`, text/thinking/tool-call blocks; model/provider identity and stop reason; error bodies omitted |
|
|
104
|
+
| Image blocks and embedded data URIs | MIME identity/omission markers; no base64 payload |
|
|
105
|
+
| Tool call | Committed start arguments, tool name and call ID; no running-output capture |
|
|
106
|
+
| Tool result | Successful committed model content and severity from `isError`, optional numeric usage/cost; error bodies and opaque details/nested calls omitted |
|
|
107
|
+
| Usage | Finite nonnegative input/output/cache-read/cache-write/total token and cost buckets from committed assistant/tool messages |
|
|
108
|
+
| Errors | Status/severity, allowlisted built-in unanswered reason codes and structural task/submission IDs; no raw provider error strings, arbitrary submission reason/detail, task-failure prose or retry errors |
|
|
109
|
+
| Provider configuration and headers | Never inspected or projected |
|
|
110
|
+
| Compaction/custom entry data | Not captured |
|
|
111
|
+
|
|
112
|
+
Error-result bodies are replaced by omission markers. For successful tool results, the adapter reads only the public `ToolResultEntry.data.diagnostics` count to omit the final rendered diagnostic block, which can contain raw error/credential prose. This covers tool/env throws, validation and hook failures, blocked/unavailable tools, recovery/abort results, returned errors and successful tools with diagnostics. The committed transcript is never changed; upgrades must re-audit the public result shape and rendered-diagnostic placement.
|
|
113
|
+
|
|
114
|
+
The sanitizer omits opaque continuation signatures, removes embedded base64 data URIs, and masks recognized credential fields such as `apiKey`, authorization, cookies, credentials, passwords, secret keys and access/refresh tokens. The production backend also masks configured Langfuse keys. These rules are not a universal secret scanner: arbitrary user text, tool output, error-looking tool content or host correlation data can still contain secrets. The host must avoid supplying secrets in arbitrary content, enforce export authorization, and use metadata-only capture where required. No provider-configuration objects or arbitrary diagnostic payloads are passed to the exporter.
|
|
115
|
+
|
|
116
|
+
## Snapshots, overflow and recovery
|
|
117
|
+
|
|
118
|
+
Initial attachment always establishes a snapshot baseline, not historical replay. Existing entries are marked seen; the adapter does not fabricate generations or tool starts from old entries, partial messages or tool slots. An active run can be represented as a snapshot-observed span with incomplete history. In-flight and queued submission IDs are reconciled using safe status reads. To observe an already completed submission, explicitly call `observeSubmission(id)`; its committed input/answer become a snapshot-observed summary, without invented round history or timestamps.
|
|
119
|
+
|
|
120
|
+
Durable keeps at most 100 undelivered watch frames. Overflow delivers a fresh snapshot: the adapter increments `pi.durable.resync_count`, marks history incomplete, closes open generation/tool observations as interrupted, and reconciles known/current submission IDs. It keeps an identifiable ongoing run but marks it incomplete; a missing run closes with an unknown-boundary warning, not an inferred success/abort. Missing frames are never evidence that activity did not happen. Previously unknown submissions that both began and finished inside a dropped interval cannot be discovered from the snapshot; hosts can retain their IDs and explicitly reconcile them.
|
|
121
|
+
|
|
122
|
+
During one attachment, committed entry IDs, terminal submission IDs, primary run identities and tool-call/round identities suppress identifiable repeated completions. Snapshot entries are not replayed. Spans after a gap have observation-time boundaries, not reconstructed execution timestamps. The adapter does not claim complete retry counts, exact request timing, exactly-once delivery, or trace continuity across crashes/reopen. Reopen leaves scheduling paused until the host explicitly drives work; attaching tracing cannot activate it. No tracing state is written into durable storage.
|
|
123
|
+
|
|
124
|
+
## Known limitations
|
|
125
|
+
|
|
126
|
+
The public event stream has no stable run UUID or generation-start task ID. Generation indices describe observed requests/rounds, not guaranteed historical ordinal numbers, and a nonstreamed response can have only an observed completion-time span. Missed attempt/tool activity cannot be recovered exactly. Generation input wire payloads, time-to-first-token, response headers, compaction history, cross-conversation subagent trees and historical usage totals are not synthesized. Conversation traces can be long-lived; dispose at the host conversation lifecycle boundary to complete them. Delivery is subject to exporter failures and process crashes, with no persistent exactly-once outbox.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-langfuse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "Pi extension that traces LLM generations and tool activity to Langfuse.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./durable": {
|
|
15
|
+
"types": "./dist/durable.d.ts",
|
|
16
|
+
"import": "./dist/durable.js"
|
|
13
17
|
}
|
|
14
18
|
},
|
|
15
19
|
"private": false,
|
|
@@ -24,6 +28,7 @@
|
|
|
24
28
|
"files": [
|
|
25
29
|
"src",
|
|
26
30
|
"dist",
|
|
31
|
+
"docs",
|
|
27
32
|
"README.md",
|
|
28
33
|
"LICENSE"
|
|
29
34
|
],
|
|
@@ -40,10 +45,25 @@
|
|
|
40
45
|
"typecheck": "tsc --noEmit"
|
|
41
46
|
},
|
|
42
47
|
"peerDependencies": {
|
|
43
|
-
"@earendil-works/pi-coding-agent": "*"
|
|
48
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
49
|
+
"@earendil-works/pi-durable": "1.0.4",
|
|
50
|
+
"@earendil-works/chord": "^1.0.4"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@earendil-works/pi-coding-agent": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"@earendil-works/pi-durable": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"@earendil-works/chord": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
44
62
|
},
|
|
45
63
|
"devDependencies": {
|
|
46
64
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
65
|
+
"@earendil-works/pi-durable": "1.0.4",
|
|
66
|
+
"@earendil-works/chord": "1.1.0",
|
|
47
67
|
"@types/node": "26.6.4",
|
|
48
68
|
"typescript": "7.0.2",
|
|
49
69
|
"esbuild": "0.28.2"
|
package/src/config.ts
CHANGED
|
@@ -4,7 +4,10 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
|
|
6
6
|
const CONFIG_FILE_NAME = "pi-langfuse.json";
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
import { DEFAULT_BASE_URL } from "./runtime-defaults.js";
|
|
9
|
+
|
|
10
|
+
export { DEFAULT_BASE_URL } from "./runtime-defaults.js";
|
|
8
11
|
|
|
9
12
|
export interface LangfuseConfig {
|
|
10
13
|
publicKey: string;
|