314 lines
11 KiB
TypeScript
314 lines
11 KiB
TypeScript
/**
|
|
* TV1 — Transversal compression bail-out discipline (OPT-IN).
|
|
*
|
|
* Proves:
|
|
* 1. An engine that THROWS in apply() → step skipped (no throw, original body kept).
|
|
* 2. An engine whose gain is < minGainPercent (10%) → step skipped.
|
|
* 3. An engine with gain ≥ 10% → applied normally.
|
|
* 4. With bail-out DISABLED (default) → a <10%-gain engine IS applied
|
|
* (proving that opt-in default never changes existing behaviour).
|
|
*/
|
|
import { describe, it, before, after } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
applyStackedCompression,
|
|
applyStackedCompressionAsync,
|
|
} from "../../../open-sse/services/compression/index.ts";
|
|
import {
|
|
registerCompressionEngine,
|
|
unregisterCompressionEngine,
|
|
} from "../../../open-sse/services/compression/engines/registry.ts";
|
|
import type {
|
|
CompressionEngine,
|
|
CompressionEngineTarget,
|
|
} from "../../../open-sse/services/compression/engines/types.ts";
|
|
import type {
|
|
CompressionPipelineStep,
|
|
CompressionResult,
|
|
} from "../../../open-sse/services/compression/types.ts";
|
|
|
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
|
|
/** Base skeleton shared by all fake engines. */
|
|
function makeBaseEngine(id: string): Omit<CompressionEngine, "apply"> {
|
|
return {
|
|
id,
|
|
name: id,
|
|
description: id,
|
|
icon: "x",
|
|
targets: ["messages"] as CompressionEngineTarget[],
|
|
stackable: true,
|
|
stackPriority: 0,
|
|
metadata: {
|
|
id,
|
|
name: id,
|
|
description: id,
|
|
inputScope: "messages",
|
|
targetLatencyMs: 1,
|
|
supportsPreview: false,
|
|
stable: true,
|
|
},
|
|
compress: (body) => ({ body, compressed: false, stats: null }),
|
|
getConfigSchema: () => [],
|
|
validateConfig: () => ({ valid: true, errors: [] }),
|
|
};
|
|
}
|
|
|
|
/** Engine that always throws during apply(). */
|
|
const THROW_ENGINE_ID = "bailout-throw-engine";
|
|
const throwEngine: CompressionEngine = {
|
|
...makeBaseEngine(THROW_ENGINE_ID),
|
|
apply: (_body) => {
|
|
throw new Error("simulated engine failure");
|
|
},
|
|
};
|
|
|
|
/** Engine that returns a low gain (5% < 10% threshold). */
|
|
const LOW_GAIN_ENGINE_ID = "bailout-low-gain-engine";
|
|
function makeLowGainEngine(id = LOW_GAIN_ENGINE_ID): CompressionEngine {
|
|
return {
|
|
...makeBaseEngine(id),
|
|
apply: (body) => {
|
|
const messages = (body.messages as Array<{ role: string; content: string }>) ?? [];
|
|
// Tag the user message; drop any padding (non-user) content so the body shrinks.
|
|
const next = messages.map((m) =>
|
|
m.role === "user" ? { ...m, content: m.content + "|low" } : { ...m, content: "" }
|
|
);
|
|
return {
|
|
body: { ...body, messages: next },
|
|
compressed: true,
|
|
stats: {
|
|
originalTokens: 100,
|
|
compressedTokens: 95,
|
|
savingsPercent: 5, // 5% < 10% threshold
|
|
techniquesUsed: [id],
|
|
mode: "stacked",
|
|
timestamp: 0,
|
|
durationMs: 0.1,
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Engine that returns a high gain (20% ≥ 10% threshold). */
|
|
const HIGH_GAIN_ENGINE_ID = "bailout-high-gain-engine";
|
|
const highGainEngine: CompressionEngine = {
|
|
...makeBaseEngine(HIGH_GAIN_ENGINE_ID),
|
|
apply: (body) => {
|
|
const messages = (body.messages as Array<{ role: string; content: string }>) ?? [];
|
|
// Tag the user message; drop any padding (non-user) content so the body shrinks.
|
|
const next = messages.map((m) =>
|
|
m.role === "user" ? { ...m, content: m.content + "|high" } : { ...m, content: "" }
|
|
);
|
|
return {
|
|
body: { ...body, messages: next },
|
|
compressed: true,
|
|
stats: {
|
|
originalTokens: 100,
|
|
compressedTokens: 80,
|
|
savingsPercent: 20, // 20% ≥ 10% threshold
|
|
techniquesUsed: [HIGH_GAIN_ENGINE_ID],
|
|
mode: "stacked",
|
|
timestamp: 0,
|
|
durationMs: 0.1,
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
/** Async variant of the low-gain engine. */
|
|
const LOW_GAIN_ASYNC_ID = "bailout-low-gain-async";
|
|
const lowGainAsyncEngine: CompressionEngine = {
|
|
...makeLowGainEngine(LOW_GAIN_ASYNC_ID),
|
|
apply: (body) => ({ body, compressed: false, stats: null }), // sync pass-through (async-only pattern)
|
|
applyAsync: async (body) => makeLowGainEngine(LOW_GAIN_ASYNC_ID).apply(body),
|
|
};
|
|
|
|
/** Async variant of the throw engine. */
|
|
const THROW_ASYNC_ID = "bailout-throw-async";
|
|
const throwAsyncEngine: CompressionEngine = {
|
|
...makeBaseEngine(THROW_ASYNC_ID),
|
|
apply: (body) => ({ body, compressed: false, stats: null }), // sync pass-through
|
|
applyAsync: async (_body) => {
|
|
throw new Error("simulated async engine failure");
|
|
},
|
|
};
|
|
|
|
function pipeline(...ids: string[]): CompressionPipelineStep[] {
|
|
return ids.map((engine) => ({ engine })) as unknown as CompressionPipelineStep[];
|
|
}
|
|
|
|
function userContent(result: CompressionResult): string {
|
|
const messages = result.body.messages as Array<{ role: string; content: string }>;
|
|
return messages.find((m) => m.role === "user")!.content;
|
|
}
|
|
|
|
const BAILOUT_ON = { bailout: { enabled: true, minGainPercent: 10 } };
|
|
const BAILOUT_OFF = {}; // default — no bailout field
|
|
|
|
// A fixture body carrying a large droppable padding message. When an engine runs it
|
|
// drops that padding (and tags the user message), so the FINAL stacked body genuinely
|
|
// shrinks — keeping the #5527 (T02) inflation guard from reverting these advancement
|
|
// fixtures. The guard only reverts a pipeline whose final body did NOT shrink in tokens;
|
|
// these tests exercise bail-out/advancement, an orthogonal concern, so the body must
|
|
// realistically shrink for the marker assertions to survive the guard.
|
|
const PADDING = "padding tokens ".repeat(40);
|
|
function mkBody(): { messages: Array<{ role: string; content: string }> } {
|
|
return {
|
|
messages: [
|
|
{ role: "user", content: "hello" },
|
|
{ role: "assistant", content: PADDING },
|
|
],
|
|
};
|
|
}
|
|
|
|
// ── suite ────────────────────────────────────────────────────────────────────
|
|
|
|
describe("TV1 — stacked pipeline bail-out discipline (OPT-IN)", () => {
|
|
before(() => {
|
|
registerCompressionEngine(throwEngine);
|
|
registerCompressionEngine(makeLowGainEngine());
|
|
registerCompressionEngine(highGainEngine);
|
|
registerCompressionEngine(lowGainAsyncEngine);
|
|
registerCompressionEngine(throwAsyncEngine);
|
|
});
|
|
|
|
after(() => {
|
|
unregisterCompressionEngine(THROW_ENGINE_ID);
|
|
unregisterCompressionEngine(LOW_GAIN_ENGINE_ID);
|
|
unregisterCompressionEngine(HIGH_GAIN_ENGINE_ID);
|
|
unregisterCompressionEngine(LOW_GAIN_ASYNC_ID);
|
|
unregisterCompressionEngine(THROW_ASYNC_ID);
|
|
});
|
|
|
|
// ── SYNC tests ────────────────────────────────────────────────────────────
|
|
|
|
describe("sync — applyStackedCompression", () => {
|
|
it("bail-out ON: throwing engine → step skipped, pipeline does NOT throw", () => {
|
|
const body = mkBody();
|
|
|
|
// Must not throw and original body is kept (throw engine was the only step)
|
|
const result = applyStackedCompression(body, pipeline(THROW_ENGINE_ID), BAILOUT_ON);
|
|
|
|
assert.equal(result.compressed, false);
|
|
assert.equal(userContent(result), "hello"); // body unchanged
|
|
|
|
// TV1 fix: a crashing engine must be RECORDED in telemetry, not silently gone.
|
|
assert.equal(result.stats?.fallbackApplied, true, "throw must set fallbackApplied");
|
|
assert.ok(
|
|
result.stats?.validationErrors?.some((e) => e.includes(THROW_ENGINE_ID)),
|
|
"throwing engine must be recorded in validationErrors"
|
|
);
|
|
});
|
|
|
|
it("bail-out ON: throwing engine before a good engine → good engine still runs", () => {
|
|
const body = mkBody();
|
|
|
|
// throw engine first, then high-gain engine — the high-gain must still run
|
|
const result = applyStackedCompression(
|
|
body,
|
|
pipeline(THROW_ENGINE_ID, HIGH_GAIN_ENGINE_ID),
|
|
BAILOUT_ON
|
|
);
|
|
|
|
// high-gain engine appends "|high"
|
|
assert.equal(userContent(result), "hello|high");
|
|
assert.equal(result.compressed, true);
|
|
});
|
|
|
|
it("bail-out ON: low-gain engine (5%) → body NOT advanced (step skipped)", () => {
|
|
const body = mkBody();
|
|
|
|
const result = applyStackedCompression(body, pipeline(LOW_GAIN_ENGINE_ID), BAILOUT_ON);
|
|
|
|
// low-gain would append "|low" but it should be skipped
|
|
assert.equal(userContent(result), "hello");
|
|
// compressed is false because the only step was skipped
|
|
assert.equal(result.compressed, false);
|
|
});
|
|
|
|
it("bail-out ON: high-gain engine (20%) → body IS advanced normally", () => {
|
|
const body = mkBody();
|
|
|
|
const result = applyStackedCompression(body, pipeline(HIGH_GAIN_ENGINE_ID), BAILOUT_ON);
|
|
|
|
assert.equal(userContent(result), "hello|high");
|
|
assert.equal(result.compressed, true);
|
|
});
|
|
|
|
it("bail-out ON: low-gain then high-gain → only high-gain advances body", () => {
|
|
const body = mkBody();
|
|
|
|
const result = applyStackedCompression(
|
|
body,
|
|
pipeline(LOW_GAIN_ENGINE_ID, HIGH_GAIN_ENGINE_ID),
|
|
BAILOUT_ON
|
|
);
|
|
|
|
// "|low" should be absent; "|high" should be present
|
|
assert.equal(userContent(result), "hello|high");
|
|
});
|
|
|
|
it("bail-out OFF (default): low-gain engine IS applied (opt-in guard)", () => {
|
|
const body = mkBody();
|
|
|
|
// No bailout config at all — original behavior
|
|
const result = applyStackedCompression(body, pipeline(LOW_GAIN_ENGINE_ID), BAILOUT_OFF);
|
|
|
|
// Without bail-out, the step is always applied
|
|
assert.equal(userContent(result), "hello|low");
|
|
assert.equal(result.compressed, true);
|
|
});
|
|
|
|
it("bail-out OFF (default): throwing engine propagates — unchanged existing behavior", () => {
|
|
const body = mkBody();
|
|
|
|
// Without bail-out, a throw is NOT caught → pipeline throws
|
|
assert.throws(() => {
|
|
applyStackedCompression(body, pipeline(THROW_ENGINE_ID), BAILOUT_OFF);
|
|
}, /simulated engine failure/);
|
|
});
|
|
});
|
|
|
|
// ── ASYNC tests ───────────────────────────────────────────────────────────
|
|
|
|
describe("async — applyStackedCompressionAsync", () => {
|
|
it("bail-out ON: async throwing engine → step skipped, no throw", async () => {
|
|
const body = mkBody();
|
|
|
|
const result = await applyStackedCompressionAsync(body, pipeline(THROW_ASYNC_ID), BAILOUT_ON);
|
|
|
|
assert.equal(userContent(result), "hello");
|
|
assert.equal(result.compressed, false);
|
|
});
|
|
|
|
it("bail-out ON: async low-gain engine (5%) → step skipped", async () => {
|
|
const body = mkBody();
|
|
|
|
const result = await applyStackedCompressionAsync(
|
|
body,
|
|
pipeline(LOW_GAIN_ASYNC_ID),
|
|
BAILOUT_ON
|
|
);
|
|
|
|
assert.equal(userContent(result), "hello");
|
|
assert.equal(result.compressed, false);
|
|
});
|
|
|
|
it("bail-out OFF (default): async low-gain engine IS applied", async () => {
|
|
const body = mkBody();
|
|
|
|
const result = await applyStackedCompressionAsync(
|
|
body,
|
|
pipeline(LOW_GAIN_ASYNC_ID),
|
|
BAILOUT_OFF
|
|
);
|
|
|
|
assert.equal(userContent(result), "hello|low");
|
|
assert.equal(result.compressed, true);
|
|
});
|
|
});
|
|
});
|