refactor(agent): name the Codex first-progress budget; reuse the watchdog test helper
`HIGH_EFFORT_SILENCE_FLOOR_SECONDS` is documented as the reasoning-effort >= high floor and gated on effort by `_high_effort_silence_floor()`, yet the first-progress budget applied it to every large official-Codex request regardless of effort. Someone tuning the high-effort floor would silently retune the first-progress fuse. `CODEX_FIRST_PROGRESS_TIMEOUT_SECONDS` carries the same 300s, so no behaviour changes; it just gets its own knob and rationale. The new lifecycle-only test re-implemented `_shorten_implicit_idle_watchdog` inline. The helper now accepts field overrides, and the test also inherits its env cleanup so a polluted runner env cannot push the resolver off the implicit branch.
This commit is contained in:
@@ -1098,6 +1098,13 @@ class _RequestClientRegistry:
|
||||
# timeout's run-budget cap is applied AFTER this floor (AIAgent._compute_non_stream_stale_timeout).
|
||||
HIGH_EFFORT_SILENCE_FLOOR_SECONDS = 300.0
|
||||
|
||||
# First-progress budget for a lifecycle-only stream on an official-Codex large request: the
|
||||
# stream opened but no substantive model event has arrived. Measured from the physical-attempt
|
||||
# start (a reconnect restarts it; lifecycle frames do not), and applied regardless of reasoning
|
||||
# effort. Equal to the high-effort floor today, but a separate knob so tuning one cannot silently
|
||||
# retune the other.
|
||||
CODEX_FIRST_PROGRESS_TIMEOUT_SECONDS = 300.0
|
||||
|
||||
|
||||
def _high_effort_silence_floor(agent) -> float:
|
||||
"""``HIGH_EFFORT_SILENCE_FLOOR_SECONDS`` when the wire reasoning config is enabled at ``high`` or any
|
||||
@@ -1216,7 +1223,7 @@ def _resolve_nonstream_watchdogs(agent, api_kwargs: dict) -> _NonStreamWatchdogs
|
||||
idle_timeout=idle_timeout, idle_requires_progress=progress_gated,
|
||||
# A lifecycle frame proves transport liveness, not model progress. Bound that phase
|
||||
# from the physical-attempt start; events cannot restart the grace period.
|
||||
progress_timeout=HIGH_EFFORT_SILENCE_FLOOR_SECONDS if progress_gated else 0.0)
|
||||
progress_timeout=CODEX_FIRST_PROGRESS_TIMEOUT_SECONDS if progress_gated else 0.0)
|
||||
|
||||
|
||||
def _codex_silent_hang_hint(agent, api_kwargs: dict) -> Optional[str]:
|
||||
|
||||
@@ -71,14 +71,18 @@ def _make_codex_agent(
|
||||
return agent
|
||||
|
||||
|
||||
def _shorten_implicit_idle_watchdog(monkeypatch, helpers, timeout=2.0):
|
||||
"""Keep the resolver on its implicit branch while scaling time for tests."""
|
||||
def _shorten_implicit_idle_watchdog(monkeypatch, helpers, timeout=2.0, **overrides):
|
||||
"""Keep the resolver on its implicit branch while scaling time for tests.
|
||||
|
||||
``timeout`` shortens ``idle_timeout``; ``overrides`` set any other resolved field."""
|
||||
monkeypatch.delenv("HERMES_CODEX_EVENT_STALE_TIMEOUT_SECONDS", raising=False)
|
||||
original = helpers._resolve_nonstream_watchdogs
|
||||
|
||||
def resolve(agent, api_kwargs):
|
||||
watchdogs = original(agent, api_kwargs)
|
||||
watchdogs.idle_timeout = timeout
|
||||
for field, value in overrides.items():
|
||||
setattr(watchdogs, field, value)
|
||||
return watchdogs
|
||||
|
||||
monkeypatch.setattr(helpers, "_resolve_nonstream_watchdogs", resolve)
|
||||
@@ -343,15 +347,7 @@ def test_large_codex_lifecycle_only_stream_hits_attempt_progress_budget(tmp_path
|
||||
from agent import chat_completion_helpers as h
|
||||
|
||||
agent = _make_codex_agent(tmp_path, monkeypatch)
|
||||
original = h._resolve_nonstream_watchdogs
|
||||
|
||||
def resolve(agent, kwargs):
|
||||
wd = original(agent, kwargs)
|
||||
wd.ttfb_timeout = 0.9
|
||||
wd.progress_timeout = 0.9
|
||||
return wd
|
||||
|
||||
monkeypatch.setattr(h, "_resolve_nonstream_watchdogs", resolve)
|
||||
_shorten_implicit_idle_watchdog(monkeypatch, h, ttfb_timeout=0.9, progress_timeout=0.9)
|
||||
closes = []
|
||||
|
||||
def stream_attempt():
|
||||
|
||||
Reference in New Issue
Block a user