diff --git a/agent/agent_init.py b/agent/agent_init.py index 188060bd7f2..107e78cd511 100644 --- a/agent/agent_init.py +++ b/agent/agent_init.py @@ -77,16 +77,54 @@ def _build_codex_gpt5_autoraise_notice(autoraise: Dict[str, float]) -> str: include the exact opt-back-out command. """ model = str(autoraise.get("model") or "gpt-5.4/5.5").strip().lower().rsplit("/", 1)[-1] + # gpt-5.3-codex-spark has a native 128K window; the gpt-5.4/5.5 family is + # capped at 272K by the Codex OAuth backend. + cap = "128K" if model.startswith("gpt-5.3-codex-spark") else "272K" from_pct = int(round(autoraise["from"] * 100)) to_pct = int(round(autoraise["to"] * 100)) return ( - f"ℹ Codex {model} caps context at 272K, so auto-compaction was raised " + f"ℹ Codex {model} caps context at {cap}, so auto-compaction was raised " f"to {to_pct}% (from {from_pct}%) to use more of the window before " f"summarizing.\n" f" Opt back out: hermes config set compression.codex_gpt55_autoraise false" ) +def _resolve_compression_threshold( + global_threshold: float, + model_cthresh: Optional[float], + *, + model: Optional[str] = None, + is_codex_autoraise: bool, +) -> tuple[float, Optional[Dict[str, Any]]]: + """Combine the user's global compaction threshold with a per-model override. + + Returns ``(effective_threshold, autoraise_notice)``. ``autoraise_notice`` is + ``{"model": , "from": , "to": }`` only when a Codex + autoraise (gpt-5.4/5.5 272K family or gpt-5.3-codex-spark) actually raises + the threshold, otherwise ``None``. + + The Codex overrides are *autoraises*: they must never LOWER a higher + user-configured threshold. A user who already set ``compression.threshold`` + above the raised value deliberately keeps more raw context, and silently + dropping them would both waste usable window and contradict the feature's + purpose (use more of the window). Other overrides (e.g. Arcee Trinity) + keep their existing unconditional behaviour. + """ + if model_cthresh is None: + return global_threshold, None + if is_codex_autoraise: + if model_cthresh <= global_threshold + 1e-9: + # Autoraise never lowers; keep the user's higher/equal threshold. + return global_threshold, None + return model_cthresh, { + "model": model, + "from": global_threshold, + "to": model_cthresh, + } + return model_cthresh, None + + def _normalized_custom_base_url(value: Any) -> str: if not isinstance(value, str): return "" @@ -1429,28 +1467,29 @@ def init_agent( from agent.auxiliary_client import ( _compression_threshold_for_model as _cthresh_fn, _is_codex_gpt54_or_gpt55 as _is_codex_gpt54_or_gpt55_fn, + _is_codex_spark as _is_codex_spark_fn, ) _model_cthresh = _cthresh_fn( agent.model, agent.provider, allow_codex_gpt55_autoraise=_codex_gpt55_autoraise, ) - if _model_cthresh is not None: - _prev_threshold = compression_threshold - compression_threshold = _model_cthresh - # Notify only for the Codex gpt-5.4 / gpt-5.5 autoraise (the Arcee - # Trinity override is a long-standing silent default). Skip the - # notice when the user's global threshold already meets/exceeds the - # raised value, since nothing actually changed for them. - if ( - _is_codex_gpt54_or_gpt55_fn(agent.model, agent.provider) - and _model_cthresh > _prev_threshold + 1e-9 - ): - agent._compression_threshold_autoraised = { - "model": agent.model, - "from": _prev_threshold, - "to": _model_cthresh, - } + # The Codex autoraises (gpt-5.4/5.5 272K family and gpt-5.3-codex-spark) + # apply only when they RAISE (never lower a user's higher global + # threshold). The notice is populated only when it actually fires, and + # carries the model slug so the banner names the right family. Arcee + # Trinity keeps its long-standing unconditional behaviour. + compression_threshold, agent._compression_threshold_autoraised = ( + _resolve_compression_threshold( + compression_threshold, + _model_cthresh, + model=agent.model, + is_codex_autoraise=( + _is_codex_gpt54_or_gpt55_fn(agent.model, agent.provider) + or _is_codex_spark_fn(agent.model, agent.provider) + ), + ) + ) except Exception: pass compression_enabled = str(_compression_cfg.get("enabled", True)).lower() in {"true", "1", "yes"} diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 1ed366d2439..2a1b5b085c2 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -417,7 +417,9 @@ def _compression_threshold_for_model( the global default (the caller passes the config flag through here). - gpt-5.3-codex-spark on the Codex OAuth route → 0.70, because the model has a native 128K window and the default 50% trigger would compact at - ~64K — wasting half the usable context. Gated by the same flag. + ~64K — wasting half the usable context. Not gated by the gpt-5.5 + opt-out flag: 128K is the model's native window, so the raise is + unambiguously correct. Returns a float in (0, 1] to override the global ``compression.threshold`` config value, or ``None`` to leave the user's config value unchanged. @@ -426,7 +428,7 @@ def _compression_threshold_for_model( return 0.75 if allow_codex_gpt55_autoraise and _is_codex_gpt54_or_gpt55(model, provider): return _CODEX_GPT54_GPT55_COMPACTION_THRESHOLD - if allow_codex_gpt55_autoraise and _is_codex_spark(model, provider): + if _is_codex_spark(model, provider): return _CODEX_SPARK_COMPACTION_THRESHOLD return None diff --git a/tests/agent/test_arcee_trinity_overrides.py b/tests/agent/test_arcee_trinity_overrides.py index edb1f7250b5..96a8fe9ff04 100644 --- a/tests/agent/test_arcee_trinity_overrides.py +++ b/tests/agent/test_arcee_trinity_overrides.py @@ -13,6 +13,7 @@ from __future__ import annotations import pytest +from agent.agent_init import _resolve_compression_threshold from agent.auxiliary_client import ( _compression_threshold_for_model, _fixed_temperature_for_model, @@ -248,3 +249,68 @@ def test_compression_threshold_codex_spark_not_gated_by_gpt55_optout() -> None: ) == 0.70 ) + + +# ── _resolve_compression_threshold (init_agent application logic) ──────────── +# +# The Codex overrides are *autoraises*: they raise the trigger (0.85 for the +# gpt-5.4/5.5 272K family, 0.70 for spark) but must never LOWER a higher +# user-configured global threshold. + + +def test_resolve_codex_autoraise_raises_from_default() -> None: + # Default 0.50 global → raised to 0.85, notice emitted. + effective, notice = _resolve_compression_threshold( + 0.50, 0.85, model="gpt-5.5", is_codex_autoraise=True + ) + assert effective == 0.85 + assert notice == {"model": "gpt-5.5", "from": 0.50, "to": 0.85} + + +def test_resolve_codex_autoraise_never_lowers_higher_threshold() -> None: + # Regression: a user who set compression.threshold above 0.85 must keep it. + # The autoraise previously clobbered it down to 0.85 (and silently, since + # the notice was suppressed when nothing "raised"). + effective, notice = _resolve_compression_threshold( + 0.90, 0.85, model="gpt-5.5", is_codex_autoraise=True + ) + assert effective == 0.90 + assert notice is None + + +def test_resolve_codex_spark_autoraise_never_lowers_higher_threshold() -> None: + # Same never-lower contract for the spark autoraise (0.70). + effective, notice = _resolve_compression_threshold( + 0.80, 0.70, model="gpt-5.3-codex-spark", is_codex_autoraise=True + ) + assert effective == 0.80 + assert notice is None + + +def test_resolve_codex_autoraise_equal_threshold_is_noop() -> None: + # User already at exactly the raised value: keep it, no notice. + effective, notice = _resolve_compression_threshold( + 0.85, 0.85, model="gpt-5.5", is_codex_autoraise=True + ) + assert effective == 0.85 + assert notice is None + + +def test_resolve_no_override_keeps_global() -> None: + # No per-model override (model_cthresh is None) → global threshold, no notice. + effective, notice = _resolve_compression_threshold( + 0.50, None, is_codex_autoraise=False + ) + assert effective == 0.50 + assert notice is None + + +def test_resolve_non_codex_override_applies_unconditionally() -> None: + # Arcee Trinity (0.75) keeps its long-standing unconditional behaviour: it + # applies even when it lowers the user's global value, and never emits the + # codex autoraise notice. + effective, notice = _resolve_compression_threshold( + 0.90, 0.75, is_codex_autoraise=False + ) + assert effective == 0.75 + assert notice is None