diff --git a/eval/runner/adapters/hybrid-nograph.ts b/eval/runner/adapters/hybrid-nograph.ts index 5c99a36e7..be5cde0eb 100644 --- a/eval/runner/adapters/hybrid-nograph.ts +++ b/eval/runner/adapters/hybrid-nograph.ts @@ -76,6 +76,11 @@ export class HybridNoGraphAdapter implements Adapter { return { engine } satisfies HybridNoGraphState; } + async teardown(state: BrainState): Promise { + const s = state as HybridNoGraphState; + await s.engine.disconnect(); + } + /** Build a markdown string importFromContent can parse. * Format: YAML frontmatter then body; matches what gbrain import expects. */ private buildContentMarkdown(p: Page): string { diff --git a/eval/runner/multi-adapter.ts b/eval/runner/multi-adapter.ts index 8247614ee..4aa4782c3 100644 --- a/eval/runner/multi-adapter.ts +++ b/eval/runner/multi-adapter.ts @@ -217,6 +217,11 @@ class GbrainAfterAdapter implements Adapter { rank: i + 1, })); } + + async teardown(state: unknown): Promise { + const { engine } = state as { engine: PGLiteEngine }; + await engine.disconnect(); + } } /** @@ -340,6 +345,7 @@ async function scoreOneRun( for (const r of topK) if (relevant.has(r.page_id)) totalCorrect++; totalExpected += relevant.size; } + if (adapter.teardown) await adapter.teardown(state); return { mean_precision_at_k: queries.length > 0 ? totalP / queries.length : 0, mean_recall_at_k: queries.length > 0 ? totalR / queries.length : 0, diff --git a/eval/runner/types.ts b/eval/runner/types.ts index af53fc81c..36a3694d2 100644 --- a/eval/runner/types.ts +++ b/eval/runner/types.ts @@ -179,6 +179,14 @@ export interface Adapter { * Adapters that don't have a poison path return an empty map. */ getPoisonDisposition?(state: BrainState): Record; + + /** + * Release any resources held by `state` (DB connections, file locks, + * worker threads). Called once per run after scoring completes. + * Adapters that hold no resources can omit this. Without it, PGLite-backed + * adapters leak engine workers and Bun exits 99 at the end of the run. + */ + teardown?(state: BrainState): Promise; } // ─── Scorer helpers ─────────────────────────────────────────────────