mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
feat: unify context compaction
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { DEFAULT_CONFIG, normalizeConfig } from "../src/config.ts";
|
||||
|
||||
test("normalizes one greenfield piCompaction config without legacy aliases", () => {
|
||||
const config = normalizeConfig({
|
||||
enabled: false,
|
||||
softRatio: 0.7,
|
||||
hardRatio: 0.91,
|
||||
targetRatio: 0.5,
|
||||
deterministic: { enabled: false, maxStubTokens: 512 },
|
||||
semantic: { enabled: false, concurrency: 2, maxRetries: 0 },
|
||||
native: { enabled: false },
|
||||
});
|
||||
assert.equal(config.enabled, false);
|
||||
assert.equal(config.deterministic.enabled, false);
|
||||
assert.equal(config.deterministic.maxStubTokens, 512);
|
||||
assert.equal(config.semantic.enabled, false);
|
||||
assert.equal(config.semantic.maxRetries, 0);
|
||||
assert.equal(config.native.enabled, false);
|
||||
});
|
||||
|
||||
test("invalid ratios and bounds fail safe to bounded defaults", () => {
|
||||
const config = normalizeConfig({
|
||||
softRatio: 2,
|
||||
hardRatio: -1,
|
||||
targetRatio: 0.99,
|
||||
semantic: { concurrency: 50, maxRetries: -1 },
|
||||
});
|
||||
assert.equal(config.softRatio, DEFAULT_CONFIG.softRatio);
|
||||
assert.ok(config.targetRatio < config.softRatio);
|
||||
assert.ok(config.hardRatio > config.softRatio);
|
||||
assert.equal(config.semantic.concurrency, DEFAULT_CONFIG.semantic.concurrency);
|
||||
assert.equal(config.semantic.maxRetries, DEFAULT_CONFIG.semantic.maxRetries);
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { performance } from "node:perf_hooks";
|
||||
import piCompaction from "../index.ts";
|
||||
|
||||
function harness() {
|
||||
const handlers = new Map<string, Function[]>();
|
||||
const tools = new Map<string, any>();
|
||||
const commands = new Map<string, any>();
|
||||
const entries: Array<{ customType: string; data: unknown }> = [];
|
||||
const toolDefinitions = [{ name: "bash", description: "shell", parameters: { type: "object" } }];
|
||||
const pi: any = {
|
||||
on(name: string, handler: Function) {
|
||||
const list = handlers.get(name) ?? [];
|
||||
list.push(handler);
|
||||
handlers.set(name, list);
|
||||
},
|
||||
appendEntry(customType: string, data: unknown) { entries.push({ customType, data }); },
|
||||
registerTool(tool: any) { tools.set(tool.name, tool); },
|
||||
registerCommand(name: string, command: any) { commands.set(name, command); },
|
||||
getActiveTools() { return ["bash"]; },
|
||||
getAllTools() { return toolDefinitions; },
|
||||
};
|
||||
return { pi, handlers, tools, commands, entries };
|
||||
}
|
||||
|
||||
function context(branch: any[] = []) {
|
||||
return {
|
||||
sessionManager: {
|
||||
getSessionId: () => "session-test",
|
||||
getBranch: () => branch,
|
||||
},
|
||||
model: { provider: "test", api: "test", id: "model", contextWindow: 100_000 },
|
||||
modelRegistry: {},
|
||||
getSystemPrompt: () => "system",
|
||||
getContextUsage: () => ({ tokens: 1_000, contextWindow: 100_000, percent: 1 }),
|
||||
hasUI: false,
|
||||
abort() {},
|
||||
} as any;
|
||||
}
|
||||
|
||||
test("one runtime projects tool evidence and recovers it through context_tree_query", async () => {
|
||||
const h = harness();
|
||||
piCompaction(h.pi);
|
||||
const ctx = context();
|
||||
await h.handlers.get("session_start")![0]!({}, ctx);
|
||||
|
||||
const assistant = {
|
||||
role: "assistant",
|
||||
content: [{ type: "toolCall", id: "call-1", name: "bash", arguments: { command: "npm test" } }],
|
||||
timestamp: 10,
|
||||
};
|
||||
const result = {
|
||||
role: "toolResult",
|
||||
toolCallId: "call-1",
|
||||
toolName: "bash",
|
||||
content: [{ type: "text", text: "failure evidence" }],
|
||||
isError: true,
|
||||
timestamp: 11,
|
||||
};
|
||||
await h.handlers.get("turn_end")![0]!({ message: assistant, toolResults: [result] }, ctx);
|
||||
const projected = await h.handlers.get("context")![0]!({ messages: [assistant, result] }, ctx);
|
||||
|
||||
assert.match(projected.messages[1].content[0].text, /pi-compaction tool=bash/);
|
||||
const cacheStart = performance.now();
|
||||
for (let iteration = 0; iteration < 1_000; iteration++) {
|
||||
await h.handlers.get("context")![0]!({ messages: [assistant, result] }, ctx);
|
||||
}
|
||||
const cacheAverageMs = (performance.now() - cacheStart) / 1_000;
|
||||
assert.ok(cacheAverageMs < 2, `full context handler cache-hit average ${cacheAverageMs.toFixed(3)}ms exceeded 2ms`);
|
||||
assert.equal(h.entries.length, 1);
|
||||
assert.equal(h.entries[0]!.customType, "pi-compaction");
|
||||
|
||||
const query = h.tools.get("context_tree_query");
|
||||
const recovered = await query.execute("q", { toolCallIds: ["call-1"] }, new AbortController().signal, undefined, ctx);
|
||||
assert.match(recovered.content[0].text, /failure evidence/);
|
||||
|
||||
await h.handlers.get("session_shutdown")![0]!({}, ctx);
|
||||
});
|
||||
|
||||
test("model switching after a native checkpoint projects the portable checkpoint", async () => {
|
||||
const h = harness();
|
||||
piCompaction(h.pi);
|
||||
const details = {
|
||||
kind: "pi-compaction",
|
||||
version: 1,
|
||||
checkpoint: "native",
|
||||
modelKey: "openai-codex/openai-codex-responses/gpt-old",
|
||||
replacementHistory: [{ type: "compaction", encrypted_content: "opaque" }],
|
||||
portableSummary: "decisions and files from the old model",
|
||||
sourceFingerprint: "old",
|
||||
};
|
||||
const branch = [
|
||||
{ type: "compaction", id: "c1", parentId: null, timestamp: new Date().toISOString(), summary: "marker", firstKeptEntryId: "m0", tokensBefore: 1_000, details },
|
||||
{ type: "message", id: "m1", parentId: "c1", timestamp: new Date().toISOString(), message: { role: "user", content: "continue", timestamp: 20 } },
|
||||
] as any[];
|
||||
const ctx = context(branch);
|
||||
await h.handlers.get("session_start")![0]!({}, ctx);
|
||||
const projected = await h.handlers.get("context")![0]!({ messages: [] }, ctx);
|
||||
assert.match(projected.messages[0].content[0].text, /decisions and files/);
|
||||
assert.equal(projected.messages[1].content, "continue");
|
||||
await h.handlers.get("session_shutdown")![0]!({}, ctx);
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
buildReplacementHistory,
|
||||
findNativeCheckpoint,
|
||||
parseNativeCompactionDetails,
|
||||
type NativeCompactionDetails,
|
||||
} from "../src/native-compaction.ts";
|
||||
|
||||
const details: NativeCompactionDetails = {
|
||||
kind: "pi-compaction",
|
||||
version: 1,
|
||||
checkpoint: "native",
|
||||
modelKey: "openai-codex/openai-codex-responses/gpt-test",
|
||||
replacementHistory: [{ type: "compaction", encrypted_content: "opaque" }],
|
||||
portableSummary: "portable",
|
||||
sourceFingerprint: "abc",
|
||||
};
|
||||
|
||||
test("recognizes only the unified native checkpoint format", () => {
|
||||
assert.deepEqual(parseNativeCompactionDetails(details), details);
|
||||
assert.equal(parseNativeCompactionDetails({ ...details, kind: "openai-codex-native-compaction" }), undefined);
|
||||
assert.equal(parseNativeCompactionDetails({ ...details, portableSummary: undefined }), undefined);
|
||||
});
|
||||
|
||||
test("latest Pi compaction entry is the authoritative native checkpoint", () => {
|
||||
const branch = [
|
||||
{ type: "message", id: "m1" },
|
||||
{ type: "compaction", id: "c1", details },
|
||||
] as any;
|
||||
const found = findNativeCheckpoint(branch);
|
||||
assert.equal(found.status, "valid");
|
||||
if (found.status === "valid") assert.equal(found.checkpoint.entryIndex, 1);
|
||||
|
||||
const shadowed = findNativeCheckpoint([...branch, { type: "compaction", id: "c2", details: { kind: "other" } }] as any);
|
||||
assert.equal(shadowed.status, "none");
|
||||
const portable = findNativeCheckpoint([
|
||||
...branch,
|
||||
{ type: "compaction", id: "c3", details: { kind: "pi-compaction", version: 1, checkpoint: "portable" } },
|
||||
] as any);
|
||||
assert.equal(portable.status, "none");
|
||||
});
|
||||
|
||||
test("replacement history ends with exactly one native item", () => {
|
||||
const history = buildReplacementHistory([
|
||||
{ role: "user", content: [{ type: "input_text", text: "recent" }] },
|
||||
], { type: "compaction", encrypted_content: "opaque" }, 1_000);
|
||||
assert.equal(history.at(-1)?.type, "compaction");
|
||||
assert.equal(history.filter((item) => item.type === "compaction").length, 1);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { performance } from "node:perf_hooks";
|
||||
import { DEFAULT_CONFIG } from "../src/config.ts";
|
||||
import { SegmentIndex } from "../src/segment-index.ts";
|
||||
|
||||
function message(index: number) {
|
||||
return { role: "user", content: `message-${index}`, timestamp: index + 1 };
|
||||
}
|
||||
|
||||
test("cached projection and append-only delta stay inside the hot-path budget", () => {
|
||||
const index = new SegmentIndex("perf");
|
||||
index.hydrate([], DEFAULT_CONFIG);
|
||||
const messages = Array.from({ length: 10_000 }, (_, value) => message(value));
|
||||
const params = { environmentHash: "env", environmentTokens: 2_000, contextWindow: 300_000 };
|
||||
index.project(messages, params);
|
||||
|
||||
const cacheStart = performance.now();
|
||||
for (let iteration = 0; iteration < 1_000; iteration++) index.project(messages, params);
|
||||
const cacheAverageMs = (performance.now() - cacheStart) / 1_000;
|
||||
|
||||
const appended = [...messages, message(10_000)];
|
||||
const deltaStart = performance.now();
|
||||
index.project(appended, params);
|
||||
const deltaMs = performance.now() - deltaStart;
|
||||
|
||||
assert.ok(cacheAverageMs < 2, `cache-hit average ${cacheAverageMs.toFixed(3)}ms exceeded 2ms`);
|
||||
assert.ok(deltaMs < 10, `delta projection ${deltaMs.toFixed(3)}ms exceeded 10ms`);
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { DEFAULT_CONFIG } from "../src/config.ts";
|
||||
import { SegmentIndex, deterministicSummary } from "../src/segment-index.ts";
|
||||
|
||||
const assistant = {
|
||||
role: "assistant",
|
||||
content: [{ type: "toolCall", id: "call-1", name: "bash", arguments: { command: "npm test" } }],
|
||||
timestamp: 10,
|
||||
};
|
||||
const result = {
|
||||
role: "toolResult",
|
||||
toolCallId: "call-1",
|
||||
toolName: "bash",
|
||||
content: [{ type: "text", text: `failure\n${"x".repeat(12_000)}\nstack tail` }],
|
||||
isError: true,
|
||||
timestamp: 11,
|
||||
};
|
||||
|
||||
test("deterministic summary is bounded and preserves head/tail evidence", () => {
|
||||
const summary = deterministicSummary({
|
||||
toolName: "bash",
|
||||
args: { command: "npm test" },
|
||||
rawText: result.content[0]!.text,
|
||||
isError: true,
|
||||
maxStubTokens: 256,
|
||||
});
|
||||
assert.match(summary, /tool=bash status=error/);
|
||||
assert.match(summary, /failure/);
|
||||
assert.match(summary, /stack tail/);
|
||||
assert.ok(summary.length <= 1_100);
|
||||
});
|
||||
|
||||
test("projection incrementally replaces tool results and caches identical input", () => {
|
||||
const index = new SegmentIndex("session-1");
|
||||
index.hydrate([], DEFAULT_CONFIG);
|
||||
const records = index.captureTurn(assistant, [result], DEFAULT_CONFIG);
|
||||
assert.equal(records.length, 1);
|
||||
|
||||
const messages = [assistant, result];
|
||||
const first = index.project(messages, {
|
||||
environmentHash: "env",
|
||||
environmentTokens: 100,
|
||||
contextWindow: 300_000,
|
||||
});
|
||||
const second = index.project(messages, {
|
||||
environmentHash: "env",
|
||||
environmentTokens: 100,
|
||||
contextWindow: 300_000,
|
||||
});
|
||||
|
||||
assert.strictEqual(first, second);
|
||||
assert.ok(first.effectiveTokens < first.rawTokens);
|
||||
assert.match(first.messages[1]!.content[0].text, /context_tree_query/);
|
||||
|
||||
const record = records[0]!;
|
||||
assert.ok(index.applySemantic(record.sourceKey, record.sourceHash, "- npm test failed\n- stack tail"));
|
||||
const semantic = index.project(messages, {
|
||||
environmentHash: "env",
|
||||
environmentTokens: 100,
|
||||
contextWindow: 300_000,
|
||||
});
|
||||
assert.notStrictEqual(semantic, first);
|
||||
assert.match(semantic.messages[1]!.content[0].text, /npm test failed/);
|
||||
});
|
||||
|
||||
test("usage-backed baseline is adjusted only by projected delta", () => {
|
||||
const index = new SegmentIndex("session-2");
|
||||
index.hydrate([], DEFAULT_CONFIG);
|
||||
const initial = index.project([assistant], {
|
||||
environmentHash: "env",
|
||||
environmentTokens: 50,
|
||||
contextWindow: 100_000,
|
||||
});
|
||||
index.recordUsage(1_000, initial.messageEstimate, "env");
|
||||
const next = index.project([assistant, { role: "user", content: "next", timestamp: 12 }], {
|
||||
environmentHash: "env",
|
||||
environmentTokens: 50,
|
||||
contextWindow: 100_000,
|
||||
});
|
||||
assert.equal(next.confidence, "usage-backed");
|
||||
assert.ok(next.effectiveTokens >= 1_000);
|
||||
assert.ok(next.effectiveTokens < 1_100);
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { shouldCompact } from "../src/token-accounting.ts";
|
||||
|
||||
test("raw pressure cannot trigger compaction while effective lower-bound stays below 90%", () => {
|
||||
const config = { hardRatio: 0.9 };
|
||||
const snapshot = { lowerBound: 231_000, contextWindow: 300_000 };
|
||||
assert.equal(shouldCompact("threshold", snapshot, config), false);
|
||||
assert.equal(shouldCompact("manual", snapshot, config), true);
|
||||
assert.equal(shouldCompact("overflow", snapshot, config), true);
|
||||
});
|
||||
|
||||
test("effective lower-bound at the configured threshold compacts", () => {
|
||||
assert.equal(shouldCompact(
|
||||
"threshold",
|
||||
{ lowerBound: 270_000, contextWindow: 300_000 },
|
||||
{ hardRatio: 0.9 },
|
||||
), true);
|
||||
});
|
||||
Reference in New Issue
Block a user