From 8e7cf55eb673ab4ee5a35cd41faf35592eb70bd6 Mon Sep 17 00:00:00 2001 From: yoniebans Date: Thu, 5 Nov 2026 11:43:39 +0100 Subject: [PATCH] fix(agent): a task with its own URL re-probes when that URL's pin changes The route identity read a provider entry's context pin only for a task that names the entry. A task with `provider: custom` and its own `base_url` returned early, but the probe still reads the pin of the entry at that URL, so lowering only that pin (900K to 80K) left a warm agent's trigger on the old window. The identity now carries the pin for the route's own URL too. Reported-by: greptile-apps[bot] --- agent/conversation_compression_feasibility.py | 21 +++++++++------- tests/agent/test_compression_feasibility.py | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/agent/conversation_compression_feasibility.py b/agent/conversation_compression_feasibility.py index 747b042d76..3b0d2a7012 100644 --- a/agent/conversation_compression_feasibility.py +++ b/agent/conversation_compression_feasibility.py @@ -24,7 +24,8 @@ def _mark_checked(agent: Any, route: tuple) -> None: def compression_route_identity() -> tuple: """The configured compression route a probe measures: the task's provider, model, URL and context - length, plus the URL of a ``providers:`` / ``custom_providers:`` entry the task names. A config this + length, the URL of a ``providers:`` / ``custom_providers:`` entry the task names, and the context pin + a provider entry sets for the compression model at the route's URL. A config this cannot read yields ``("unreadable",)``: the main request carries on and the probe reports the problem.""" try: return _route_identity() @@ -38,15 +39,19 @@ def _route_identity() -> tuple: cfg = _get_auxiliary_task_config("compression") route = tuple(str(cfg.get(k) or "") for k in ("provider", "model", "base_url", "context_length")) provider = route[0].strip() - if not provider or provider in ("auto", "main", "custom") or route[2]: - return route from hermes_cli.config import get_custom_provider_context_length - from hermes_cli.runtime_provider_custom import _get_named_custom_provider - entry = _get_named_custom_provider(provider, resolve_credentials=False) or {} - url, model = str(entry.get("base_url") or ""), route[1] or str(entry.get("model") or "") - # The context pin the probe will read for this model at this URL (per-model, else entry-level). + if route[2]: + url, model, named = route[2], route[1], () + elif not provider or provider in ("auto", "main", "custom"): + return route + else: + from hermes_cli.runtime_provider_custom import _get_named_custom_provider + entry = _get_named_custom_provider(provider, resolve_credentials=False) or {} + url, model = str(entry.get("base_url") or ""), route[1] or str(entry.get("model") or "") + named = (url, str(entry.get("model") or "")) + # The context pin the probe reads for this model at this URL (a matching entry's per-model, else entry-level). pin = get_custom_provider_context_length(model=model, base_url=url) if url and model else None - return route + (url, str(entry.get("model") or ""), str(pin or "")) + return route + named + (str(pin or ""),) def _drop_stale_ceiling(agent: Any) -> None: diff --git a/tests/agent/test_compression_feasibility.py b/tests/agent/test_compression_feasibility.py index 2eab330415..9ad13a28f2 100644 --- a/tests/agent/test_compression_feasibility.py +++ b/tests/agent/test_compression_feasibility.py @@ -710,6 +710,30 @@ def test_a_changed_provider_context_pin_reprobes_a_warm_agent(monkeypatch, tmp_p assert agent.context_compressor.threshold_tokens == 80_000 + +def test_a_changed_provider_pin_reprobes_a_task_that_sets_its_own_url(monkeypatch, tmp_path): + """``provider: custom`` with the task's own ``base_url``: the probe still reads the pin of the + provider entry at that URL, so lowering only that pin (900K to 80K) re-probes a warm agent.""" + import json + from agent.conversation_compression_feasibility import ensure_compression_feasibility_checked + home = tmp_path / "home" + home.mkdir() + monkeypatch.setenv("HERMES_HOME", str(home)) + url = "http://gateway.invalid/v1" + task = {"provider": "custom", "model": "small", "base_url": url} + entry = {"name": "work-gateway", "base_url": url, "models": {"small": {"context_length": 900_000}}} + (home / "config.yaml").write_text(json.dumps({"auxiliary": {"compression": task}, "custom_providers": [entry]})) + agent = _warm_agent_on(home, [entry]) + (home / "config.yaml").write_text(json.dumps({"auxiliary": {"compression": task}, "custom_providers": [entry]})) + client = MagicMock(base_url=url, api_key="test-key") + with patch("agent.auxiliary_client.get_text_auxiliary_client", return_value=(client, "small")): + ensure_compression_feasibility_checked(agent, 200_000) + assert agent.context_compressor.threshold_tokens == 750_000 + entry["models"]["small"]["context_length"] = 80_000 + (home / "config.yaml").write_text(json.dumps({"auxiliary": {"compression": task}, "custom_providers": [entry]})) + ensure_compression_feasibility_checked(agent, 200_000) + assert agent.context_compressor.threshold_tokens == 80_000 + def test_a_route_edited_during_the_probe_is_probed_again(monkeypatch, tmp_path): """The latch records the route the probe measured: an edit that lands while the probe runs is probed on the next request instead of being labelled with the earlier measurement."""