Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions packages/core/src/services/chatCompressionService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1883,14 +1883,23 @@ describe('ChatCompressionService.compress cheap-gate uses estimated tokens', ()
});

describe('computeThresholds', () => {
it('32K window — proportional fallback for all tiers, hard degrades to auto', () => {
it('32K window — proportional fallback for all tiers, hard = auto + HARD_BUFFER', () => {
const t = computeThresholds(32_000);
expect(t.warn).toBe(19_200); // 0.6 * 32K
expect(t.auto).toBe(22_400); // 0.7 * 32K
expect(t.hard).toBe(22_400); // max(window-23K=9K, auto=22.4K) = auto
expect(t.hard).toBe(25_400); // auto + HARD_BUFFER = 22.4K + 3K
expect(t.effectiveWindow).toBe(12_000);
});

it('60K window — hard no longer equals auto (issue #4945)', () => {
const t = computeThresholds(60_000);
expect(t.warn).toBe(36_000); // 0.6 * 60K
expect(t.auto).toBe(42_000); // 0.7 * 60K (pct wins: 42K vs ew-13K=27K)
expect(t.hard).toBe(45_000); // auto + HARD_BUFFER = 42K + 3K
expect(t.hard).toBeGreaterThan(t.auto);
expect(t.effectiveWindow).toBe(40_000);
});

it('128K window — mixed (warn=pct, auto/hard=abs)', () => {
const t = computeThresholds(128_000);
expect(t.warn).toBe(76_800); // 0.6 * 128K (pct wins: 76.8K vs auto-20K=75K)
Expand Down Expand Up @@ -1933,11 +1942,13 @@ describe('computeThresholds', () => {
expect(t.hard).toBe(0);
});

it('thresholds always satisfy warn <= auto <= hard', () => {
for (const w of [32_000, 64_000, 128_000, 200_000, 256_000, 1_000_000]) {
it('thresholds always satisfy warn <= auto < hard for non-zero windows', () => {
for (const w of [
10_000, 32_000, 60_000, 64_000, 128_000, 200_000, 256_000, 1_000_000,
]) {
const t = computeThresholds(w);
expect(t.warn).toBeLessThanOrEqual(t.auto);
expect(t.auto).toBeLessThanOrEqual(t.hard);
expect(t.auto).toBeLessThan(t.hard);
}
});
});
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/services/chatCompressionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export interface CompactionThresholds {
* Each tier is `max(proportional, absolute)`:
* auto = max(DEFAULT_PCT * window, effectiveWindow - AUTOCOMPACT_BUFFER)
* warn = max((DEFAULT_PCT - WARN_PCT_OFFSET) * window, auto - WARN_BUFFER)
* hard = max(effectiveWindow - HARD_BUFFER, auto) // hard degrades to auto for tiny windows
* hard = min(window, max(effectiveWindow - HARD_BUFFER, auto + HARD_BUFFER))
*
* Small windows (where the absolute branch goes negative) automatically
* fall back to the proportional branch. Large windows are dominated by
Expand All @@ -144,7 +144,10 @@ export function computeThresholds(window: number): CompactionThresholds {
const warn = Math.max((DEFAULT_PCT - WARN_PCT_OFFSET) * window, absWarn);

const rawHard = effectiveWindow - HARD_BUFFER;
const hard = Math.max(rawHard, auto);
// Guarantee hard > auto so compaction doesn't wait until the last moment.
// For tiny/zero windows where auto is already at the proportional floor,
// clamp hard to the window itself so it never exceeds the actual limit.
const hard = Math.min(window, Math.max(rawHard, auto + HARD_BUFFER));

return { warn, auto, hard, effectiveWindow };
}
Expand Down
Loading