Budget basic compaction projections

This commit is contained in:
Robin Newhouse
2026-06-26 19:04:04 -07:00
parent 64aa2618ab
commit 59a392bd78
2 changed files with 40 additions and 6 deletions
@@ -3,6 +3,7 @@ import type {
CoreCompactionContext,
CoreCompactionResult,
} from "../../types/config";
import { buildBudgetProjection } from "./budget-projection";
import {
type EstimateMessageTokens,
findFirstUserMessageIndex,
@@ -390,7 +391,23 @@ export function runBasicCompaction(options: {
...candidates.map((candidate) => candidate.message),
...protectedTail,
];
if (!haveMessagesChanged(options.context.messages, nextMessages)) {
const budgeted = buildBudgetProjection({
messages: nextMessages,
targetTokens,
policyIntent: "basic_compaction_projection",
estimateMessageTokens: options.estimateMessageTokens,
});
if (budgeted.status === "failed") {
options.logger?.debug("Basic compaction returned best-effort projection", {
budgetWarnings: budgeted.warnings.map((warning) => warning.code),
projectedTokens: budgeted.estimatedTokens,
targetTokens,
maxInputTokens: options.context.maxInputTokens,
});
}
const resultMessages = budgeted.messages;
if (!haveMessagesChanged(options.context.messages, resultMessages)) {
return undefined;
}
@@ -402,18 +419,20 @@ export function runBasicCompaction(options: {
options.estimateMessageTokens,
);
const afterTokens = getTotalTokens(
nextMessages,
resultMessages,
options.estimateMessageTokens,
);
options.logger?.debug("Performed basic compaction", {
messagesBefore: options.context.messages.length,
messagesAfter: nextMessages.length,
messagesRemoved: options.context.messages.length - nextMessages.length,
messagesAfter: resultMessages.length,
messagesRemoved: options.context.messages.length - resultMessages.length,
tokensBefore: beforeTokens,
tokensAfter: afterTokens,
budgetActions: budgeted.actions.length,
budgetWarnings: budgeted.warnings.map((warning) => warning.code),
targetTokens,
maxInputTokens: options.context.maxInputTokens,
});
return { messages: nextMessages };
return { messages: resultMessages };
}
@@ -365,13 +365,28 @@ describe("createContextCompactionPrepareTurn", () => {
const compacted = runForcedBasicCompaction(messages, 1);
expect(compacted).toEqual([
{ role: "user", content: "Old request" },
{ role: "user", content: "Read the latest file" },
assistantToolUseMessage("tool-a"),
toolResultMessage("tool-a", "latest result"),
]);
});
it("budgets the complete basic compaction output including the latest turn", () => {
const messages: LlmsProviders.Message[] = [
{ role: "user", content: "original task" },
{ role: "assistant", content: "old assistant " + "x".repeat(10_000) },
{ role: "user", content: "latest typed prompt" },
assistantToolUseMessage("tool-live"),
toolResultMessage("tool-live", "live result " + "y".repeat(10_000)),
];
const compacted = runForcedBasicCompaction(messages, 700);
expect(totalJsonTokens(compacted)).toBeLessThanOrEqual(700);
expect(JSON.stringify(compacted)).toContain("latest typed prompt");
expectNoOrphanedToolPairs(compacted);
});
it("does not compact a single typed user message", () => {
const messages: LlmsProviders.Message[] = [
{ role: "user", content: "Only current request" },