From dbc71fb6e4031bf2aa1274e24ba901f8cf33f757 Mon Sep 17 00:00:00 2001 From: Sora-bluesky Date: Sun, 19 Jul 2026 19:31:37 +0900 Subject: [PATCH] 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 --- agent/conversation_compression.py | 45 +++++++++++++--- .../run_agent/test_compression_feasibility.py | 52 ++++++++++++++++++- 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/agent/conversation_compression.py b/agent/conversation_compression.py index 87b2797dae89..f799647fe08f 100644 --- a/agent/conversation_compression.py +++ b/agent/conversation_compression.py @@ -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: \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: \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: \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( diff --git a/tests/run_agent/test_compression_feasibility.py b/tests/run_agent/test_compression_feasibility.py index f07cf8607b40..1f3d750515ba 100644 --- a/tests/run_agent/test_compression_feasibility.py +++ b/tests/run_agent/test_compression_feasibility.py @@ -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]