fix(context): skip compaction when the window can't fit a summary request

When instructions, the reserved summary output, and any prior checkpoint
already consume the model's context window, the summary-input budget is
zero. Previously a doomed summary request was still submitted (and
rejected). Skip summarization in that case and log, rather than issue a
request guaranteed to overflow.
This commit is contained in:
Ahmed Allam
2026-07-25 23:46:27 +00:00
committed by Devin AI
parent 739e969914
commit dd35af6181
2 changed files with 41 additions and 11 deletions
+11 -5
View File
@@ -297,13 +297,19 @@ async def maybe_compact(
split = _select_split(model, items, context.keep_tokens)
head, recent = items[:split], items[split:]
if not head:
previous = _previous_summary(head)
input_budget = _summary_input_budget(model, previous)
if not head or input_budget <= 0:
# Nothing to summarise, or the window can't even fit the summary
# instructions plus the reserved output allowance — any request would
# be rejected, so skip rather than submit a doomed call.
if head:
logger.warning(
"skipping compaction for %s: no room to summarise within its context window", model
)
return False
previous = _previous_summary(head)
serialized_head = _fit_to_tokens(
model, _serialize_items(head), _summary_input_budget(model, previous)
)
serialized_head = _fit_to_tokens(model, _serialize_items(head), input_budget)
summary = await _summarize(
model,
_build_summary_prompt(serialized_head, previous),
+30 -6
View File
@@ -139,11 +139,11 @@ async def test_maybe_compact_noop_when_within_budget(monkeypatch: pytest.MonkeyP
@pytest.mark.asyncio
async def test_maybe_compact_rewrites_and_keeps_pairs(monkeypatch: pytest.MonkeyPatch) -> None:
_patch_budget(monkeypatch, keep_tokens=30, window=50)
_patch_budget(monkeypatch, keep_tokens=30, window=4_000)
_patch_summary(monkeypatch, "SUMMARY BODY")
session = FakeSession(_turns(12))
assert await compaction.maybe_compact(session, model="m") is True
assert await compaction.maybe_compact(session, model="m", force=True) is True
items = await session.get_items()
assert items[0]["role"] == "user"
@@ -155,7 +155,8 @@ async def test_maybe_compact_rewrites_and_keeps_pairs(monkeypatch: pytest.Monkey
@pytest.mark.asyncio
async def test_maybe_compact_updates_previous_summary(monkeypatch: pytest.MonkeyPatch) -> None:
_patch_budget(monkeypatch, keep_tokens=30, window=50)
# Window large enough to leave real room for the summary instructions.
_patch_budget(monkeypatch, keep_tokens=30, window=4_000)
captured: dict[str, str] = {}
async def fake_acompletion(**kwargs: Any) -> Any:
@@ -167,7 +168,7 @@ async def test_maybe_compact_updates_previous_summary(monkeypatch: pytest.Monkey
prior = compaction._checkpoint_item("OLD SUMMARY TEXT")
session = FakeSession([prior, *_turns(12)])
assert await compaction.maybe_compact(session, model="m") is True
assert await compaction.maybe_compact(session, model="m", force=True) is True
assert "OLD SUMMARY TEXT" in captured["prompt"]
@@ -227,7 +228,7 @@ async def test_summary_request_fits_when_room_is_below_old_floor(
@pytest.mark.asyncio
async def test_maybe_compact_skips_when_summary_fails(monkeypatch: pytest.MonkeyPatch) -> None:
_patch_budget(monkeypatch, keep_tokens=30, window=50)
_patch_budget(monkeypatch, keep_tokens=30, window=4_000)
async def fake_acompletion(**_kwargs: Any) -> Any:
raise RuntimeError("boom")
@@ -236,5 +237,28 @@ async def test_maybe_compact_skips_when_summary_fails(monkeypatch: pytest.Monkey
session = FakeSession(_turns(12))
before = await session.get_items()
assert await compaction.maybe_compact(session, model="m") is False
assert await compaction.maybe_compact(session, model="m", force=True) is False
assert await session.get_items() == before
@pytest.mark.asyncio
async def test_maybe_compact_skips_when_no_room_to_summarise(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# A window too small to fit the summary instructions plus the reserved
# output leaves no room for any head, so no (doomed) summary is attempted.
_patch_budget(monkeypatch, keep_tokens=30, window=200)
called = False
async def fake_acompletion(**_kwargs: Any) -> Any:
nonlocal called
called = True
return SimpleNamespace(choices=[SimpleNamespace(message=SimpleNamespace(content="S"))])
monkeypatch.setattr("strix.llm.compaction.litellm.acompletion", fake_acompletion)
session = FakeSession(_turns(12))
before = await session.get_items()
assert await compaction.maybe_compact(session, model="m", force=True) is False
assert called is False
assert await session.get_items() == before