SPB Git

spb/khaelor Public

KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.

TypeScript 82.9% HTML 14.9% CSS 1.1% JavaScript 0.7%
4.7 KB · 123 lines typescript
Raw Blame History
1/**2 * KHAELOR3 * File: tests/anthropic/caching.test.ts4 * Description: Cache breakpoint planning tests — tier-end breakpoints, last-user-message breakpoints, 4-breakpoint budget.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { describe, expect, it } from "vitest";11import {12  MAX_CACHE_BREAKPOINTS,13  buildMessagesWithCacheControl,14  buildSystemBlocks,15  planCacheBreakpoints,16} from "../../src/anthropic/caching.js";17import type { AnthropicMessage, SystemTier } from "../../src/anthropic/types.js";1819const TIERS: SystemTier[] = [20  { name: "identity", text: "You are KHAELOR." },21  { name: "tool-guidance", text: "Use tools carefully." },22  { name: "project-instructions", text: "Follow KHAELOR.md." },23];2425function user(text: string): AnthropicMessage {26  return { role: "user", content: [{ type: "text", text }] };27}28function assistant(text: string): AnthropicMessage {29  return { role: "assistant", content: [{ type: "text", text }] };30}3132describe("planCacheBreakpoints", () => {33  it("marks the first and last system tier ends, plus the last two user messages", () => {34    const messages = [user("a"), assistant("b"), user("c"), assistant("d"), user("e")];35    const plan = planCacheBreakpoints({ system: TIERS, messages });36    expect(plan.systemTierIndices).toEqual([0, 2]);37    expect(plan.messageIndices).toEqual([2, 4]);38    expect(plan.systemTierIndices.length + plan.messageIndices.length).toBeLessThanOrEqual(39      MAX_CACHE_BREAKPOINTS,40    );41  });4243  it("uses a single breakpoint for a single system tier", () => {44    const plan = planCacheBreakpoints({ system: [TIERS[0] as SystemTier], messages: [user("a")] });45    expect(plan.systemTierIndices).toEqual([0]);46    expect(plan.messageIndices).toEqual([0]);47  });4849  it("handles empty system and empty messages", () => {50    const plan = planCacheBreakpoints({ system: [], messages: [] });51    expect(plan.systemTierIndices).toEqual([]);52    expect(plan.messageIndices).toEqual([]);53  });5455  it("is deterministic — same input shape produces the same plan", () => {56    const messages = [user("a"), user("b"), user("c")];57    const first = planCacheBreakpoints({ system: TIERS, messages });58    const second = planCacheBreakpoints({ system: TIERS, messages });59    expect(second).toEqual(first);60  });61});6263describe("buildSystemBlocks", () => {64  it("renders tiers verbatim with cache_control only at planned tier ends", () => {65    const plan = planCacheBreakpoints({ system: TIERS, messages: [] });66    const blocks = buildSystemBlocks(TIERS, plan);67    expect(blocks.map((b) => b.text)).toEqual(TIERS.map((t) => t.text)); // byte-stable68    expect(blocks[0]?.cache_control).toEqual({ type: "ephemeral" });69    expect(blocks[1]?.cache_control).toBeUndefined();70    expect(blocks[2]?.cache_control).toEqual({ type: "ephemeral" });71  });72});7374describe("buildMessagesWithCacheControl", () => {75  it("marks the last cacheable block of planned messages without mutating the input", () => {76    const messages = [user("a"), assistant("b"), user("c")];77    const plan = planCacheBreakpoints({ system: [], messages });78    const rendered = buildMessagesWithCacheControl(messages, plan);7980    const last = rendered[2]?.content[0];81    expect(last).toMatchObject({ type: "text", cache_control: { type: "ephemeral" } });82    // Input history untouched (byte-stability of the projection).83    expect(messages[2]?.content[0]).toEqual({ type: "text", text: "c" });84    // Unmarked messages pass through by reference.85    expect(rendered[1]).toBe(messages[1]);86  });8788  it("places the breakpoint on tool_result blocks (the common tool-loop tail)", () => {89    const messages: AnthropicMessage[] = [90      user("start"),91      {92        role: "user",93        content: [{ type: "tool_result", tool_use_id: "toolu_1", content: "ok" }],94      },95    ];96    const plan = planCacheBreakpoints({ system: [], messages });97    const rendered = buildMessagesWithCacheControl(messages, plan);98    expect(rendered[1]?.content[0]).toMatchObject({99      type: "tool_result",100      cache_control: { type: "ephemeral" },101    });102  });103104  it("skips non-cacheable blocks (thinking) when choosing the breakpoint block", () => {105    const messages: AnthropicMessage[] = [106      {107        role: "user",108        content: [109          { type: "text", text: "before" },110          { type: "thinking", thinking: "t", signature: "s" },111        ],112      },113    ];114    const plan = planCacheBreakpoints({ system: [], messages });115    const rendered = buildMessagesWithCacheControl(messages, plan);116    expect(rendered[0]?.content[0]).toMatchObject({117      type: "text",118      cache_control: { type: "ephemeral" },119    });120    expect(rendered[0]?.content[1]).not.toHaveProperty("cache_control");121  });122});123