@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
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@narumitw/pi-langfuse) [](https://pi.dev) [](./LICENSE)
|
|
4
4
|
|
|
5
|
-
Export Pi agent
|
|
5
|
+
Export Pi coding-agent and durable conversation activity to [Langfuse](https://langfuse.com/) through OpenTelemetry.
|
|
6
6
|
|
|
7
7
|
## ✨ Features
|
|
8
8
|
|
|
@@ -13,6 +13,7 @@ Export Pi agent runs, model generations, retries, tools, compaction, usage, and
|
|
|
13
13
|
- Supports metadata-only tracing when content capture is disabled.
|
|
14
14
|
- Keeps the default extension's credentials in a private local settings file and redacts them from exported data.
|
|
15
15
|
- Supports multiple concurrent Pi sessions through one process-owned runtime without sharing session trace state.
|
|
16
|
+
- Provides an opt-in public `/durable` adapter for committed submissions, model generations and tool activity without `AgentSession`.
|
|
16
17
|
- Batches exports without delaying normal completion and isolates its OpenTelemetry provider from other extensions.
|
|
17
18
|
|
|
18
19
|
## 📦 Install
|
|
@@ -35,7 +36,8 @@ pi -e ./packages/pi-langfuse
|
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
The package declares `dist/index.ts`, so Pi cannot load an unbuilt local checkout.
|
|
38
|
-
The
|
|
39
|
+
The Langfuse SDK requires Node.js 20 or newer; the supported Pi runtimes require Node.js >=22.19.0.
|
|
40
|
+
The durable adapter targets experimental `pi-durable@1.0.4` exactly; review its [compatibility and upgrade checks](./docs/durable.md#compatibility) before enabling or upgrading it.
|
|
39
41
|
Pi extensions run with your user permissions.
|
|
40
42
|
Review this extension and its data-export behavior before installing it.
|
|
41
43
|
|
|
@@ -114,6 +116,44 @@ The default extension always uses its validated `pi-langfuse.json` with `env: fa
|
|
|
114
116
|
Custom metadata is sanitized, bounded, and exported even when `captureContent` is `false`; package-owned `pi.*` fields cannot be overridden.
|
|
115
117
|
Treat custom metadata, tags, session IDs, user IDs, request IDs, and trace names as exported data.
|
|
116
118
|
|
|
119
|
+
## 🧱 Durable host applications
|
|
120
|
+
|
|
121
|
+
**Experimental:** use the dedicated public export with `@earendil-works/pi-durable@1.0.4`, compatible Pi AI, and Chord ^1.0.4.
|
|
122
|
+
Install those host dependencies explicitly; the durable entry does not require or load coding-agent registration.
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
126
|
+
import {
|
|
127
|
+
createLangfuseRuntime,
|
|
128
|
+
createPiLangfuseDurableConversation,
|
|
129
|
+
} from "@narumitw/pi-langfuse/durable";
|
|
130
|
+
|
|
131
|
+
const runtime = await createLangfuseRuntime(); // Standard Langfuse credentials/environment.
|
|
132
|
+
const tracing = await createPiLangfuseDurableConversation(runtime, {
|
|
133
|
+
harness, // Existing, authorized host-owned Harness.
|
|
134
|
+
conversationId: conversation.id,
|
|
135
|
+
context: BACKGROUND_CONTEXT,
|
|
136
|
+
sessionId: hostConversationId,
|
|
137
|
+
userId: accountId,
|
|
138
|
+
applicationId: "support-service",
|
|
139
|
+
captureContent: false,
|
|
140
|
+
});
|
|
141
|
+
const submission = await conversation.submit({
|
|
142
|
+
type: "input", content: userPrompt, requestId: incomingRequestId,
|
|
143
|
+
}, executionContext);
|
|
144
|
+
const result = await submission.wait(executionContext); // Host execution responsibility.
|
|
145
|
+
await tracing.observeSubmission(submission.id); // Optional committed-status reconciliation.
|
|
146
|
+
await tracing.dispose(); // Observation only; does not abort work or close the Harness.
|
|
147
|
+
// After every controller using this shared runtime has ended:
|
|
148
|
+
await runtime.shutdown();
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Create one observer per conversation and retain submission IDs for explicit reconciliation after gaps.
|
|
152
|
+
Late attachment and overflow provide snapshots, not complete event replay; interrupted observations are marked uncertain.
|
|
153
|
+
Tracing never resumes paused recovery or controls execution, and capture bounds never change execution data.
|
|
154
|
+
See the installed [durable API guide](./docs/durable.md) for the compatibility matrix, ownership, per-request correlation, privacy projections, recovery semantics and limitations.
|
|
155
|
+
The host remains responsible for authorization, storage, execution cancellation/recovery, callback-owned work and shared-runtime shutdown.
|
|
156
|
+
|
|
117
157
|
## ⚙️ Settings
|
|
118
158
|
|
|
119
159
|
Run the interactive manager and choose **Set up Langfuse for this Pi agent directory** or **Update Langfuse for this Pi agent directory**:
|
|
@@ -301,15 +341,18 @@ packages/pi-langfuse/
|
|
|
301
341
|
│ ├── index.ts # Thin default and public entrypoint
|
|
302
342
|
│ ├── langfuse.ts # Default settings and command adapter
|
|
303
343
|
│ ├── pi-session.ts # Per-session Pi lifecycle controller
|
|
344
|
+
│ ├── durable.ts # Public durable observer entrypoint
|
|
304
345
|
│ ├── runtime-core.ts # Process-runtime ownership
|
|
305
346
|
│ ├── runtime.ts # Lazy production Langfuse runtime
|
|
306
347
|
│ └── tracing.ts # Schema-v2 observation recorder
|
|
348
|
+
├── docs/durable.md # Durable API, compatibility and privacy contract
|
|
307
349
|
├── dist/ # Generated Node, declaration, and Jiti runtime
|
|
308
350
|
├── scripts/build-runtime.mjs # Runtime builder
|
|
309
351
|
└── test/ # Behavior and lifecycle coverage
|
|
310
352
|
```
|
|
311
353
|
|
|
312
|
-
The generated runtime is built from `src/index.ts
|
|
354
|
+
The generated extension runtime is built from `src/index.ts`; the Node library also builds `src/durable.ts` as a separate public entry.
|
|
355
|
+
Neither generated runtime imports back into `src`.
|
|
313
356
|
|
|
314
357
|
## 🔎 Keywords
|
|
315
358
|
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
// src/runtime-core.ts
|
|
8
|
+
var RUNTIME_INTERNAL_KEY = /* @__PURE__ */ Symbol.for("@narumitw/pi-langfuse/runtime-internal/v2");
|
|
9
|
+
var ManagedLangfuseRuntime = class {
|
|
10
|
+
constructor(backend) {
|
|
11
|
+
this.backend = backend;
|
|
12
|
+
}
|
|
13
|
+
backend;
|
|
14
|
+
state = "open";
|
|
15
|
+
sessionDisposers = /* @__PURE__ */ new Set();
|
|
16
|
+
flushQueue = Promise.resolve();
|
|
17
|
+
shutdownPromise;
|
|
18
|
+
get closed() {
|
|
19
|
+
return this.state !== "open";
|
|
20
|
+
}
|
|
21
|
+
registerSession(disposer) {
|
|
22
|
+
if (this.state !== "open") throw new Error("Langfuse runtime is shutting down or already closed.");
|
|
23
|
+
this.sessionDisposers.add(disposer);
|
|
24
|
+
return () => this.sessionDisposers.delete(disposer);
|
|
25
|
+
}
|
|
26
|
+
flush() {
|
|
27
|
+
if (this.state !== "open") {
|
|
28
|
+
return Promise.reject(new Error("Langfuse runtime is shutting down or already closed."));
|
|
29
|
+
}
|
|
30
|
+
const operation = this.flushQueue.then(() => this.backend.forceFlush());
|
|
31
|
+
this.flushQueue = operation.then(
|
|
32
|
+
() => void 0,
|
|
33
|
+
() => void 0
|
|
34
|
+
);
|
|
35
|
+
return operation;
|
|
36
|
+
}
|
|
37
|
+
shutdown() {
|
|
38
|
+
if (this.shutdownPromise) return this.shutdownPromise;
|
|
39
|
+
this.state = "closing";
|
|
40
|
+
this.shutdownPromise = Promise.resolve().then(() => this.shutdownNow());
|
|
41
|
+
return this.shutdownPromise;
|
|
42
|
+
}
|
|
43
|
+
async shutdownNow() {
|
|
44
|
+
const errors = [];
|
|
45
|
+
for (const dispose of [...this.sessionDisposers]) {
|
|
46
|
+
try {
|
|
47
|
+
await dispose("Langfuse runtime shut down before the Pi session was disposed.");
|
|
48
|
+
} catch (error) {
|
|
49
|
+
errors.push(error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
this.sessionDisposers.clear();
|
|
53
|
+
await this.flushQueue;
|
|
54
|
+
try {
|
|
55
|
+
await this.backend.forceFlush();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
errors.push(error);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
await this.backend.shutdown();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
errors.push(error);
|
|
63
|
+
} finally {
|
|
64
|
+
this.state = "closed";
|
|
65
|
+
}
|
|
66
|
+
if (errors.length === 1) throw errors[0];
|
|
67
|
+
if (errors.length > 1) throw new AggregateError(errors, "Langfuse runtime shutdown failed.");
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
function getLangfuseRuntimeInternal(runtime) {
|
|
71
|
+
const internal = runtime[RUNTIME_INTERNAL_KEY];
|
|
72
|
+
if (!internal) throw new Error("Langfuse runtime was not created by @narumitw/pi-langfuse.");
|
|
73
|
+
return internal;
|
|
74
|
+
}
|
|
75
|
+
function createLangfuseRuntimeFromBackend(backend) {
|
|
76
|
+
return registerLangfuseRuntime(new ManagedLangfuseRuntime(backend));
|
|
77
|
+
}
|
|
78
|
+
function registerLangfuseRuntime(runtime) {
|
|
79
|
+
Object.defineProperty(runtime, RUNTIME_INTERNAL_KEY, { value: runtime });
|
|
80
|
+
return runtime;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
DEFAULT_BASE_URL,
|
|
85
|
+
getLangfuseRuntimeInternal,
|
|
86
|
+
createLangfuseRuntimeFromBackend
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=chunk-N7WSLB5Q.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/runtime-defaults.ts", "../../src/runtime-core.ts"],
|
|
4
|
+
"sourcesContent": ["export const DEFAULT_BASE_URL = \"https://us.cloud.langfuse.com\";\n", "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": ";;;;AAAO,IAAM,mBAAmB;;;AC4BhC,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
|
+
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
DEFAULT_BASE_URL,
|
|
5
5
|
createLangfuseRuntimeFromBackend,
|
|
6
6
|
getLangfuseRuntimeInternal
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-N7WSLB5Q.ts";
|
|
8
8
|
|
|
9
9
|
// src/runtime.ts
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
@@ -349,4 +349,4 @@ export {
|
|
|
349
349
|
maskSecrets,
|
|
350
350
|
resolveLangfuseRuntimeConfig
|
|
351
351
|
};
|
|
352
|
-
//# sourceMappingURL=runtime-
|
|
352
|
+
//# sourceMappingURL=runtime-GLRAF7FE.ts.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/config.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EntryRecord } from "@earendil-works/pi-durable";
|
|
2
|
+
import type { ObservationAttributes } from "./tracing.js";
|
|
3
|
+
type Message = NonNullable<EntryRecord["model"]>[number];
|
|
4
|
+
export declare function durableAttributes(attributes: ObservationAttributes): ObservationAttributes;
|
|
5
|
+
export declare function durableDiagnosticCount(entry: EntryRecord | undefined): number;
|
|
6
|
+
/** Project committed messages without provider configuration, deltas or raw diagnostics. */
|
|
7
|
+
export declare function durableContent(messages: readonly Message[] | undefined, capture: boolean, diagnosticCount?: number): unknown;
|
|
8
|
+
export declare function durableUsage(message: Message): ObservationAttributes;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AgentEvent, EntryId, EntryRecord, SnapshotEvent, SubmissionId, SubmissionRecord } from "@earendil-works/pi-durable";
|
|
2
|
+
import type { TraceBackend } from "./tracing.js";
|
|
3
|
+
export interface DurableCorrelation {
|
|
4
|
+
requestId?: string;
|
|
5
|
+
userId?: string;
|
|
6
|
+
applicationId?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DurableRecorderOptions extends DurableCorrelation {
|
|
9
|
+
conversationId: number;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
traceName?: string;
|
|
12
|
+
captureContent?: boolean;
|
|
13
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
14
|
+
submissionCorrelation?: (record: SubmissionRecord) => DurableCorrelation;
|
|
15
|
+
onTraceId?: (traceId: string) => void | Promise<void>;
|
|
16
|
+
onError?: (stage: string) => void | Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** Durable state machine using the same trace backend, ownership and capture core as the Pi adapter. */
|
|
19
|
+
export declare class DurableRecorder {
|
|
20
|
+
private readonly backend;
|
|
21
|
+
private readonly options;
|
|
22
|
+
private readonly root;
|
|
23
|
+
private run?;
|
|
24
|
+
private readonly submissions;
|
|
25
|
+
private readonly pendingSubmissions;
|
|
26
|
+
private readonly entries;
|
|
27
|
+
private readonly completedRuns;
|
|
28
|
+
private disposed;
|
|
29
|
+
private gaps;
|
|
30
|
+
constructor(backend: TraceBackend, options: DurableRecorderOptions);
|
|
31
|
+
get submissionIds(): readonly SubmissionId[];
|
|
32
|
+
report(stage: string): void;
|
|
33
|
+
private callback;
|
|
34
|
+
private customMetadata;
|
|
35
|
+
private safe;
|
|
36
|
+
private start;
|
|
37
|
+
private update;
|
|
38
|
+
private end;
|
|
39
|
+
private interrupted;
|
|
40
|
+
private correlationAttributes;
|
|
41
|
+
submission(record: SubmissionRecord, readEntry: (id: EntryId) => Promise<EntryRecord | undefined>, correlation?: DurableCorrelation, snapshot?: boolean): Promise<void>;
|
|
42
|
+
snapshot(snapshot: SnapshotEvent, initial?: boolean): void;
|
|
43
|
+
private beginRun;
|
|
44
|
+
private closeRound;
|
|
45
|
+
private closeRun;
|
|
46
|
+
private generation;
|
|
47
|
+
event(event: AgentEvent): void;
|
|
48
|
+
dispose(reason?: string): void;
|
|
49
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Context } from "@earendil-works/chord";
|
|
2
|
+
import { type ConversationId, type Harness, type SubmissionId } from "@earendil-works/pi-durable";
|
|
3
|
+
import { type DurableCorrelation, type DurableRecorderOptions } from "./durable-recorder.js";
|
|
4
|
+
import { type LangfuseRuntime } from "./runtime-core.js";
|
|
5
|
+
export type { DurableCorrelation } from "./durable-recorder.js";
|
|
6
|
+
export { type CreateLangfuseRuntimeOptions, createLangfuseRuntime, type LangfuseRuntime, type LangfuseRuntimeConfig, } from "./runtime-api.js";
|
|
7
|
+
export { createLangfuseRuntimeFromBackend } from "./runtime-core.js";
|
|
8
|
+
export type { Observation, ObservationAttributes, ObservationType, TraceBackend } from "./tracing.js";
|
|
9
|
+
export interface PiLangfuseDurableOptions extends Omit<DurableRecorderOptions, "conversationId" | "requestId"> {
|
|
10
|
+
harness: Harness;
|
|
11
|
+
conversationId: ConversationId;
|
|
12
|
+
/** Governs observation only. Cancellation never aborts execution or resumes recovery. */
|
|
13
|
+
context: Context;
|
|
14
|
+
}
|
|
15
|
+
export interface PiLangfuseDurableConversation {
|
|
16
|
+
readonly active: boolean;
|
|
17
|
+
/** Resolves when observation has detached, including cancellation and Harness closure. */
|
|
18
|
+
readonly closed: Promise<void>;
|
|
19
|
+
/** Read committed status without waiting for or starting agent work. Also supports late submission attachment. */
|
|
20
|
+
observeSubmission(id: SubmissionId, correlation?: DurableCorrelation): Promise<void>;
|
|
21
|
+
/** Detach and close observations, without flushing/shutting down the runtime or modifying durable work. */
|
|
22
|
+
dispose(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/** Attach a fail-open observer using only public durable APIs. A failed attachment returns an inactive controller. */
|
|
25
|
+
export declare function createPiLangfuseDurableConversation(runtime: LangfuseRuntime, options: PiLangfuseDurableOptions): Promise<PiLangfuseDurableConversation>;
|