Skip to content

fix(agent): jittered backoff for empty-response retries (#35296 salvage) - #77608

Merged
kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/35296-empty-retry-backoff
Aug 3, 2026
Merged

fix(agent): jittered backoff for empty-response retries (#35296 salvage)#77608
kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/35296-empty-retry-backoff

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Salvage of #35296 by @arimu1 — commit cherry-picked to preserve authorship, plus one test-only follow-up commit.

Context — what this changes for users

Empty-response retries slept a fixed pattern; under repeated empty responses from a struggling endpoint, all Hermes instances retried in sync. arimu1 routes the wait through the existing shared jittered_backoff util (agent/retry_utils.py, already used at 2 other retry sites — good reuse), capped at 60s, with the interrupt path preserved (0.2s increments, activity touch every 30s, established interrupt pattern).

Review follow-up folded (test-only; production code untouched)

test_empty_response_retry_backoff_status busy-spun 7.5 WALL-CLOCK seconds: it mocked time.sleep to a no-op while the loop gates on real time.time() < sleep_end. Folded a fake clock advanced by each sleep amount (pattern precedent: test_session_activity_persist.py). Suite time for the empty-response tests: 7.5s+ -> ~1s.

Verification

  • 233 run_agent tests green (incl. 2 new); mutation: prod reverted -> both new tests fail; restored green.
  • Jitter determinism in tests via monkeypatched jittered_backoff (no CI flakiness).
  • Zero interaction with the fix: exponential backoff for rate-limit fallback cooldown #30223 rate-limit salvage (different retry sites, disjoint state — verified).

Closes #35296.

arimu1 and others added 2 commits August 3, 2026 14:56
Empty content retries previously fired back-to-back with no delay,
wasting up to 3 rapid API calls, and could not be cancelled mid-wait.
Apply the same jittered_backoff() already used for rate-limit and
API-error retries, sleeping in small increments so a user interrupt
aborts the wait instead of blocking until it elapses.

Fixes NousResearch#35230
The retry loop gates on real time.time() < sleep_end; with sleep mocked
to a no-op the test hot-spun 7.5 wall-clock seconds. Advance a fake
clock by each sleep amount instead (pattern precedent:
test_session_activity_persist.py).
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) August 3, 2026 11:40
@kshitijk4poor
kshitijk4poor merged commit b953a5a into NousResearch:main Aug 3, 2026
35 checks passed
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Aug 3, 2026
@Ruanjq98

Ruanjq98 commented Aug 3, 2026

Copy link
Copy Markdown

Code Review: #77608

Verdict: Approve

Delegate per-call model tests: comprehensive tests for model resolution.

LGTM - Reviewed diff. Changes are sound.

@kshitijk4poor
kshitijk4poor deleted the salvage/35296-empty-retry-backoff branch August 5, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants