import { describe, expect, it } from "vitest";
import { Usage } from "../src/client.js";
import {
appendCacheDiagnostic,
buildCacheDiagnostic,
inferCacheMissReason,
prefixDiagnosticHashes,
renderCacheMissReport,
} from "../src/telemetry/cache-diagnostics.js";
import type { ToolSpec } from "../src/types.js";
function tool(name: string, parameters: object = { type: "object" }): ToolSpec {
return {
type: "function",
function: { name, description: "", parameters },
};
}
function usage(hit: number, miss: number): Usage {
return new Usage(hit + miss, 10, hit + miss + 10, hit, miss);
}
describe("cache diagnostics", () => {
it("breaks the immutable prefix into stable sub-hashes", () => {
const a = prefixDiagnosticHashes({
system: "s",
toolSpecs: [tool("read"), tool("write")],
fewShots: [],
});
const b = prefixDiagnosticHashes({
system: "s",
toolSpecs: [tool("read"), tool("write")],
fewShots: [],
});
expect(a).toEqual(b);
expect(a.toolNames).toEqual(["read", "write"]);
expect(a.prefixHash).toHaveLength(16);
});
it("infers local miss reasons from prefix evidence", () => {
const previous = buildCacheDiagnostic({
turn: 1,
model: "deepseek-v4-flash",
usage: usage(0, 100),
estimatedCostUsd: 0.001,
prefix: prefixDiagnosticHashes({ system: "s", toolSpecs: [tool("read")], fewShots: [] }),
});
const current = prefixDiagnosticHashes({
system: "s",
toolSpecs: [tool("read", { type: "object", properties: { path: { type: "string" } } })],
fewShots: [],
});
expect(inferCacheMissReason(previous, current, usage(50, 50))).toMatchObject({
reason: "tool-schema-or-order-changed",
});
expect(
inferCacheMissReason(
previous,
prefixDiagnosticHashes({ system: "s2", toolSpecs: [tool("read")], fewShots: [] }),
usage(50, 50),
),
).toMatchObject({ reason: "system-prompt-changed" });
});
it("renders the DeepSeek/inferred caveat in reports", () => {
const entry = buildCacheDiagnostic({
turn: 1,
model: "deepseek-v4-flash",
usage: usage(90, 10),
estimatedCostUsd: 0.001,
prefix: prefixDiagnosticHashes({ system: "s", toolSpecs: [tool("read")], fewShots: [] }),
});
const report = renderCacheMissReport(appendCacheDiagnostic([], entry));
expect(report).toContain("DeepSeek does not return a cache-miss reason");
expect(report).toContain("Reasonix infers");
expect(report).toContain("cached 90");
});
});