pi-langfuse 1.5.8 → 1.5.10
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 +8 -0
- package/README_CN.md +8 -0
- package/index.ts +17 -9
- package/package.json +1 -1
- package/src/langfuse.ts +20 -1
- package/src/types.ts +1 -0
- package/types/pi-coding-agent.d.ts +7 -0
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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { basename } from "node:path";
|
|
11
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
|
|
13
13
|
import { state, resetRunState, runWithSession, setCurrentSession } from "./src/state.js";
|
|
14
14
|
import { ensureConfig, promptForConfig, loadConfig } from "./src/config.js";
|
|
@@ -74,10 +74,18 @@ export default async function (pi: ExtensionAPI) {
|
|
|
74
74
|
},
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
const getSessionId = (ctx?:
|
|
77
|
+
const getSessionId = (ctx?: unknown): string | undefined => {
|
|
78
78
|
try {
|
|
79
|
-
const
|
|
80
|
-
|
|
79
|
+
const sessionManager = (ctx as ExtensionContext | undefined)?.sessionManager;
|
|
80
|
+
const sessionId = sessionManager?.getSessionId?.();
|
|
81
|
+
if (typeof sessionId === "string" && sessionId) {
|
|
82
|
+
return sessionId;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const sessionFile = sessionManager?.getSessionFile?.();
|
|
86
|
+
return typeof sessionFile === "string" && sessionFile
|
|
87
|
+
? basename(sessionFile, ".jsonl")
|
|
88
|
+
: undefined;
|
|
81
89
|
} catch {
|
|
82
90
|
return undefined;
|
|
83
91
|
}
|
|
@@ -159,11 +167,11 @@ export default async function (pi: ExtensionAPI) {
|
|
|
159
167
|
pi.on("agent_end", async (event, ctx) => withSession(ctx, async () => {
|
|
160
168
|
await finishAgentRun(event);
|
|
161
169
|
const sessionId = state.currentSessionId;
|
|
162
|
-
|
|
163
|
-
shutdownRuntime(sessionId)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
170
|
+
try {
|
|
171
|
+
await shutdownRuntime(sessionId);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.warn("📊 Langfuse: Shutdown failed", error);
|
|
174
|
+
}
|
|
167
175
|
}));
|
|
168
176
|
|
|
169
177
|
const handleSessionInterruption = (reason: string) => {
|
package/package.json
CHANGED
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) {
|
|
@@ -211,6 +219,9 @@ async function flushPendingScores(rt: LangfuseRuntime, signal: AbortSignal): Pro
|
|
|
211
219
|
|
|
212
220
|
while (pendingScores.length > 0) {
|
|
213
221
|
const scores = pendingScores.slice(0, MAX_SCORE_BATCH_SIZE);
|
|
222
|
+
for (const score of scores) {
|
|
223
|
+
score.id ??= randomUUID();
|
|
224
|
+
}
|
|
214
225
|
try {
|
|
215
226
|
const errors = await ingestBatch(
|
|
216
227
|
rt,
|
|
@@ -642,6 +653,12 @@ function doShutdownRuntime(): Promise<void> {
|
|
|
642
653
|
const deadline = Date.now() + shutdownStepTimeoutMs;
|
|
643
654
|
const controller = new AbortController();
|
|
644
655
|
const abortTimeout = setTimeout(() => controller.abort(), shutdownStepTimeoutMs);
|
|
656
|
+
const scoreController = new AbortController();
|
|
657
|
+
const scoreAbortTimeout = setTimeout(
|
|
658
|
+
() => scoreController.abort(),
|
|
659
|
+
Math.min(getScoreShutdownTimeoutMs(), shutdownStepTimeoutMs),
|
|
660
|
+
);
|
|
661
|
+
scoreAbortTimeout.unref?.();
|
|
645
662
|
stopScoreFlush(rt);
|
|
646
663
|
|
|
647
664
|
try {
|
|
@@ -650,13 +667,13 @@ function doShutdownRuntime(): Promise<void> {
|
|
|
650
667
|
() => rt.scoreFlushPromise,
|
|
651
668
|
deadline,
|
|
652
669
|
);
|
|
670
|
+
await flushPendingScores(rt, scoreController.signal);
|
|
653
671
|
await withShutdownDeadline("OTel force flush", () => rt.tracerProvider?.forceFlush?.(), deadline);
|
|
654
672
|
await withShutdownDeadline(
|
|
655
673
|
"REST fallback ingestion",
|
|
656
674
|
() => fallbackToRestIngestion(rt, controller.signal),
|
|
657
675
|
deadline,
|
|
658
676
|
);
|
|
659
|
-
await flushPendingScores(rt, controller.signal);
|
|
660
677
|
await withShutdownDeadline("Langfuse score flush", () => rt.scoreClient.flush?.(), deadline);
|
|
661
678
|
await withShutdownDeadline("Langfuse client shutdown", () => rt.scoreClient.shutdown?.(), deadline);
|
|
662
679
|
await withShutdownDeadline("OTel tracer shutdown", () => rt.tracerProvider?.shutdown?.(), deadline);
|
|
@@ -665,6 +682,8 @@ function doShutdownRuntime(): Promise<void> {
|
|
|
665
682
|
console.warn("📊 Langfuse: Failed to flush/shutdown cleanly", e);
|
|
666
683
|
} finally {
|
|
667
684
|
clearTimeout(abortTimeout);
|
|
685
|
+
clearTimeout(scoreAbortTimeout);
|
|
686
|
+
scoreController.abort();
|
|
668
687
|
clearScoreFlushTimer(rt);
|
|
669
688
|
rt.scoreFlushController?.abort();
|
|
670
689
|
rt.scoreFlushController = undefined;
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
declare module "@earendil-works/pi-coding-agent" {
|
|
2
|
+
export interface ExtensionContext {
|
|
3
|
+
sessionManager?: {
|
|
4
|
+
getSessionId?: () => unknown;
|
|
5
|
+
getSessionFile?: () => unknown;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
2
9
|
export interface ExtensionAPI {
|
|
3
10
|
on(event: string, handler: (event: any, ctx: any) => unknown): void;
|
|
4
11
|
registerCommand(
|