fix(core): restore compaction input budget contract

This commit is contained in:
Robin Newhouse
2026-07-07 20:25:19 -07:00
parent 302f6f7f41
commit 35e12b047d
3 changed files with 15 additions and 30 deletions
@@ -90,7 +90,6 @@ function runForcedBasicCompaction(
info: { id: "mock-model", maxInputTokens: targetTokens },
},
maxInputTokens: targetTokens,
usableBudgetTokens: targetTokens,
triggerTokens: targetTokens,
thresholdRatio: 1,
utilizationRatio: 2,
@@ -266,7 +265,6 @@ describe("createContextCompactionPrepareTurn", () => {
info: { id: "mock-model", maxInputTokens: 100_000 },
},
maxInputTokens: 100_000,
usableBudgetTokens: 100_000,
triggerTokens: 100_000,
thresholdRatio: 1,
utilizationRatio: 0.1,
@@ -490,7 +488,6 @@ describe("createContextCompactionPrepareTurn", () => {
info: { id: "mock-model", maxInputTokens: 1_000 },
},
maxInputTokens: 1_000,
usableBudgetTokens: 1_000,
triggerTokens: 900,
targetTokens: 100,
thresholdRatio: 0.9,
@@ -1416,9 +1413,9 @@ describe("createContextCompactionPrepareTurn", () => {
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.maxInputTokens).toBe(400_000);
expect(context?.maxInputTokens).toBe(272_000);
expect(context?.triggerTokens).toBe(244_800);
expect(context?.thresholdRatio).toBe(244_800 / 400_000);
expect(context?.thresholdRatio).toBe(0.9);
expect(result?.messages).toEqual([
{ role: "user", content: "Compacted by derived input budget" },
]);
@@ -1466,10 +1463,9 @@ describe("createContextCompactionPrepareTurn", () => {
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.maxInputTokens).toBe(40_960);
expect(context?.usableBudgetTokens).toBe(24_576);
expect(context?.maxInputTokens).toBe(24_576);
expect(context?.triggerTokens).toBe(22_118);
expect(context?.thresholdRatio).toBe(22_118 / 40_960);
expect(context?.thresholdRatio).toBe(22_118 / 24_576);
});
it("caps shared-context output reserve at half the context window", async () => {
@@ -1514,7 +1510,7 @@ describe("createContextCompactionPrepareTurn", () => {
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.maxInputTokens).toBe(40_960);
expect(context?.maxInputTokens).toBe(20_480);
expect(context?.triggerTokens).toBe(18_432);
});
@@ -1561,7 +1557,7 @@ describe("createContextCompactionPrepareTurn", () => {
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.triggerTokens).toBe(19_660);
expect(context?.thresholdRatio).toBe(19_660 / 40_960);
expect(context?.thresholdRatio).toBe(19_660 / 24_576);
});
it("applies explicit reserve tokens to the usable budget", async () => {
@@ -1607,7 +1603,7 @@ describe("createContextCompactionPrepareTurn", () => {
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.triggerTokens).toBe(20_480);
expect(context?.thresholdRatio).toBe(0.5);
expect(context?.thresholdRatio).toBe(20_480 / 24_576);
});
it("uses the lower split input budget when it is below context-derived input budget", async () => {
@@ -2013,7 +2009,6 @@ describe("createContextCompactionPrepareTurn", () => {
info: { id: "mock-model", maxInputTokens: 100 },
},
maxInputTokens: 100,
usableBudgetTokens: 100,
triggerTokens: 100,
thresholdRatio: 1,
utilizationRatio: 0.1,
@@ -100,7 +100,6 @@ interface CompactionBudget {
outputReserveTokens: number;
triggerTokens: number;
thresholdRatio: number;
usableThresholdRatio: number;
ceilingSource: CompactionBudgetSource;
reserveCapped: boolean;
}
@@ -166,8 +165,6 @@ function resolveCompactionBudget(input: {
outputReserveTokens,
triggerTokens,
thresholdRatio:
ceilingTokens > 0 ? triggerTokens / ceilingTokens : 0,
usableThresholdRatio:
usableBudgetTokens > 0 ? triggerTokens / usableBudgetTokens : 0,
ceilingSource,
reserveCapped:
@@ -371,7 +368,7 @@ export function createContextCompactionPrepareTurn(
contextWindow: context.model.info?.contextWindow,
modelMaxTokens: context.model.info?.maxTokens,
});
const maxInputTokens = compactionBudget.ceilingTokens;
const maxInputTokens = compactionBudget.usableBudgetTokens;
const shouldCompact = inputTokens > compactionBudget.triggerTokens;
config.logger?.debug("Context compaction diagnostics", {
mode,
@@ -381,13 +378,12 @@ export function createContextCompactionPrepareTurn(
modelId: config.modelId,
inputTokens,
maxInputTokens,
usableBudgetTokens: compactionBudget.usableBudgetTokens,
ceilingTokens: compactionBudget.ceilingTokens,
outputReserveTokens: compactionBudget.outputReserveTokens,
ceilingSource: compactionBudget.ceilingSource,
reserveCapped: compactionBudget.reserveCapped,
triggerTokens: compactionBudget.triggerTokens,
thresholdRatio: compactionBudget.thresholdRatio,
usableThresholdRatio: compactionBudget.usableThresholdRatio,
shouldCompact,
messageCount: context.messages.length,
apiMessageCount: context.apiMessages.length,
@@ -425,7 +421,6 @@ export function createContextCompactionPrepareTurn(
messages: context.messages,
model: context.model,
maxInputTokens,
usableBudgetTokens: compactionBudget.usableBudgetTokens,
triggerTokens: targetState.triggerTokens,
targetTokens,
thresholdRatio: targetState.thresholdRatio,
@@ -442,7 +437,7 @@ export function createContextCompactionPrepareTurn(
iteration: context.iteration,
triggerTokens: targetState.triggerTokens,
maxInputTokens,
usableBudgetTokens: compactionBudget.usableBudgetTokens,
ceilingTokens: compactionBudget.ceilingTokens,
outputReserveTokens: compactionBudget.outputReserveTokens,
ceilingSource: compactionBudget.ceilingSource,
},
@@ -486,7 +481,7 @@ export function createContextCompactionPrepareTurn(
severity: "info",
strategy: strategy,
maxInputTokens,
usableBudgetTokens: compactionBudget.usableBudgetTokens,
ceilingTokens: compactionBudget.ceilingTokens,
outputReserveTokens: compactionBudget.outputReserveTokens,
ceilingSource: compactionBudget.ceilingSource,
reserveCapped: compactionBudget.reserveCapped,
+4 -9
View File
@@ -67,20 +67,15 @@ export interface CoreCompactionContext {
info?: ModelInfo;
};
/**
* Full input ceiling for the model (context window or explicit override),
* before any output-token reserve is subtracted.
* Usable input budget after reserving any shared context window space needed
* for model output.
*/
maxInputTokens: number;
/**
* Tokens the input may actually occupy: `maxInputTokens` minus the output
* reserve held back on shared-context models. Compact below this value.
*/
usableBudgetTokens: number;
triggerTokens: number;
targetTokens?: number;
/**
* Effective trigger point as a fraction of `maxInputTokens`
* (`triggerTokens / maxInputTokens`), not the raw configured ratio.
* Effective trigger point as a fraction of the usable input budget
* (`triggerTokens / maxInputTokens`).
*/
thresholdRatio: number;
utilizationRatio: number;