fix(context): cap summary output at model limit; safe token upper bound

- Clamp the summary request's max_tokens to the model's output limit so a
  large STRIX_CONTEXT_SUMMARY_TOKENS can't get the request rejected (which
  left the overflowing session uncompacted). Applied consistently to the
  input-budget reservation and the request itself.
- Replace the tokenizer-unavailable fallback with the UTF-8 byte length, a
  guaranteed upper bound on tokens for byte-level BPE, so budget checks can
  never under-count dense history.
This commit is contained in:
Ahmed Allam
2026-07-26 00:15:49 +00:00
committed by Devin AI
parent 2163a66b78
commit 4eedfc64b4
4 changed files with 35 additions and 11 deletions
+13
View File
@@ -190,6 +190,19 @@ def test_fit_to_tokens_truncates_oversized_text(monkeypatch: pytest.MonkeyPatch)
assert compaction._fit_to_tokens("m", "short", 500) == "short"
def test_summary_output_tokens_capped_at_model_limit(monkeypatch: pytest.MonkeyPatch) -> None:
context = ContextSettings()
monkeypatch.setattr(compaction, "load_settings", lambda: SimpleNamespace(context=context))
monkeypatch.setattr(compaction, "output_limit", lambda _m: 1_000)
context.summary_max_tokens = 4_096
# Configured allowance above the model cap is clamped down to the cap.
assert compaction._summary_output_tokens("m") == 1_000
# Below the cap, the configured value is used unchanged.
context.summary_max_tokens = 500
assert compaction._summary_output_tokens("m") == 500
@pytest.mark.asyncio
async def test_maybe_compact_bounds_summary_prompt(monkeypatch: pytest.MonkeyPatch) -> None:
# A tiny window with a huge head must not send an oversized summary request.
+4 -3
View File
@@ -38,10 +38,11 @@ def test_count_tokens_fallback_on_error(monkeypatch: pytest.MonkeyPatch) -> None
raise RuntimeError("no tokenizer")
monkeypatch.setattr("strix.llm.context_budget.litellm.token_counter", _raise)
# Conservative ~3-chars/token estimate, rounded up, so budgets never
# UTF-8 byte length is a guaranteed upper bound on tokens, so budgets never
# under-count when no tokenizer is available.
assert context_budget.count_tokens("weird-model", "x" * 400) == 134
assert context_budget.count_tokens("weird-model", "x") == 1
assert context_budget.count_tokens("weird-model", "x" * 400) == 400
# Multibyte text: bytes (not chars) bound the token count.
assert context_budget.count_tokens("weird-model", "😀" * 10) == 40
def test_count_tokens_empty_is_zero() -> None: