Skip to content

fix(agent): add exponential backoff and chat_completion retry to stream API calls (#60029) - #60299

Open
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix/60029-stream-retry-eb
Open

fix(agent): add exponential backoff and chat_completion retry to stream API calls (#60029)#60299
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix/60029-stream-retry-eb

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Fixes three defects in _interruptible_streaming_api_call: (1) adds exponential backoff (1s/2s/4s/8s/16s capped at 30s), (2) adds non-streaming chat_completion handler, (3) adds per-attempt state cleanup. Closes #60029

…am API calls

_interruptible_streaming_api_call had no backoff (immediate retry),
only handled streaming endpoint, and leaked state across retries.
Added non-streaming chat_completion handler, exponential backoff
(1s/2s/4s/8s/16s capped at 30s), and per-attempt state cleanup.

Closes NousResearch#60029
@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 Jul 7, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: competes with #60031 for the same issue #60029. This PR (#60299) is a clean single-file agent/chat_completion_helpers.py change (backoff + non-streaming handler + per-attempt state reset). #60031 carries the full 3-part fix including the run_agent.py _is_provider_stream_parse_error extension, but its branch is contaminated (~19 unrelated files, stacked-bundle). Flagging the cluster for a maintainer to pick canonical.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for isolating the retry/backoff work.

Problems

  • The new non-streaming branch at agent/chat_completion_helpers.py:2555 is not the normal non-streaming route on current main: agent/conversation_loop.py:1358-1362 sends non-streaming calls to _interruptible_api_call, whose shared dispatch is _dispatch_nonstreaming_api_request at agent/chat_completion_helpers.py:240-293.
  • time.sleep() at agent/chat_completion_helpers.py:2550 cannot observe an interrupt that arrives during the wait. Current regression coverage requires retries to stop immediately (tests/run_agent/test_stream_interrupt_retry.py:45-77).
  • #60029 also requires chat-completions parse-error classification and Anthropic stale-close retry handling. This diff does not modify the gate that currently excludes chat-completions at run_agent.py:1130-1137.

Suggested changes

  • Rework the backoff around the current retry continuations with a cancellation-aware wait, remove the duplicate non-streaming path, and add deterministic retry/cancellation tests. Either include the remaining #60029 recovery cases or narrow the issue linkage.

Automated hermes-sweeper review.

if _stream_attempt > 0:
_backoff = min(2 ** (_stream_attempt - 1), 30)
import time as _time
_time.sleep(_backoff)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time.sleep() cannot observe /stop after the pre-attempt check at line 2540. Use a cancellation-aware wait so an interrupt during backoff cannot leave the retry worker sleeping until the full delay expires; current tests require retry interruption to be immediate.

if agent.api_mode == "anthropic_messages":
agent._try_refresh_anthropic_client_credentials()
result["response"] = _call_anthropic()
elif agent.api_mode == "chat_completions" and not api_kwargs.get("stream", True):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current non-streaming calls do not normally enter this helper: conversation_loop dispatches them to _interruptible_api_call, which uses _dispatch_nonstreaming_api_request. Please remove this parallel handler and place any needed non-streaming behavior on that established path.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 15, 2026
@teknium1 teknium1 added the area/streaming Streaming responses: gateway delivery, provider wire label Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/streaming Streaming responses: gateway delivery, provider wire comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Stream retry system has no exponential backoff, misses chat_completions parse errors, and breaks stale-detect reconnect

3 participants