@narumitw/pi-langfuse 0.49.3 → 0.49.4

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 CHANGED
@@ -35,7 +35,7 @@ pi -e npm:@narumitw/pi-langfuse
35
35
  Try a local checkout:
36
36
 
37
37
  ```bash
38
- pi -e ./extensions/pi-langfuse
38
+ pi -e ./packages/pi-langfuse
39
39
  ```
40
40
 
41
41
  The Langfuse v4 SDK requires Node.js 20 or newer.
@@ -173,7 +173,7 @@ The built-in mask specifically protects Langfuse credentials; it is not a genera
173
173
  ## 🗂️ Package layout
174
174
 
175
175
  ```txt
176
- extensions/pi-langfuse/
176
+ packages/pi-langfuse/
177
177
  ├── src/
178
178
  │ ├── index.ts # Pi package entrypoint
179
179
  │ ├── langfuse.ts # Pi lifecycle integration and slash command
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-langfuse",
3
- "version": "0.49.3",
3
+ "version": "0.49.4",
4
4
  "description": "Pi extension that traces LLM generations and tool activity to Langfuse.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,6 +23,9 @@
23
23
  "./src/index.ts"
24
24
  ]
25
25
  },
26
+ "piExtension": {
27
+ "lifecycle": "stable"
28
+ },
26
29
  "scripts": {
27
30
  "check": "biome check . && npm run typecheck",
28
31
  "format": "biome check --write .",
@@ -32,9 +35,9 @@
32
35
  "@earendil-works/pi-coding-agent": "*"
33
36
  },
34
37
  "devDependencies": {
35
- "@biomejs/biome": "2.5.7",
36
- "@earendil-works/pi-coding-agent": "0.83.0",
37
- "@types/node": "26.1.2",
38
+ "@biomejs/biome": "2.5.8",
39
+ "@earendil-works/pi-coding-agent": "0.84.2",
40
+ "@types/node": "26.2.0",
38
41
  "typescript": "7.0.2"
39
42
  },
40
43
  "dependencies": {
@@ -49,6 +52,6 @@
49
52
  "repository": {
50
53
  "type": "git",
51
54
  "url": "https://github.com/narumiruna/pi-extensions",
52
- "directory": "extensions/pi-langfuse"
55
+ "directory": "packages/pi-langfuse"
53
56
  }
54
57
  }
package/src/runtime.ts CHANGED
@@ -39,7 +39,9 @@ class ProductionObservation implements Observation {
39
39
  constructor(readonly native: LangfuseObservation) {}
40
40
 
41
41
  update(attributes: ObservationAttributes): Observation {
42
- this.native.updateOtelSpanAttributes(attributes as LangfuseObservationAttributes);
42
+ const { sessionId, ...observationAttributes } = attributes;
43
+ this.native.updateOtelSpanAttributes(observationAttributes as LangfuseObservationAttributes);
44
+ applySessionId(this.native, sessionId);
43
45
  return this;
44
46
  }
45
47
 
@@ -51,9 +53,7 @@ class ProductionObservation implements Observation {
51
53
  if (name !== undefined) {
52
54
  this.native.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_NAME, name);
53
55
  }
54
- if (sessionId !== undefined) {
55
- this.native.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_SESSION_ID, sessionId);
56
- }
56
+ applySessionId(this.native, sessionId);
57
57
  if (tags !== undefined) {
58
58
  this.native.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_TAGS, tags);
59
59
  }
@@ -86,11 +86,14 @@ class ProductionTraceBackend implements TraceBackend {
86
86
  attributes: ObservationAttributes,
87
87
  options: { asType: ObservationType; parent?: Observation },
88
88
  ): Observation {
89
+ const { sessionId, ...observationAttributes } = attributes;
89
90
  const parent = options.parent;
90
- if (parent instanceof ProductionObservation) {
91
- return new ProductionObservation(startChild(parent.native, name, attributes, options.asType));
92
- }
93
- return new ProductionObservation(startRoot(name, attributes, options.asType));
91
+ const native =
92
+ parent instanceof ProductionObservation
93
+ ? startChild(parent.native, name, observationAttributes, options.asType)
94
+ : startRoot(name, observationAttributes, options.asType);
95
+ applySessionId(native, sessionId);
96
+ return new ProductionObservation(native);
94
97
  }
95
98
 
96
99
  async forceFlush(): Promise<void> {
@@ -177,6 +180,12 @@ const defaultFactories: RuntimeFactories = {
177
180
  selectProvider: setLangfuseTracerProvider,
178
181
  };
179
182
 
183
+ function applySessionId(observation: LangfuseObservation, sessionId: string | undefined): void {
184
+ if (sessionId !== undefined) {
185
+ observation.otelSpan.setAttribute(LangfuseOtelSpanAttributes.TRACE_SESSION_ID, sessionId);
186
+ }
187
+ }
188
+
180
189
  function startRoot(
181
190
  name: string,
182
191
  attributes: ObservationAttributes,
package/src/tracing.ts CHANGED
@@ -234,6 +234,14 @@ export class TraceRecorder {
234
234
  private readonly context: RecorderContext,
235
235
  ) {}
236
236
 
237
+ private startObservation(
238
+ name: string,
239
+ attributes: ObservationAttributes,
240
+ options: { asType: ObservationType; parent?: Observation },
241
+ ): Observation {
242
+ return this.backend.start(name, { ...attributes, sessionId: this.context.sessionId }, options);
243
+ }
244
+
237
245
  hasActiveTrace(): boolean {
238
246
  return this.root !== undefined;
239
247
  }
@@ -275,7 +283,7 @@ export class TraceRecorder {
275
283
  ? "git:detached"
276
284
  : undefined;
277
285
 
278
- this.root = this.backend.start("pi.agent", attributes, { asType: "agent" });
286
+ this.root = this.startObservation("pi.agent", attributes, { asType: "agent" });
279
287
  this.root.updateTrace?.({
280
288
  name: "pi.trace",
281
289
  sessionId: this.context.sessionId,
@@ -296,7 +304,7 @@ export class TraceRecorder {
296
304
  this.lastAssistant = undefined;
297
305
  this.unresolvedToolErrors = 0;
298
306
  this.attemptIndex = index;
299
- this.attempt = this.backend.start(
307
+ this.attempt = this.startObservation(
300
308
  "pi.attempt",
301
309
  {
302
310
  metadata: {
@@ -325,7 +333,7 @@ export class TraceRecorder {
325
333
  if (this.turn) this.closeTurn("Interrupted by the next Pi turn.", "ERROR");
326
334
  this.counters.turns += 1;
327
335
  this.turnIndex = turnIndex;
328
- this.turn = this.backend.start(
336
+ this.turn = this.startObservation(
329
337
  "pi.turn",
330
338
  { metadata: { "pi.turn.index": turnIndex }, version: TRACE_SCHEMA_VERSION },
331
339
  { asType: "span", parent: this.attempt ?? this.root },
@@ -372,7 +380,7 @@ export class TraceRecorder {
372
380
  ...(input.model?.api ? { "pi.request.api": input.model.api } : {}),
373
381
  ...(input.thinkingLevel ? { "pi.request.thinking_level": input.thinkingLevel } : {}),
374
382
  };
375
- const observation = this.backend.start(
383
+ const observation = this.startObservation(
376
384
  "pi.llm",
377
385
  {
378
386
  ...(input.payload !== undefined ? { input: this.capture(input.payload) } : {}),
@@ -559,7 +567,7 @@ export class TraceRecorder {
559
567
  this.closeCompaction("Interrupted by another Pi compaction.");
560
568
  this.counters.compactions += 1;
561
569
  this.compaction = {
562
- observation: this.backend.start(
570
+ observation: this.startObservation(
563
571
  "pi.compaction",
564
572
  {
565
573
  metadata: {
@@ -711,7 +719,7 @@ export class TraceRecorder {
711
719
  }
712
720
 
713
721
  private startToolObservation(toolCallId: string, toolName: string, args?: unknown): Observation {
714
- return this.backend.start(
722
+ return this.startObservation(
715
723
  `pi.tool.${toolName}`,
716
724
  {
717
725
  ...(args !== undefined ? { input: this.capture(args) } : {}),