fix(compression): keep Codex gpt-5.5 autoraise from lowering a higher threshold

The Codex gpt-5.5 compaction autoraise (#40957) overrode the effective
threshold unconditionally. If a user had set compression.threshold above
0.85, agent_init dropped them down to 0.85. That wastes usable window and
contradicts the feature's whole point: use more of the context, not less.
It happened silently too, since the one-time notice is suppressed when the
override doesn't raise.

The override is an autoraise. It must only raise. Pulled the apply logic
into a small pure helper that clamps the Codex case to never lower a
higher-or-equal user threshold, and emits the notice only when it actually
fires. Other overrides (Arcee Trinity) keep their existing unconditional
behavior.

Fixes the Codex gpt-5.5 compaction autoraise lowering a user's higher
configured threshold. A user on the Codex OAuth route with
compression.threshold > 0.85 was silently clamped to 0.85, compacting
earlier than they asked and using less of the 272K window the feature was
meant to unlock. The autoraise now only ever raises.

N/A

- [x] 🐛 Bug fix (non-breaking change that fixes an issue)
- [ ]  New feature (non-breaking change that adds functionality)
- [ ] 🔒 Security fix
- [ ] 📝 Documentation update
- [ ]  Tests (adding or improving test coverage)
- [ ] ♻️ Refactor (no behavior change)
- [ ] 🎯 New skill (bundled or hub)

- `agent/agent_init.py`: added `_resolve_compression_threshold()`, a pure
  helper that combines the global threshold with a per-model override. The
  Codex gpt-5.5 autoraise never lowers a higher-or-equal user threshold;
  the notice is returned only when it actually raises. Rewired `init_agent`
  to call it, replacing the unconditional `compression_threshold = _model_cthresh`.
- `tests/agent/test_arcee_trinity_overrides.py`: added 5 cases for the
  helper — raise from default, never-lower regression, equal-is-noop,
  no-override passthrough, and non-codex (Trinity) unconditional apply.

1. Set `compression.threshold: 0.90` and run gpt-5.5 on provider `openai-codex`.
2. Before: effective threshold drops to 0.85, no notice. After: stays 0.90.
3. Run `scripts/run_tests.sh tests/agent/test_arcee_trinity_overrides.py`.
   Stash `agent/agent_init.py` and the new cases fail; restore and they pass.

- [x] I've read the [Contributing Guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md)
- [x] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) (`fix(scope):`, `feat(scope):`, etc.)
- [x] I searched for [existing PRs](https://github.com/NousResearch/hermes-agent/pulls) to make sure this isn't a duplicate
- [x] My PR contains **only** changes related to this fix/feature (no unrelated commits)
- [x] I've run `pytest tests/ -q` and all tests pass
- [x] I've added tests for my changes (required for bug fixes, strongly encouraged for features)
- [x] I've tested on my platform: macOS 15 (Darwin 25.5)

- [x] I've updated relevant documentation (README, `docs/`, docstrings) — or N/A
- [x] I've updated `cli-config.yaml.example` if I added/changed config keys — or N/A
- [x] I've updated `CONTRIBUTING.md` or `AGENTS.md` if I changed architecture or workflows — or N/A
- [x] I've considered cross-platform impact (Windows, macOS) per the [compatibility guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#cross-platform-compatibility) — or N/A
- [x] I've updated tool descriptions/schemas if I changed tool behavior — or N/A
This commit is contained in:
sasquatch9818 2026-06-07 23:29:27 +03:00 committed by Teknium
parent 0b6df665a9
commit bdca94e749
3 changed files with 126 additions and 19 deletions

View file

@ -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": <slug>, "from": <old>, "to": <new>}`` 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"}

View file

@ -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

View file

@ -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