Files
OmniRoute/tests/unit/compression/progressiveAging.test.ts
oyi77 6bf7b9601d feat(compression): Phase 3 — aggressive mode with summarization, tool compression, and progressive aging
Implements the aggressive compression pipeline (issue #1588) with three
core stages: tool-result compression, progressive aging, and rule-based
summarization. Includes downgrade chain to caveman/lite when savings are
insufficient.

Core modules:
- summarizer.ts: RuleBasedSummarizer with intent/file/error/decision
  extraction, code fence trimming, skip-already-compressed guard
- toolResultCompressor.ts: 5 auto-detected strategies (fileContent,
  grepSearch, shellOutput, json, errorMessage) with toggle support
- progressiveAging.ts: 4-tier degradation (verbatim→light→moderate→
  fullSummary) with [COMPRESSED:aging:<tier>] markers
- aggressive.ts: Orchestrator with 3-step pipeline and downgrade chain
  (aggressive→caveman→lite→as-is)

Integration:
- strategySelector.ts: applyCompression() now dispatches mode='aggressive'
- compression.ts: aggressiveConfig CRUD with deep merge of nested objects
- API route: Zod schema for aggressiveConfig thresholds/strategies
- CompressionSettingsTab: aggressive mode card, thresholds, toggles, i18n
- Migration 031: SELECT 1 no-op (config stored as kv key)

Tests: 109 across 8 files (types, summarizer, toolResultCompressor,
progressiveAging, aggressive, integration, golden eval, caveman regression)
All pass. Typecheck clean. Lint clean (0 errors). Build succeeds.
2026-04-28 16:58:17 +07:00

175 lines
6.3 KiB
TypeScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { applyAging } from "../../../open-sse/services/compression/progressiveAging.ts";
import type { AgingThresholds, Summarizer } from "../../../open-sse/services/compression/types.ts";
function makeMessages(
count: number,
role: string = "user",
prefix: string = "Message"
): Array<{ role: string; content: string }> {
return Array.from({ length: count }, (_, i) => ({
role: i % 2 === 0 ? "user" : "assistant",
content: `${prefix} ${i}: ${"x".repeat(100)}`,
}));
}
describe("applyAging", () => {
const defaultThresholds: AgingThresholds = { fullSummary: 5, moderate: 3, light: 2, verbatim: 2 };
it("returns empty array for empty input", () => {
const result = applyAging([]);
assert.deepEqual(result.messages, []);
assert.equal(result.saved, 0);
});
it("returns single message unchanged (verbatim tier)", () => {
const msgs = [{ role: "user", content: "Hello world" }];
const result = applyAging(msgs, defaultThresholds);
assert.equal(result.messages.length, 1);
assert.equal((result.messages[0] as { content: string }).content, "Hello world");
});
it("keeps last 2 messages as verbatim", () => {
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds);
const last2 = result.messages.slice(-2);
for (const msg of last2) {
const content =
typeof (msg as { content: unknown }).content === "string"
? (msg as { content: string }).content
: "";
assert.ok(
!content.startsWith("[COMPRESSED:aging:"),
`Last message should be verbatim, got: ${content.slice(0, 40)}`
);
}
});
it("applies fullSummary tier to oldest messages", () => {
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds);
const first = result.messages[0] as { content: string };
const content = typeof first.content === "string" ? first.content : "";
assert.ok(
content.startsWith("[COMPRESSED:aging:fullSummary]"),
`Expected fullSummary marker, got: ${content.slice(0, 60)}`
);
});
it("skips messages with [COMPRESSED: prefix (idempotent)", () => {
const msgs: Array<{ role: string; content: string }> = [
{ role: "assistant", content: "[COMPRESSED:aging:fullSummary] prior summary" },
{ role: "user", content: "New message" },
];
const result = applyAging(msgs, defaultThresholds);
const first = result.messages[0] as { content: string };
const content = typeof first.content === "string" ? first.content : "";
assert.ok(content.startsWith("[COMPRESSED:aging:fullSummary]"));
assert.ok(!content.includes("[COMPRESSED:aging:fullSummary][COMPRESSED:"));
});
it("uses custom thresholds", () => {
const customThresholds: AgingThresholds = {
fullSummary: 1,
moderate: 1,
light: 1,
verbatim: 1,
};
const msgs = makeMessages(6);
const result = applyAging(msgs, customThresholds);
assert.ok(result.messages.length > 0);
});
it("accepts custom summarizer", () => {
const customSummarizer: Summarizer = {
summarize: () => "[CUSTOM SUMMARY]",
};
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds, customSummarizer);
const fullSummaryMsgs = result.messages.filter((m) => {
const content =
typeof (m as { content: unknown }).content === "string"
? (m as { content: string }).content
: "";
return content.includes("[CUSTOM SUMMARY]");
});
assert.ok(fullSummaryMsgs.length > 0, "Expected at least one message with custom summary");
});
it("computes saved tokens", () => {
const msgs = makeMessages(10);
const result = applyAging(msgs, defaultThresholds);
assert.ok(typeof result.saved === "number");
});
it("handles 2-message conversation (all verbatim)", () => {
const msgs = [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there" },
];
const result = applyAging(msgs, defaultThresholds);
assert.equal(result.messages.length, 2);
for (const msg of result.messages) {
const content =
typeof (msg as { content: unknown }).content === "string"
? (msg as { content: string }).content
: "";
assert.ok(!content.startsWith("[COMPRESSED:"));
}
});
it("handles 5-message conversation with light tier", () => {
const msgs = makeMessages(5);
const thresholds: AgingThresholds = { fullSummary: 10, moderate: 10, light: 3, verbatim: 1 };
const result = applyAging(msgs, thresholds);
assert.ok(result.messages.length > 0);
});
it("handles messages with array content", () => {
const msgs = [{ role: "user", content: [{ type: "text", text: "Hello from array" }] }];
const result = applyAging(msgs, defaultThresholds);
assert.equal(result.messages.length, 1);
});
it("does not mutate original messages array", () => {
const original = makeMessages(5);
const originalCopy = JSON.parse(JSON.stringify(original));
applyAging(original, defaultThresholds);
assert.deepEqual(original, originalCopy);
});
it("handles all-assistant messages", () => {
const msgs = Array.from({ length: 8 }, (_, i) => ({
role: "assistant" as const,
content: `Response ${i}: ${"y".repeat(100)}`,
}));
const result = applyAging(msgs, defaultThresholds);
assert.ok(result.messages.length > 0);
});
it("handles mixed user/assistant/tool messages", () => {
const msgs = [
{ role: "user", content: "Fix the bug" },
{ role: "assistant", content: "I'll fix it" },
{ role: "tool", content: "Result: fixed" },
{ role: "assistant", content: "Done" },
];
const result = applyAging(msgs, defaultThresholds);
assert.ok(result.messages.length > 0);
});
it("idempotent — running twice produces same output", () => {
const msgs = makeMessages(10);
const first = applyAging(msgs, defaultThresholds);
const second = applyAging(first.messages, defaultThresholds);
const firstContent = JSON.stringify(
first.messages.map((m) => (m as { content: string }).content)
);
const secondContent = JSON.stringify(
second.messages.map((m) => (m as { content: string }).content)
);
assert.equal(firstContent, secondContent);
});
});