@cat-factory/observability-langfuse 0.6.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Igor Savin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @cat-factory/observability-langfuse
2
+
3
+ Opt-in [Langfuse](https://langfuse.com) trace sink for the Agent Architecture Board.
4
+
5
+ It implements the runtime-neutral `LlmTraceSink` port from `@cat-factory/kernel`, so
6
+ when wired into a facade every LLM call — container-agent calls (through the LLM proxy)
7
+ **and** inline calls (requirements review, document planner, fragment selector, inline
8
+ agent) — surfaces in Langfuse as a generation grouped under its run's trace, plus
9
+ optional container tool spans.
10
+
11
+ ## Why fetch-only
12
+
13
+ The sink talks to Langfuse's public **ingestion API** (`POST /api/public/ingestion`,
14
+ HTTP Basic auth, batched JSON events) using only the global `fetch` / `crypto` /
15
+ `btoa`. It deliberately does **not** depend on the official `langfuse` Node SDK or any
16
+ `@opentelemetry/*` package — those rely on Node-only APIs that are unavailable on the
17
+ Cloudflare Worker runtime (workerd). Using the raw ingestion API keeps the sink
18
+ byte-for-byte identical on both the Worker and Node facades.
19
+
20
+ ## Behaviour
21
+
22
+ - Never throws into the caller: every flush swallows its own errors (logging at most a
23
+ warning). Observability must never break agent work.
24
+ - Honours the same `LLM_RECORD_PROMPTS` privacy switch as the local metric store: when
25
+ prompt recording is off, generations carry usage/timing/metadata but no prompt or
26
+ response bodies.
27
+
28
+ ## Usage
29
+
30
+ ```ts
31
+ import { createLangfuseSink } from '@cat-factory/observability-langfuse'
32
+
33
+ const sink = createLangfuseSink({
34
+ publicKey: env.LANGFUSE_PUBLIC_KEY,
35
+ secretKey: env.LANGFUSE_SECRET_KEY,
36
+ baseUrl: env.LANGFUSE_BASE_URL, // optional; defaults to Langfuse Cloud
37
+ })
38
+ ```
39
+
40
+ Wired into a facade via its container's `selectLangfuseSink(config)`; absent config ⇒
41
+ the sink is never built and there is no external emission or behaviour change.
@@ -0,0 +1,30 @@
1
+ import type { LlmGenerationEvent, LlmToolSpan, LlmToolSpanContext, LlmTraceSink } from '@cat-factory/kernel';
2
+ /** Minimal structured logger (pino-compatible); optional. */
3
+ export interface LangfuseLogger {
4
+ warn(obj: Record<string, unknown>, msg?: string): void;
5
+ }
6
+ export interface LangfuseSinkConfig {
7
+ /** Langfuse public key (`pk-lf-…`). */
8
+ publicKey: string;
9
+ /** Langfuse secret key (`sk-lf-…`). */
10
+ secretKey: string;
11
+ /** Host of the Langfuse instance. Default: Langfuse Cloud (`https://cloud.langfuse.com`). */
12
+ baseUrl?: string;
13
+ /** Optional logger for swallowed errors. */
14
+ logger?: LangfuseLogger;
15
+ /** Injectable fetch (tests); defaults to the global `fetch`. */
16
+ fetchImpl?: typeof fetch;
17
+ }
18
+ export declare class LangfuseTraceSink implements LlmTraceSink {
19
+ private readonly endpoint;
20
+ private readonly authorization;
21
+ private readonly logger?;
22
+ private readonly fetchImpl;
23
+ constructor(config: LangfuseSinkConfig);
24
+ recordGeneration(event: LlmGenerationEvent): Promise<void>;
25
+ recordToolSpans(context: LlmToolSpanContext, spans: LlmToolSpan[]): Promise<void>;
26
+ private send;
27
+ }
28
+ /** Build a {@link LangfuseTraceSink}. Returns the opt-in sink wired into a facade. */
29
+ export declare function createLangfuseSink(config: LangfuseSinkConfig): LangfuseTraceSink;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,kBAAkB,EAClB,YAAY,EACb,MAAM,qBAAqB,CAAA;AAkD5B,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvD;AAED,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAkBD,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IAExC,YAAY,MAAM,EAAE,kBAAkB,EAMrC;IAEK,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD/D;IAEK,eAAe,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBtF;YAEa,IAAI;CA4BnB;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,iBAAiB,CAEhF"}
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ // A fetch-based Langfuse trace sink. It speaks Langfuse's public **ingestion API**
2
+ // (`POST /api/public/ingestion`, HTTP Basic auth = public:secret key, batched JSON
3
+ // events) using only the global `fetch`/`crypto`/`btoa` — NO `langfuse` Node SDK and
4
+ // NO `@opentelemetry/*`, both of which depend on Node-only APIs that are unavailable on
5
+ // the Cloudflare Worker runtime (workerd). This keeps the sink byte-for-byte identical
6
+ // across the Worker and Node facades, which is the whole reason it exists as its own
7
+ // package: see `LlmTraceSink` in `@cat-factory/kernel`.
8
+ //
9
+ // Observability must never break the product, so every method swallows its own errors
10
+ // (logging at most a warning) and the caller additionally schedules the call off the
11
+ // response path. A failed flush drops that batch; it never propagates.
12
+ //
13
+ // Each `recordGeneration` posts its own small ingestion batch rather than buffering
14
+ // across calls. This is deliberate: the Worker runtime is stateless per request (there
15
+ // is no durable cross-request buffer to flush, and a `waitUntil`-scheduled POST can't
16
+ // outlive its request), so a per-call POST is the only shape that stays identical across
17
+ // the Worker and Node facades. Tool spans, which the backend already accumulates per
18
+ // poll, ARE sent as one batch. Langfuse's ingestion API is built for this volume.
19
+ //
20
+ // IMPACT ANALYSIS — why per-call POST is safe for the execution hot path:
21
+ // - NOT on the hot path. The proxied feeder runs under `executionCtx.waitUntil`
22
+ // (`LlmProxyController`), scheduled AFTER the container's chat-completion response
23
+ // is returned (on Node `waitUntil` is a plain fire-and-forget); the inline feeder
24
+ // (`InstrumentedModelProvider`) dispatches AFTER `generateText` resolves. Inside
25
+ // `LlmObservabilityService.record` the POST is then dispatched detached (not
26
+ // awaited), so even the `waitUntil` window never blocks on the Langfuse round trip.
27
+ // The only synchronous cost added to any path is one object build + `JSON.stringify`
28
+ // — microseconds, never the network call.
29
+ // - NOT a source of execution brittleness. Every error is swallowed + logged, the
30
+ // fetch is bounded by SEND_TIMEOUT_MS, and nothing in the run lifecycle reads the
31
+ // sink's result — a Langfuse outage / slowness / 4xx drops a batch and nothing else.
32
+ // - The costs that DO exist are telemetry-side, not run-side: +1 detached subrequest
33
+ // per proxy invocation (~2 of the Worker's 1000-subrequest budget), negligible
34
+ // `waitUntil` CPU (I/O-bound, timeout-capped), and N calls ⇒ N POSTs — a very chatty
35
+ // run could brush Langfuse ingestion rate limits and drop some batches, degrading
36
+ // telemetry COMPLETENESS only, never the run. (Tool spans are batched per poll, so
37
+ // they don't multiply.)
38
+ const DEFAULT_BASE_URL = 'https://cloud.langfuse.com';
39
+ /**
40
+ * Hard ceiling on a single ingestion POST. Observability must never tie up the LLM
41
+ * path: the proxied feeder records under the platform's `waitUntil` budget and the
42
+ * inline feeder dispatches without awaiting, so a hung Langfuse endpoint must abort
43
+ * rather than dangle. A dropped batch is the documented best-effort worst case.
44
+ */
45
+ const SEND_TIMEOUT_MS = 10_000;
46
+ function iso(ms) {
47
+ return new Date(ms).toISOString();
48
+ }
49
+ function basicAuth(publicKey, secretKey) {
50
+ return `Basic ${btoa(`${publicKey}:${secretKey}`)}`;
51
+ }
52
+ export class LangfuseTraceSink {
53
+ endpoint;
54
+ authorization;
55
+ logger;
56
+ fetchImpl;
57
+ constructor(config) {
58
+ const base = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
59
+ this.endpoint = `${base}/api/public/ingestion`;
60
+ this.authorization = basicAuth(config.publicKey, config.secretKey);
61
+ this.logger = config.logger;
62
+ this.fetchImpl = config.fetchImpl ?? fetch;
63
+ }
64
+ async recordGeneration(event) {
65
+ // Group every call of a run under one trace (keyed by the execution id); inline
66
+ // single-shot calls (no execution) become their own standalone trace.
67
+ const traceId = event.executionId ?? crypto.randomUUID();
68
+ const generation = {
69
+ id: crypto.randomUUID(),
70
+ traceId,
71
+ name: event.agentKind,
72
+ startTime: iso(event.startedAt),
73
+ endTime: iso(event.endedAt),
74
+ model: event.model,
75
+ usage: {
76
+ input: event.promptTokens,
77
+ output: event.completionTokens,
78
+ total: event.totalTokens,
79
+ unit: 'TOKENS',
80
+ },
81
+ level: event.ok ? 'DEFAULT' : 'ERROR',
82
+ metadata: {
83
+ provider: event.provider,
84
+ agentKind: event.agentKind,
85
+ finishReason: event.finishReason,
86
+ workspaceId: event.workspaceId,
87
+ },
88
+ };
89
+ // Prompt/response bodies are present only when prompt recording is on (the same
90
+ // `LLM_RECORD_PROMPTS` switch the local store honours): omit empty bodies entirely.
91
+ if (event.input)
92
+ generation.input = event.input;
93
+ if (event.output)
94
+ generation.output = event.output;
95
+ if (event.errorMessage)
96
+ generation.statusMessage = event.errorMessage;
97
+ await this.send([
98
+ {
99
+ id: crypto.randomUUID(),
100
+ type: 'trace-create',
101
+ timestamp: iso(event.endedAt),
102
+ body: {
103
+ id: traceId,
104
+ // A run trace is upserted by every call it groups, so keep the trace body
105
+ // stable across them: the per-call agent kind lives on each generation
106
+ // (its `name` + metadata), NOT as a trace tag that the next call would clobber.
107
+ name: event.executionId ? `run ${event.executionId}` : event.agentKind,
108
+ metadata: { workspaceId: event.workspaceId },
109
+ },
110
+ },
111
+ {
112
+ id: crypto.randomUUID(),
113
+ type: 'generation-create',
114
+ timestamp: iso(event.endedAt),
115
+ body: generation,
116
+ },
117
+ ]);
118
+ }
119
+ async recordToolSpans(context, spans) {
120
+ // Tool spans are only meaningful as children of a run's trace.
121
+ if (!context.executionId || spans.length === 0)
122
+ return;
123
+ const traceId = context.executionId;
124
+ const batch = spans.map((span) => ({
125
+ id: crypto.randomUUID(),
126
+ type: 'span-create',
127
+ timestamp: iso(span.endedAt),
128
+ body: {
129
+ id: crypto.randomUUID(),
130
+ traceId,
131
+ name: span.tool,
132
+ startTime: iso(span.startedAt),
133
+ endTime: iso(span.endedAt),
134
+ level: span.ok ? 'DEFAULT' : 'ERROR',
135
+ metadata: { agentKind: context.agentKind },
136
+ },
137
+ }));
138
+ await this.send(batch);
139
+ }
140
+ async send(batch) {
141
+ try {
142
+ const res = await this.fetchImpl(this.endpoint, {
143
+ method: 'POST',
144
+ headers: {
145
+ 'content-type': 'application/json',
146
+ authorization: this.authorization,
147
+ },
148
+ body: JSON.stringify({ batch }),
149
+ // Bound the request so a hung endpoint can't tie up the caller's waitUntil
150
+ // budget; an abort lands in the catch below and drops the batch (best-effort).
151
+ signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
152
+ });
153
+ // 207 = partial success (per-event errors in the body); anything else non-2xx is
154
+ // a hard failure. Either way we only log — observability never breaks the caller.
155
+ if (!res.ok && res.status !== 207) {
156
+ this.logger?.warn({ scope: 'langfuse', status: res.status }, 'langfuse: ingestion rejected batch');
157
+ }
158
+ }
159
+ catch (err) {
160
+ this.logger?.warn({ scope: 'langfuse', err: err instanceof Error ? err.message : String(err) }, 'langfuse: failed to post ingestion batch');
161
+ }
162
+ }
163
+ }
164
+ /** Build a {@link LangfuseTraceSink}. Returns the opt-in sink wired into a facade. */
165
+ export function createLangfuseSink(config) {
166
+ return new LangfuseTraceSink(config);
167
+ }
168
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@cat-factory/observability-langfuse",
3
+ "version": "0.6.0",
4
+ "description": "Opt-in Langfuse trace sink for the Agent Architecture Board. A fetch-based LlmTraceSink that streams LLM generations (and container tool spans) to Langfuse's ingestion API — runs unchanged on both the Cloudflare Worker (workerd) and Node facades.",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "dependencies": {
22
+ "@cat-factory/kernel": "0.6.0"
23
+ },
24
+ "devDependencies": {
25
+ "typescript": "7.0.1-rc",
26
+ "vitest": "^3.2.4"
27
+ },
28
+ "scripts": {
29
+ "build": "tsc -b tsconfig.build.json",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit",
31
+ "test:run": "vitest run"
32
+ }
33
+ }