pi-langfuse 1.5.7 → 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.7",
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
@@ -1,4 +1,4 @@
1
- import type { LangfuseRuntime, LangfuseScoreClient } from "./types.js";
1
+ import type { LangfuseRuntime, LangfuseScoreClient, PendingScore } from "./types.js";
2
2
  import { state } from "./state.js";
3
3
  import { randomUUID } from "node:crypto";
4
4
 
@@ -60,6 +60,12 @@ 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;
64
+ const DEFAULT_LANGFUSE_REQUEST_TIMEOUT_SECONDS = 5;
65
+ const DEFAULT_SCORE_FLUSH_AT = 10;
66
+ const DEFAULT_SCORE_FLUSH_INTERVAL_MS = 1_000;
67
+ const MAX_SCORE_QUEUE_SIZE = 100_000;
68
+ const MAX_SCORE_BATCH_SIZE = 100;
63
69
 
64
70
  let shutdownStepTimeoutMs = DEFAULT_SHUTDOWN_STEP_TIMEOUT_MS;
65
71
 
@@ -67,8 +73,34 @@ function nowIso() {
67
73
  return new Date().toISOString();
68
74
  }
69
75
 
70
- function delay(ms: number) {
71
- return new Promise((resolve) => setTimeout(resolve, ms));
76
+ function resolvePositiveEnvNumber(name: string, fallback: number, integer = false): number {
77
+ const parsed = Number(process.env[name]);
78
+ if (!Number.isFinite(parsed) || parsed <= 0) {
79
+ return fallback;
80
+ }
81
+ return integer ? Math.floor(parsed) : parsed;
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
+
91
+ function delay(ms: number, signal?: AbortSignal) {
92
+ return new Promise<void>((resolve, reject) => {
93
+ if (signal?.aborted) {
94
+ reject(signal.reason);
95
+ return;
96
+ }
97
+
98
+ const timeout = setTimeout(resolve, ms);
99
+ signal?.addEventListener("abort", () => {
100
+ clearTimeout(timeout);
101
+ reject(signal.reason);
102
+ }, { once: true });
103
+ });
72
104
  }
73
105
 
74
106
  function debugLog(message: string) {
@@ -107,7 +139,14 @@ export function getLastRuntimeError(): { scope: string; message: string; timesta
107
139
  return lastRuntimeError;
108
140
  }
109
141
 
110
- async function withTimeout<T>(label: string, operation: Promise<T> | undefined): Promise<T | undefined> {
142
+ async function withShutdownDeadline<T>(label: string, startOperation: () => Promise<T> | undefined, deadline: number): Promise<T | undefined> {
143
+ const remainingMs = deadline - Date.now();
144
+ if (remainingMs <= 0) {
145
+ debugLog(`📊 Langfuse: Skipped ${label}; shutdown deadline elapsed`);
146
+ return undefined;
147
+ }
148
+
149
+ const operation = startOperation();
111
150
  if (!operation) {
112
151
  return undefined;
113
152
  }
@@ -118,9 +157,9 @@ async function withTimeout<T>(label: string, operation: Promise<T> | undefined):
118
157
  operation,
119
158
  new Promise<undefined>((resolve) => {
120
159
  timeout = setTimeout(() => {
121
- debugLog(`📊 Langfuse: ${label} timed out after ${shutdownStepTimeoutMs}ms`);
160
+ debugLog(`📊 Langfuse: ${label} timed out; shutdown deadline elapsed`);
122
161
  resolve(undefined);
123
- }, shutdownStepTimeoutMs);
162
+ }, remainingMs);
124
163
  }),
125
164
  ]);
126
165
  } finally {
@@ -130,6 +169,142 @@ async function withTimeout<T>(label: string, operation: Promise<T> | undefined):
130
169
  }
131
170
  }
132
171
 
172
+ function getRuntimeConfig(rt: LangfuseRuntime) {
173
+ return rt.runtimeConfig ?? state.config;
174
+ }
175
+
176
+ function ingestionHeaders(rt: LangfuseRuntime): Record<string, string> {
177
+ const config = getRuntimeConfig(rt);
178
+ if (!config) {
179
+ throw new Error("Langfuse runtime config is unavailable");
180
+ }
181
+
182
+ const auth = Buffer.from(`${config.publicKey}:${config.secretKey}`).toString("base64");
183
+ return {
184
+ Authorization: `Basic ${auth}`,
185
+ "Content-Type": "application/json",
186
+ };
187
+ }
188
+
189
+ async function ingestBatch(rt: LangfuseRuntime, batch: unknown[], signal: AbortSignal): Promise<unknown[]> {
190
+ const config = getRuntimeConfig(rt);
191
+ if (!config) {
192
+ throw new Error("Langfuse runtime config is unavailable");
193
+ }
194
+
195
+ const response = await fetch(`${config.host.replace(/\/$/, "")}/api/public/ingestion`, {
196
+ method: "POST",
197
+ headers: ingestionHeaders(rt),
198
+ body: JSON.stringify({ batch }),
199
+ signal,
200
+ });
201
+ if (!response.ok) {
202
+ throw new Error(`Langfuse ingestion failed with HTTP ${response.status}`);
203
+ }
204
+
205
+ const text = await response.text();
206
+ if (!text) {
207
+ return [];
208
+ }
209
+
210
+ const responseBody = JSON.parse(text) as { errors?: unknown[] };
211
+ return Array.isArray(responseBody.errors) ? responseBody.errors : [];
212
+ }
213
+
214
+ async function flushPendingScores(rt: LangfuseRuntime, signal: AbortSignal): Promise<void> {
215
+ const pendingScores = rt.pendingScores;
216
+ if (!pendingScores || pendingScores.length === 0) {
217
+ return;
218
+ }
219
+
220
+ while (pendingScores.length > 0) {
221
+ const scores = pendingScores.slice(0, MAX_SCORE_BATCH_SIZE);
222
+ try {
223
+ const errors = await ingestBatch(
224
+ rt,
225
+ scores.map((score) => ({
226
+ type: "score-create",
227
+ id: randomUUID(),
228
+ timestamp: nowIso(),
229
+ body: score,
230
+ })),
231
+ signal,
232
+ );
233
+ pendingScores.splice(0, scores.length);
234
+ if (errors.length > 0) {
235
+ rememberRuntimeError("score ingestion", new Error(JSON.stringify(errors)));
236
+ console.warn("📊 Langfuse: Score ingestion reported errors", errors);
237
+ }
238
+ } catch (error) {
239
+ if ((error as { name?: string }).name !== "AbortError") {
240
+ rememberRuntimeError("score ingestion", error);
241
+ console.warn("📊 Langfuse: Failed to flush scores", error);
242
+ }
243
+ return;
244
+ }
245
+ }
246
+ }
247
+
248
+ function clearScoreFlushTimer(rt: LangfuseRuntime) {
249
+ if (rt.scoreFlushTimer) {
250
+ clearTimeout(rt.scoreFlushTimer);
251
+ rt.scoreFlushTimer = undefined;
252
+ }
253
+ }
254
+
255
+ function scheduleScoreFlush(rt: LangfuseRuntime) {
256
+ if (
257
+ rt.scoreFlushStopped
258
+ || rt.scoreFlushTimer
259
+ || rt.scoreFlushPromise
260
+ || !rt.pendingScores?.length
261
+ ) {
262
+ return;
263
+ }
264
+
265
+ rt.scoreFlushTimer = setTimeout(() => {
266
+ rt.scoreFlushTimer = undefined;
267
+ void startScoreFlush(rt);
268
+ }, rt.scoreFlushIntervalMs ?? DEFAULT_SCORE_FLUSH_INTERVAL_MS);
269
+ rt.scoreFlushTimer.unref?.();
270
+ }
271
+
272
+ function startScoreFlush(rt: LangfuseRuntime): Promise<void> {
273
+ if (rt.scoreFlushPromise) {
274
+ return rt.scoreFlushPromise;
275
+ }
276
+
277
+ clearScoreFlushTimer(rt);
278
+ const controller = new AbortController();
279
+ const timeout = setTimeout(
280
+ () => controller.abort(new DOMException("Langfuse score request timed out", "AbortError")),
281
+ rt.scoreRequestTimeoutMs ?? DEFAULT_LANGFUSE_REQUEST_TIMEOUT_SECONDS * 1_000,
282
+ );
283
+ timeout.unref?.();
284
+ rt.scoreFlushController = controller;
285
+
286
+ const promise = flushPendingScores(rt, controller.signal).finally(() => {
287
+ clearTimeout(timeout);
288
+ if (rt.scoreFlushPromise === promise) {
289
+ rt.scoreFlushPromise = undefined;
290
+ }
291
+ if (rt.scoreFlushController === controller) {
292
+ rt.scoreFlushController = undefined;
293
+ }
294
+ scheduleScoreFlush(rt);
295
+ });
296
+ rt.scoreFlushPromise = promise;
297
+ return promise;
298
+ }
299
+
300
+ function stopScoreFlush(rt: LangfuseRuntime) {
301
+ rt.scoreFlushStopped = true;
302
+ clearScoreFlushTimer(rt);
303
+ rt.scoreFlushController?.abort(
304
+ new DOMException("Langfuse score flushing stopped", "AbortError"),
305
+ );
306
+ }
307
+
133
308
  function toIso(value: unknown): string | undefined {
134
309
  if (!value) {
135
310
  return undefined;
@@ -258,27 +433,40 @@ function wrapObservation(
258
433
  };
259
434
  }
260
435
 
261
- async function traceExists(rt: LangfuseRuntime, traceId: string): Promise<boolean> {
436
+ async function traceExists(rt: LangfuseRuntime, traceId: string, signal: AbortSignal): Promise<boolean> {
437
+ const config = getRuntimeConfig(rt);
438
+ if (!config) {
439
+ return false;
440
+ }
441
+
262
442
  try {
263
- const traceApi = rt.scoreClient.api?.trace;
264
- if (!traceApi?.get) {
443
+ const response = await fetch(
444
+ `${config.host.replace(/\/$/, "")}/api/public/traces/${encodeURIComponent(traceId)}`,
445
+ {
446
+ headers: ingestionHeaders(rt),
447
+ signal,
448
+ },
449
+ );
450
+ if (response.status === 404) {
265
451
  return false;
266
452
  }
267
- const trace = await withTimeout("Trace visibility check", traceApi.get(traceId));
268
- if (!trace) {
269
- return false;
453
+ if (!response.ok) {
454
+ throw new Error(`Langfuse trace visibility check failed with HTTP ${response.status}`);
270
455
  }
271
456
  return true;
272
- } catch {
457
+ } catch (error) {
458
+ if (signal.aborted) {
459
+ throw error;
460
+ }
273
461
  return false;
274
462
  }
275
463
  }
276
464
 
277
- async function waitForTraceVisibility(rt: LangfuseRuntime, traceId: string): Promise<boolean> {
465
+ async function waitForTraceVisibility(rt: LangfuseRuntime, traceId: string, signal: AbortSignal): Promise<boolean> {
278
466
  const deadline = Date.now() + OTEL_VISIBILITY_TIMEOUT_MS;
279
467
 
280
468
  while (true) {
281
- if (await traceExists(rt, traceId)) {
469
+ if (await traceExists(rt, traceId, signal)) {
282
470
  return true;
283
471
  }
284
472
 
@@ -287,7 +475,7 @@ async function waitForTraceVisibility(rt: LangfuseRuntime, traceId: string): Pro
287
475
  return false;
288
476
  }
289
477
 
290
- await delay(Math.min(OTEL_VISIBILITY_POLL_INTERVAL_MS, remainingMs));
478
+ await delay(Math.min(OTEL_VISIBILITY_POLL_INTERVAL_MS, remainingMs), signal);
291
479
  }
292
480
  }
293
481
 
@@ -295,14 +483,14 @@ function eventTimestamp(record: { endTime?: string; startTime?: string; timestam
295
483
  return record.endTime ?? record.startTime ?? record.timestamp ?? nowIso();
296
484
  }
297
485
 
298
- async function fallbackToRestIngestion(rt: LangfuseRuntime) {
486
+ async function fallbackToRestIngestion(rt: LangfuseRuntime, signal: AbortSignal) {
299
487
  const store = rt.restFallback as RestFallbackStore | undefined;
300
488
  if (!store?.trace || store.attempted) {
301
489
  return;
302
490
  }
303
491
  store.attempted = true;
304
492
 
305
- if (await waitForTraceVisibility(rt, store.trace.id)) {
493
+ if (await waitForTraceVisibility(rt, store.trace.id, signal)) {
306
494
  return;
307
495
  }
308
496
 
@@ -355,31 +543,7 @@ async function fallbackToRestIngestion(rt: LangfuseRuntime) {
355
543
  });
356
544
  }
357
545
 
358
- const ingestionApi = rt.scoreClient.api?.ingestion;
359
- if (!ingestionApi?.batch) {
360
- debugLog("📊 Langfuse: REST fallback ingestion is unavailable");
361
- return;
362
- }
363
-
364
- const response = await withTimeout(
365
- "REST fallback ingestion",
366
- ingestionApi.batch({
367
- batch,
368
- metadata: {
369
- source: "pi-langfuse",
370
- fallback: "rest-ingestion",
371
- reason: "otel-trace-not-visible-after-flush",
372
- },
373
- }),
374
- );
375
-
376
- if (!response) {
377
- return;
378
- }
379
-
380
- const responseBody = response as { errors?: unknown[] } | undefined;
381
- const responseErrors = responseBody?.errors;
382
- const errors = Array.isArray(responseErrors) ? responseErrors : [];
546
+ const errors = await ingestBatch(rt, batch, signal);
383
547
  if (errors.length > 0) {
384
548
  rememberRuntimeError("REST fallback ingestion", new Error(JSON.stringify(errors)));
385
549
  console.warn("📊 Langfuse: REST fallback ingestion reported errors", errors);
@@ -426,6 +590,11 @@ export async function getRuntime(): Promise<LangfuseRuntime> {
426
590
 
427
591
  try {
428
592
  ensureOtelContextManager(context, AsyncHooksContextManager);
593
+ const scoreFlushAt = resolvePositiveEnvNumber("LANGFUSE_FLUSH_AT", DEFAULT_SCORE_FLUSH_AT, true);
594
+ const scoreFlushIntervalMs =
595
+ resolvePositiveEnvNumber("LANGFUSE_FLUSH_INTERVAL", DEFAULT_SCORE_FLUSH_INTERVAL_MS / 1_000) * 1_000;
596
+ const scoreRequestTimeoutMs =
597
+ resolvePositiveEnvNumber("LANGFUSE_TIMEOUT", DEFAULT_LANGFUSE_REQUEST_TIMEOUT_SECONDS) * 1_000;
429
598
  const spanProcessor = new LangfuseSpanProcessor({
430
599
  publicKey: state.config.publicKey,
431
600
  secretKey: state.config.secretKey,
@@ -449,6 +618,16 @@ export async function getRuntime(): Promise<LangfuseRuntime> {
449
618
  tracerProvider,
450
619
  clearTracerProvider: () => tracing.setLangfuseTracerProvider(null),
451
620
  restFallback,
621
+ pendingScores: [],
622
+ scoreFlushAt,
623
+ scoreFlushIntervalMs,
624
+ scoreRequestTimeoutMs,
625
+ scoreFlushStopped: false,
626
+ runtimeConfig: {
627
+ publicKey: state.config.publicKey,
628
+ secretKey: state.config.secretKey,
629
+ host: state.config.host,
630
+ },
452
631
  };
453
632
  lastRuntimeError = null;
454
633
  } catch (e) {
@@ -468,17 +647,44 @@ function doShutdownRuntime(): Promise<void> {
468
647
 
469
648
  const rt = runtime;
470
649
  runtime = null;
650
+ const deadline = Date.now() + shutdownStepTimeoutMs;
651
+ const controller = new AbortController();
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?.();
659
+ stopScoreFlush(rt);
471
660
 
472
661
  try {
473
- await withTimeout("OTel force flush", rt.tracerProvider?.forceFlush?.());
474
- await fallbackToRestIngestion(rt);
475
- await withTimeout("Langfuse score flush", rt.scoreClient.flush?.());
476
- await withTimeout("Langfuse client shutdown", rt.scoreClient.shutdown?.());
477
- await withTimeout("OTel tracer shutdown", rt.tracerProvider?.shutdown?.());
662
+ await withShutdownDeadline(
663
+ "Active score flush",
664
+ () => rt.scoreFlushPromise,
665
+ deadline,
666
+ );
667
+ await flushPendingScores(rt, scoreController.signal);
668
+ await withShutdownDeadline("OTel force flush", () => rt.tracerProvider?.forceFlush?.(), deadline);
669
+ await withShutdownDeadline(
670
+ "REST fallback ingestion",
671
+ () => fallbackToRestIngestion(rt, controller.signal),
672
+ deadline,
673
+ );
674
+ await withShutdownDeadline("Langfuse score flush", () => rt.scoreClient.flush?.(), deadline);
675
+ await withShutdownDeadline("Langfuse client shutdown", () => rt.scoreClient.shutdown?.(), deadline);
676
+ await withShutdownDeadline("OTel tracer shutdown", () => rt.tracerProvider?.shutdown?.(), deadline);
478
677
  } catch (e) {
479
678
  rememberRuntimeError("runtime shutdown", e);
480
679
  console.warn("📊 Langfuse: Failed to flush/shutdown cleanly", e);
481
680
  } finally {
681
+ clearTimeout(abortTimeout);
682
+ clearTimeout(scoreAbortTimeout);
683
+ scoreController.abort();
684
+ clearScoreFlushTimer(rt);
685
+ rt.scoreFlushController?.abort();
686
+ rt.scoreFlushController = undefined;
687
+ rt.scoreFlushPromise = undefined;
482
688
  if (!runtime) {
483
689
  rt.clearTracerProvider?.();
484
690
  }
@@ -516,7 +722,13 @@ export async function forceShutdownRuntime(): Promise<void> {
516
722
  }
517
723
 
518
724
  export function __setRuntimeForTest(rt: LangfuseRuntime | null, timeoutMs = DEFAULT_SHUTDOWN_STEP_TIMEOUT_MS): void {
725
+ if (runtime && runtime !== rt) {
726
+ stopScoreFlush(runtime);
727
+ }
519
728
  runtime = rt;
729
+ if (rt) {
730
+ rt.scoreFlushStopped = false;
731
+ }
520
732
  shutdownStepTimeoutMs = timeoutMs;
521
733
  activeSessions.clear();
522
734
  }
@@ -524,14 +736,35 @@ export function __setRuntimeForTest(rt: LangfuseRuntime | null, timeoutMs = DEFA
524
736
  export async function sendScore(name: string, value: number, options: { traceId?: string; observationId?: string } = {}) {
525
737
  try {
526
738
  const rt = await getRuntime();
527
- rt.scoreClient.score?.create({
739
+ const score: PendingScore = {
528
740
  name,
529
741
  value,
530
742
  dataType: name === "session_had_errors" || name === "tool_is_error" ? "BOOLEAN" : "NUMERIC",
531
743
  traceId: options.traceId,
532
744
  observationId: options.observationId,
533
745
  sessionId: options.traceId ? undefined : state.currentSessionId || undefined,
534
- });
746
+ ...(process.env.LANGFUSE_TRACING_ENVIRONMENT
747
+ ? { environment: process.env.LANGFUSE_TRACING_ENVIRONMENT }
748
+ : {}),
749
+ };
750
+ if (!rt.pendingScores) {
751
+ return;
752
+ }
753
+ if (rt.pendingScores.length >= MAX_SCORE_QUEUE_SIZE) {
754
+ const error = new Error(
755
+ `Langfuse score queue is full (${MAX_SCORE_QUEUE_SIZE}); dropping score`,
756
+ );
757
+ rememberRuntimeError("score queue", error);
758
+ console.warn(`📊 Langfuse: ${error.message}`);
759
+ return;
760
+ }
761
+
762
+ rt.pendingScores.push(score);
763
+ if (rt.pendingScores.length >= (rt.scoreFlushAt ?? DEFAULT_SCORE_FLUSH_AT)) {
764
+ void startScoreFlush(rt);
765
+ } else {
766
+ scheduleScoreFlush(rt);
767
+ }
535
768
  } catch (e) {
536
769
  rememberRuntimeError(`score ${name}`, e);
537
770
  console.warn(`📊 Langfuse: Failed to send score ${name}`, e);
package/src/types.ts CHANGED
@@ -59,6 +59,22 @@ export interface LangfuseScoreClient {
59
59
  shutdown?: () => Promise<void>;
60
60
  }
61
61
 
62
+ export interface PendingScore {
63
+ traceId?: string;
64
+ sessionId?: string;
65
+ observationId?: string;
66
+ name: string;
67
+ value: number;
68
+ dataType?: "NUMERIC" | "BOOLEAN";
69
+ environment?: string;
70
+ }
71
+
72
+ export interface LangfuseRuntimeConfig {
73
+ publicKey: string;
74
+ secretKey: string;
75
+ host: string;
76
+ }
77
+
62
78
  export interface LangfuseRuntime {
63
79
  startObservation: (
64
80
  name: string,
@@ -79,6 +95,15 @@ export interface LangfuseRuntime {
79
95
  tracerProvider?: { forceFlush?: () => Promise<void>; shutdown?: () => Promise<void> };
80
96
  clearTracerProvider?: () => void;
81
97
  restFallback?: unknown;
98
+ pendingScores?: PendingScore[];
99
+ scoreFlushAt?: number;
100
+ scoreFlushIntervalMs?: number;
101
+ scoreRequestTimeoutMs?: number;
102
+ scoreFlushTimer?: NodeJS.Timeout;
103
+ scoreFlushPromise?: Promise<void>;
104
+ scoreFlushController?: AbortController;
105
+ scoreFlushStopped?: boolean;
106
+ runtimeConfig?: LangfuseRuntimeConfig;
82
107
  }
83
108
 
84
109
  export interface GenerationState {