fix(context): never floor summary input above the model's remaining room

The 1,000-token floor on summary-input room could exceed the space
actually left after instructions, the reserved summary output, and any
existing checkpoint. On a small window the summary request then overflowed
and returned nothing, leaving the oversized session uncompacted. Clamp to
the real room instead so the request always fits.
This commit is contained in:
Ahmed Allam
2026-07-25 23:46:27 +00:00
committed by Devin AI
parent 8fd871249c
commit 739e969914
2 changed files with 27 additions and 4 deletions
+4 -4
View File
@@ -28,8 +28,6 @@ logger = logging.getLogger(__name__)
_CHECKPOINT_TAG = "<conversation-checkpoint>"
_TOOL_OUTPUT_MAX_CHARS = 2_000
_MIN_ITEMS_TO_COMPACT = 6
# Floor for how much of the head we still try to summarise even on a tiny model.
_MIN_SUMMARY_INPUT_TOKENS = 1_000
_HEAD_TRUNCATED_MARKER = "\n\n[... older conversation omitted to fit the summary request ...]\n\n"
# Substrings that identify a context-window-overflow error across providers.
@@ -214,9 +212,11 @@ def _summary_input_budget(model: str, previous: str | None) -> int:
if previous:
overhead += count_tokens(model, previous)
# Leave slack for the prompt's wrapper text ("Conversation to summarise:",
# the update instructions, etc.) that is not part of ``overhead``.
# the update instructions, etc.) that is not part of ``overhead``. Never
# floor above the actual room: doing so would let the summary request
# itself overflow a small window (and then compaction silently fails).
room = context_window(model) - context.summary_max_tokens - overhead - 256
return max(_MIN_SUMMARY_INPUT_TOKENS, room)
return max(0, room)
def _build_summary_prompt(serialized_head: str, previous: str | None) -> str:
+23
View File
@@ -202,6 +202,29 @@ async def test_maybe_compact_bounds_summary_prompt(monkeypatch: pytest.MonkeyPat
assert len(captured["prompt"]) <= 4_000
@pytest.mark.asyncio
async def test_summary_request_fits_when_room_is_below_old_floor(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# When the window leaves less head-input room than the old fixed floor, the
# budget must shrink to the real room so the request still fits the window
# (a fixed floor above the room would overflow and silently fail).
instructions = len(compaction._SUMMARY_INSTRUCTIONS)
window = instructions + 64 + 256 + 300 # summary_max(64)+slack(256)+room(300)
_patch_budget(monkeypatch, keep_tokens=30, window=window)
captured: dict[str, str] = {}
async def fake_acompletion(**kwargs: Any) -> Any:
captured["prompt"] = kwargs["messages"][0]["content"]
return SimpleNamespace(choices=[SimpleNamespace(message=SimpleNamespace(content="S"))])
monkeypatch.setattr("strix.llm.compaction.litellm.acompletion", fake_acompletion)
session = FakeSession([{"role": "user", "content": "y" * 5_000} for _ in range(20)])
assert await compaction.maybe_compact(session, model="m") is True
assert len(captured["prompt"]) <= window
@pytest.mark.asyncio
async def test_maybe_compact_skips_when_summary_fails(monkeypatch: pytest.MonkeyPatch) -> None:
_patch_budget(monkeypatch, keep_tokens=30, window=50)