import { bench, describe } from "vitest"; import { runImmediatePipeline, runDeferredPipeline, type PipelineConfig, type Message, } from "../../extensions/compress/pipeline.js"; import { compilePathEntries } from "../../extensions/compress/stages/paths.js"; // ── Fixtures ────────────────────────────────────────────────────────────────── const WS = "/Users/testuser/projects/my-long-workspace-path"; function makeConfig(): PipelineConfig { const entries = [{ path: WS, sigil: "$WS" }]; return { pathEntries: entries, compiledPaths: compilePathEntries(entries), blockCache: new WeakMap(), }; } function makeToolResult(text: string): Message { return { role: "toolResult", content: [{ type: "text", text }] }; } /** Large dirty tool_result: ANSI + trailing spaces */ const DIRTY_OUTPUT = Array.from( { length: 100 }, (_, i) => `\x1b[32m✓\x1b[0m ${WS}/src/module${i}.ts: ok `, ).join("\n"); /** Clean tool_result: no ANSI, no trailing spaces */ const CLEAN_OUTPUT = Array.from( { length: 100 }, (_, i) => `Result ${i}: ${WS}/src/module${i}.ts`, ).join("\n"); /** Highly compressible: repeated lines + paths + separators */ const COMPRESSIBLE_OUTPUT = Array.from({ length: 20 }, () => `Error: connection refused at ${WS}/src/db.ts`).join("\n") + "\n" + "─".repeat(80) + "\n" + Array.from({ length: 20 }, () => `Warning: deprecated method at ${WS}/src/utils.ts`).join("\n"); /** Full conversation context: mix of user/assistant/toolResult messages */ function makeConversation(toolResultCount: number, textSize: number): Message[] { const msgs: Message[] = []; for (let i = 0; i < toolResultCount; i++) { msgs.push({ role: "user", content: [{ type: "text", text: `Query ${i}` }] }); msgs.push(makeToolResult(COMPRESSIBLE_OUTPUT.slice(0, textSize))); msgs.push({ role: "assistant", content: [{ type: "text", text: `Response ${i}` }] }); } return msgs; } // ── Benchmarks ──────────────────────────────────────────────────────────────── describe("runImmediatePipeline", () => { bench("dirty output with ANSI + trailing spaces (~7KB)", () => { runImmediatePipeline(DIRTY_OUTPUT, makeConfig()); }); bench("clean output (minimal changes needed)", () => { runImmediatePipeline(CLEAN_OUTPUT, makeConfig()); }); bench("small input (< MIN_IMMEDIATE_SIZE, still processes)", () => { runImmediatePipeline("short output", makeConfig()); }); }); describe("runDeferredPipeline — short conversation (5 tool results)", () => { const config = makeConfig(); const msgs5small = makeConversation(5, 500); const msgs5large = makeConversation(5, 3000); bench("5 tool results × 500 chars", () => { runDeferredPipeline(msgs5small, config); }); bench("5 tool results × 3KB", () => { runDeferredPipeline(msgs5large, config); }); }); describe("runDeferredPipeline — long conversation (20 tool results)", () => { const config = makeConfig(); const msgs20 = makeConversation(20, 1000); bench("20 tool results × 1KB (no cache)", () => { runDeferredPipeline(msgs20, makeConfig()); }); bench("20 tool results × 1KB (warm cache — same msgs)", () => { // First pass fills the cache const r1 = runDeferredPipeline(msgs20, config); // Subsequent passes hit the cache runDeferredPipeline(r1.messages, config); }); }); describe("runDeferredPipeline — cache effectiveness", () => { bench("second pass with warm cache vs no cache", () => { const config = makeConfig(); const msgs = makeConversation(10, 2000); // First pass const r1 = runDeferredPipeline(msgs, config); // Second pass (cache warm) runDeferredPipeline(r1.messages, config); }); });