fix(agent): add jittered backoff to empty-response retries - #35296
Conversation
|
Gentle bump — this has been sitting quiet for ~11 days. It's a small, isolated diff (1 file, 9 lines: jittered backoff + |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating a real retry burst. Remote main still takes the empty-response branch at agent/conversation_loop.py:4997-5008 with an immediate continue, so the underlying problem remains.
Problems
agent/conversation_loop.py:4128adds a one-shottime.sleep(wait_time). Current main's other retry waits deliberately pollagent._interrupt_requestedevery 200 ms and touch activity while waiting atagent/conversation_loop.py:4173-4198; this sleep would defer that handling for the full empty-response wait.- The existing empty-response test checks retry count only (
tests/run_agent/test_run_agent.py:4289-4308), and the shared fixture forcesjittered_backoff()to0.0(tests/run_agent/conftest.py:34-44). The new delay/status path is therefore untested.
Suggested changes
- Apply the change at the current empty-response branch and use the existing interrupt-aware incremental wait pattern instead of a single blocking sleep.
- Add a deterministic regression test for the backoff/status and interruption behavior.
Automated hermes-sweeper review.
| ) | ||
| time.sleep(wait_time) |
There was a problem hiding this comment.
Please avoid a one-shot sleep here. Current retry backoffs poll _interrupt_requested every 200 ms and touch activity (agent/conversation_loop.py:4173-4198 on main); use that interrupt-aware wait pattern so /stop is not deferred for the full empty-response delay.
|
Gentle bump — this has been quiet since the June 10 ping. Still a small, isolated change (jittered backoff + Rebased/mergeable against current |
7399f41 to
a8e6a3f
Compare
|
Thanks @teknium1 — addressed in the latest push. The empty-response retry wait is now interrupt-aware: it polls |
bad692c to
567b75a
Compare
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
567b75a to
b58d08e
Compare
|
Rebuilt this branch from scratch on current |
|
Thanks @arimu1 — clean work: reuses the shared jittered_backoff util, deterministic tests, interrupt path preserved. Salvaged with your authorship in #77608; the only fold is test-only (a fake clock — the backoff-status test was busy-spinning 7.5 wall-clock seconds because the loop gates on real time.time()). Closing in favor of the salvage. |
What does this PR do?
Empty-response retries fired back-to-back with no delay. When a model returns an empty response, the agent retried up to 3 times immediately — all within <1 second — then switched to the fallback provider and did the same. Worst case: 6 rapid-fire API calls with no wait.
This applies the same
jittered_backoff()already used for rate-limit and API-error retries, so the agent waits before each empty-response retry. The status buffer message now shows the countdown so the user knows what's happening.Related Issue
Fixes #35230
Type of Change
Changes Made
agent/conversation_loop.py(~line 4112): addedjittered_backoff(retry_count, base_delay=5.0, max_delay=60.0)andtime.sleep(wait_time)beforecontinuein the empty-response retry path. Updated log and status messages to include the wait time.How to Test
⚠️ Empty response from model — retrying (1/3) in 5sand waits before the next attemptChecklist
Code
fix(agent):)pytest tests/ -q— no test suite available in my local envDocumentation & Housekeeping
time.sleep()andjittered_backoff()are platform-agnosticScreenshots / Logs
Before — all 3 retries fire within 1 second:
After — each retry waits with jitter (5s base, 60s max):