pi-langfuse 1.5.8 → 1.5.9

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
@@ -77,6 +77,14 @@ export LANGFUSE_BASE_URL="https://cloud.langfuse.com" # optional; LANGFUSE_HOST
77
77
 
78
78
  Saved config takes precedence. Environment variables are only used when `~/.pi/agent/pi-langfuse/config.json` is missing or incomplete.
79
79
 
80
+ For short-lived SDK hosts, set the bounded final score-delivery attempt during shutdown:
81
+
82
+ ```bash
83
+ export PI_LANGFUSE_SCORE_SHUTDOWN_TIMEOUT=2 # seconds; defaults to 2 seconds
84
+ ```
85
+
86
+ The extension attempts queued trace-level scores before other shutdown telemetry work. This value cannot extend the overall shutdown deadline.
87
+
80
88
  Privacy controls can also be set through environment variables:
81
89
 
82
90
  ```bash
package/README_CN.md CHANGED
@@ -77,6 +77,14 @@ export LANGFUSE_BASE_URL="https://cloud.langfuse.com" # 可选;也支持 LANG
77
77
 
78
78
  保存的配置优先级更高。只有当 `~/.pi/agent/pi-langfuse/config.json` 缺失或不完整时,扩展才会使用环境变量。
79
79
 
80
+ 对于短生命周期的 SDK 宿主,可设置关闭时最终分数发送尝试的上限:
81
+
82
+ ```bash
83
+ export PI_LANGFUSE_SCORE_SHUTDOWN_TIMEOUT=2 # 单位为秒;默认 2 秒
84
+ ```
85
+
86
+ 扩展会在其他关闭遥测工作之前尝试发送已排队的 trace 级分数。该值不会延长总关闭超时。
87
+
80
88
  隐私采集策略也可以通过环境变量设置:
81
89
 
82
90
  ```bash
package/index.ts CHANGED
@@ -159,11 +159,11 @@ export default async function (pi: ExtensionAPI) {
159
159
  pi.on("agent_end", async (event, ctx) => withSession(ctx, async () => {
160
160
  await finishAgentRun(event);
161
161
  const sessionId = state.currentSessionId;
162
- setTimeout(() => {
163
- shutdownRuntime(sessionId).catch((error) => {
164
- console.warn("📊 Langfuse: Deferred shutdown failed", error);
165
- });
166
- }, 0);
162
+ try {
163
+ await shutdownRuntime(sessionId);
164
+ } catch (error) {
165
+ console.warn("📊 Langfuse: Shutdown failed", error);
166
+ }
167
167
  }));
168
168
 
169
169
  const handleSessionInterruption = (reason: string) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-langfuse",
3
- "version": "1.5.8",
3
+ "version": "1.5.9",
4
4
  "description": "Langfuse extension for Pi coding agent",
5
5
  "repository": {
6
6
  "type": "git",
package/src/langfuse.ts CHANGED
@@ -60,6 +60,7 @@ interface RestFallbackStore {
60
60
  const OTEL_VISIBILITY_TIMEOUT_MS = 1_500;
61
61
  const OTEL_VISIBILITY_POLL_INTERVAL_MS = 200;
62
62
  const DEFAULT_SHUTDOWN_STEP_TIMEOUT_MS = 2_000;
63
+ const DEFAULT_SCORE_SHUTDOWN_TIMEOUT_MS = 2_000;
63
64
  const DEFAULT_LANGFUSE_REQUEST_TIMEOUT_SECONDS = 5;
64
65
  const DEFAULT_SCORE_FLUSH_AT = 10;
65
66
  const DEFAULT_SCORE_FLUSH_INTERVAL_MS = 1_000;
@@ -80,6 +81,13 @@ function resolvePositiveEnvNumber(name: string, fallback: number, integer = fals
80
81
  return integer ? Math.floor(parsed) : parsed;
81
82
  }
82
83
 
84
+ function getScoreShutdownTimeoutMs(): number {
85
+ return resolvePositiveEnvNumber(
86
+ "PI_LANGFUSE_SCORE_SHUTDOWN_TIMEOUT",
87
+ DEFAULT_SCORE_SHUTDOWN_TIMEOUT_MS / 1_000,
88
+ ) * 1_000;
89
+ }
90
+
83
91
  function delay(ms: number, signal?: AbortSignal) {
84
92
  return new Promise<void>((resolve, reject) => {
85
93
  if (signal?.aborted) {
@@ -642,6 +650,12 @@ function doShutdownRuntime(): Promise<void> {
642
650
  const deadline = Date.now() + shutdownStepTimeoutMs;
643
651
  const controller = new AbortController();
644
652
  const abortTimeout = setTimeout(() => controller.abort(), shutdownStepTimeoutMs);
653
+ const scoreController = new AbortController();
654
+ const scoreAbortTimeout = setTimeout(
655
+ () => scoreController.abort(),
656
+ Math.min(getScoreShutdownTimeoutMs(), shutdownStepTimeoutMs),
657
+ );
658
+ scoreAbortTimeout.unref?.();
645
659
  stopScoreFlush(rt);
646
660
 
647
661
  try {
@@ -650,13 +664,13 @@ function doShutdownRuntime(): Promise<void> {
650
664
  () => rt.scoreFlushPromise,
651
665
  deadline,
652
666
  );
667
+ await flushPendingScores(rt, scoreController.signal);
653
668
  await withShutdownDeadline("OTel force flush", () => rt.tracerProvider?.forceFlush?.(), deadline);
654
669
  await withShutdownDeadline(
655
670
  "REST fallback ingestion",
656
671
  () => fallbackToRestIngestion(rt, controller.signal),
657
672
  deadline,
658
673
  );
659
- await flushPendingScores(rt, controller.signal);
660
674
  await withShutdownDeadline("Langfuse score flush", () => rt.scoreClient.flush?.(), deadline);
661
675
  await withShutdownDeadline("Langfuse client shutdown", () => rt.scoreClient.shutdown?.(), deadline);
662
676
  await withShutdownDeadline("OTel tracer shutdown", () => rt.tracerProvider?.shutdown?.(), deadline);
@@ -665,6 +679,8 @@ function doShutdownRuntime(): Promise<void> {
665
679
  console.warn("📊 Langfuse: Failed to flush/shutdown cleanly", e);
666
680
  } finally {
667
681
  clearTimeout(abortTimeout);
682
+ clearTimeout(scoreAbortTimeout);
683
+ scoreController.abort();
668
684
  clearScoreFlushTimer(rt);
669
685
  rt.scoreFlushController?.abort();
670
686
  rt.scoreFlushController = undefined;