fix(compression): don't recommend a threshold the small-context floor will ignore

The auxiliary-compression feasibility warning computes its
compression.threshold suggestion as aux_context / main_context,
independently of ContextCompressor._effective_threshold_percent()'s
raise-only small-context floor. For main windows under 512K the floor
raises any configured value below 75% back up, so a suggestion like
'threshold: 0.40' is silently ignored and the same warning returns every
session.

Derive the suggestion's viability through the compressor's own floor
logic: offer the 'lower the threshold' option only when the floored value
still fits the auxiliary model's context; otherwise recommend only a
larger compression model and explain the floor, so the guidance is always
actionable.

Tests: the updated auto-correct test pins the floored branch (no
threshold suggestion, floor explained); two new tests pin the surviving
suggestion at/above the floor on a small window and below 75% on a
512K+ window where no floor applies. The updated test fails against the
previous code.

Fixes #67422

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Sora-bluesky 2026-07-19 19:31:37 +09:00 committed by Teknium
parent 295e20358c
commit dbc71fb6e4
2 changed files with 88 additions and 9 deletions

View file

@ -504,6 +504,20 @@ def check_compression_model_feasibility(agent: Any) -> None:
new_threshold / main_ctx
)
safe_pct = int((aux_context / main_ctx) * 100) if main_ctx else 50
# The "lower the threshold" suggestion must survive the
# compressor's raise-only small-context floor (#67422): for main
# windows under 512K, _effective_threshold_percent() raises any
# configured value below 75% back up, so recommending e.g. 0.40
# would be silently ignored and this warning would reappear every
# session. Only offer the option when the floored value still
# fits the auxiliary model's context.
from agent.context_compressor import ContextCompressor as _CC
threshold_suggestion_viable = (
not main_ctx
or _CC._effective_threshold_percent(main_ctx, safe_pct / 100) * main_ctx
<= aux_context
)
# Build human-readable "model (provider)" labels for both
# the main model and the compression model so users can
# tell at a glance which provider each side is actually
@ -537,15 +551,30 @@ def check_compression_model_feasibility(agent: Any) -> None:
f"{old_threshold:,} tokens. "
f"Auto-lowered this session's threshold to "
f"{new_threshold:,} tokens so compression can run.\n"
f" To make this permanent, edit config.yaml — either:\n"
f" 1. Use a larger compression model:\n"
f" auxiliary:\n"
f" compression:\n"
f" model: <model-with-{old_threshold:,}+-context>\n"
f" 2. Lower the compression threshold:\n"
f" compression:\n"
f" threshold: 0.{safe_pct:02d}"
)
if threshold_suggestion_viable:
msg += (
f" To make this permanent, edit config.yaml — either:\n"
f" 1. Use a larger compression model:\n"
f" auxiliary:\n"
f" compression:\n"
f" model: <model-with-{old_threshold:,}+-context>\n"
f" 2. Lower the compression threshold:\n"
f" compression:\n"
f" threshold: 0.{safe_pct:02d}"
)
else:
msg += (
f" To make this permanent, use a larger compression "
f"model in config.yaml:\n"
f" auxiliary:\n"
f" compression:\n"
f" model: <model-with-{old_threshold:,}+-context>\n"
f" (Lowering compression.threshold cannot help here — "
f"{_main_label}'s {main_ctx:,}-token window is under "
f"512K, where Hermes floors the compression trigger at "
f"75% and raises any lower configured value back up.)"
)
agent._compression_warning = msg
agent._emit_status(msg)
logger.warning(

View file

@ -95,7 +95,12 @@ def test_auto_corrects_threshold_when_aux_context_below_threshold(mock_get_clien
assert "config.yaml" in messages[0]
assert "auxiliary:" in messages[0]
assert "compression:" in messages[0]
assert "threshold:" in messages[0]
# 200K main is under the 512K small-context limit and 80K/200K = 40% sits
# below the 75% floor — a `threshold:` suggestion would be raised back to
# 75% and ignored (#67422), so the message must not offer one and must
# explain the floor instead.
assert "threshold:" not in messages[0]
assert "75%" in messages[0]
# Warning stored for gateway replay
assert agent._compression_warning is not None
# Threshold on the live compressor was actually lowered to aux_context.
@ -514,3 +519,48 @@ def test_run_conversation_clears_warning_after_replay(mock_get_client, mock_ctx_
agent._compression_warning = None
assert len(callback_events) == 0
# ── #67422: threshold suggestion must survive the small-context floor ────────
@patch("agent.model_metadata.get_model_context_length", return_value=80_000)
@patch("agent.auxiliary_client.get_text_auxiliary_client")
def test_threshold_suggestion_kept_when_at_or_above_floor(mock_get_client, mock_ctx_len):
"""Small-context main, but aux/main = 80% >= the 75% floor — the
`threshold:` suggestion is still viable and must be offered."""
agent = _make_agent(main_context=100_000, threshold_percent=0.85)
# threshold = 85,000 — aux has 80,000 (above 64K floor, below threshold)
mock_client = MagicMock()
mock_client.base_url = "https://openrouter.ai/api/v1"
mock_client.api_key = "sk-aux"
mock_get_client.return_value = (mock_client, "google/gemini-3-flash-preview")
messages = []
agent._emit_status = lambda msg: messages.append(msg)
agent._check_compression_model_feasibility()
assert len(messages) == 1
assert "threshold: 0.80" in messages[0]
@patch("agent.model_metadata.get_model_context_length", return_value=300_000)
@patch("agent.auxiliary_client.get_text_auxiliary_client")
def test_threshold_suggestion_kept_for_large_context_main(mock_get_client, mock_ctx_len):
"""Main window >= 512K has no floor — any suggestion is honored, so the
`threshold:` option stays even below 75%."""
agent = _make_agent(main_context=1_000_000, threshold_percent=0.50)
# threshold = 500,000 — aux has 300,000
mock_client = MagicMock()
mock_client.base_url = "https://openrouter.ai/api/v1"
mock_client.api_key = "sk-aux"
mock_get_client.return_value = (mock_client, "google/gemini-3-flash-preview")
messages = []
agent._emit_status = lambda msg: messages.append(msg)
agent._check_compression_model_feasibility()
assert len(messages) == 1
assert "threshold: 0.30" in messages[0]