diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index de4b63a1e1..81c69ae8e5 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -166,6 +166,7 @@ import { mergeResponseToolNameMap, } from "./chatCore/passthroughToolNames.ts"; import { recordContextEditingTelemetryHook } from "./chatCore/contextEditingTelemetry.ts"; +import { recordCompressionCacheStats } from "./chatCore/compressionCacheStats.ts"; import { appendNonStreamingSseTerminalSignal, type NonStreamingSseTerminalState, @@ -1362,39 +1363,15 @@ export async function handleChatCore({ } if (result.compressed) { - void (async () => { - try { - const { detectCachingContext } = - await import("../services/compression/cachingAware.ts"); - const { recordCacheStats } = - await import("../../src/lib/db/compressionCacheStats.ts"); - const cacheContext = detectCachingContext(compressionInputBody, { - provider, - targetFormat, - model: effectiveModel, - }); - const tokensSavedCompression = Math.max( - 0, - result.stats.originalTokens - result.stats.compressedTokens - ); - recordCacheStats({ - provider: cacheContext.provider ?? provider ?? "unknown", - model: effectiveModel ?? "", - compressionMode: mode, - cacheControlPresent: cacheContext.hasCacheControl, - estimatedCacheHit: cacheContext.hasCacheControl && cacheContext.isCachingProvider, - tokensSavedCompression, - tokensSavedCaching: 0, - netSavings: tokensSavedCompression, - }); - } catch (err) { - log?.debug?.( - "COMPRESSION", - "Compression cache stats write skipped: " + - (err instanceof Error ? err.message : String(err)) - ); - } - })(); + recordCompressionCacheStats({ + compressionInputBody, + provider, + targetFormat, + effectiveModel, + mode, + stats: result.stats, + log, + }); log?.info?.( "COMPRESSION", `Prompt compressed (${mode}): ${result.stats.originalTokens} -> ${result.stats.compressedTokens} tokens (${result.stats.savingsPercent}% saved, techniques: ${result.stats.techniquesUsed.join(",")})` diff --git a/open-sse/handlers/chatCore/compressionCacheStats.ts b/open-sse/handlers/chatCore/compressionCacheStats.ts new file mode 100644 index 0000000000..06bca10837 --- /dev/null +++ b/open-sse/handlers/chatCore/compressionCacheStats.ts @@ -0,0 +1,53 @@ +/** + * chatCore compression cache-stats hook (Quality Gate v2 / Fase 9 — chatCore god-file + * decomposition, #3501). + * + * Extracted from handleChatCore's request-setup compression path: when a prompt was compressed, + * record the caching-context cache-stats receipt (estimated cache hit + tokens saved). Best-effort, + * fire-and-forget — the inner work is an un-awaited IIFE that swallows its own errors and never + * affects the request. Behaviour is byte-identical to the previous inline block. + */ + +type LoggerLike = { debug?: (...args: unknown[]) => void } | null | undefined; + +export function recordCompressionCacheStats(args: { + compressionInputBody: unknown; + provider: string | null | undefined; + targetFormat: string | null | undefined; + effectiveModel: string | null | undefined; + mode: string; + stats: { originalTokens: number; compressedTokens: number }; + log?: LoggerLike; +}): void { + void (async () => { + try { + const { detectCachingContext } = await import("../../services/compression/cachingAware.ts"); + const { recordCacheStats } = await import("@/lib/db/compressionCacheStats"); + const cacheContext = detectCachingContext(args.compressionInputBody, { + provider: args.provider, + targetFormat: args.targetFormat, + model: args.effectiveModel, + }); + const tokensSavedCompression = Math.max( + 0, + args.stats.originalTokens - args.stats.compressedTokens + ); + recordCacheStats({ + provider: cacheContext.provider ?? args.provider ?? "unknown", + model: args.effectiveModel ?? "", + compressionMode: args.mode, + cacheControlPresent: cacheContext.hasCacheControl, + estimatedCacheHit: cacheContext.hasCacheControl && cacheContext.isCachingProvider, + tokensSavedCompression, + tokensSavedCaching: 0, + netSavings: tokensSavedCompression, + }); + } catch (err) { + args.log?.debug?.( + "COMPRESSION", + "Compression cache stats write skipped: " + + (err instanceof Error ? err.message : String(err)) + ); + } + })(); +} diff --git a/tests/unit/chatcore-compression-cache-stats.test.ts b/tests/unit/chatcore-compression-cache-stats.test.ts new file mode 100644 index 0000000000..fe1e3904b2 --- /dev/null +++ b/tests/unit/chatcore-compression-cache-stats.test.ts @@ -0,0 +1,90 @@ +// Characterization of recordCompressionCacheStats — the compression cache-stats hook extracted +// from handleChatCore's request-setup compression path (chatCore god-file decomposition, #3501). +// Fire-and-forget; uses a real temp DB and polls compression_cache_stats. Locks: a compressed +// prompt records a cache-stats row with the resolved provider/mode/tokens-saved, and the helper +// returns synchronously without throwing (fail-open). +import { test, before, after } from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +const testDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "omni-comp-cache-test-")); +process.env.DATA_DIR = testDataDir; + +const coreDb = await import("../../src/lib/db/core.ts"); +const { recordCompressionCacheStats } = await import( + "../../open-sse/handlers/chatCore/compressionCacheStats.ts" +); + +function rowsFor(provider: string): Array> { + return coreDb + .getDbInstance() + .prepare( + "SELECT provider, compression_mode AS mode, tokens_saved_compression AS saved FROM compression_cache_stats WHERE provider = ?" + ) + .all(provider) as Array>; +} + +async function waitForRows(provider: string, min: number, timeoutMs = 3000) { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + if (rowsFor(provider).length >= min) break; + await new Promise((r) => setTimeout(r, 25)); + } + return rowsFor(provider); +} + +before(async () => { + await coreDb.ensureDbInitialized(); +}); + +after(() => { + coreDb.resetDbInstance(); + try { + fs.rmSync(testDataDir, { recursive: true, force: true }); + } catch { + // best-effort cleanup + } +}); + +test("returns synchronously without throwing (fire-and-forget)", () => { + assert.doesNotThrow(() => + recordCompressionCacheStats({ + compressionInputBody: { messages: [{ role: "user", content: "hi" }] }, + provider: "openai", + targetFormat: "openai", + effectiveModel: "gpt-x", + mode: "balanced", + stats: { originalTokens: 100, compressedTokens: 60 }, + }) + ); +}); + +test("records a cache-stats row with the resolved provider/mode/tokens-saved", async () => { + recordCompressionCacheStats({ + compressionInputBody: { messages: [{ role: "user", content: "hello world" }] }, + provider: "ccs-prov", + targetFormat: "openai", + effectiveModel: "gpt-ccs", + mode: "aggressive", + stats: { originalTokens: 200, compressedTokens: 75 }, + }); + const rows = await waitForRows("ccs-prov", 1); + assert.equal(rows.length, 1); + assert.equal(rows[0].mode, "aggressive"); + assert.equal(rows[0].saved, 125); +}); + +test("clamps negative tokens-saved to 0", async () => { + recordCompressionCacheStats({ + compressionInputBody: { messages: [] }, + provider: "ccs-neg", + targetFormat: "openai", + effectiveModel: "gpt-x", + mode: "balanced", + stats: { originalTokens: 50, compressedTokens: 80 }, + }); + const rows = await waitForRows("ccs-neg", 1); + assert.equal(rows[0].saved, 0); +});