fix(streaming): gate stale-timer reset on content chunks (ignore keepalive pings) - #34541
Open
sh940701 wants to merge 1 commit into
Open
fix(streaming): gate stale-timer reset on content chunks (ignore keepalive pings)#34541sh940701 wants to merge 1 commit into
sh940701 wants to merge 1 commit into
Conversation
…alive pings) The chat_completions streaming loop in `agent/chat_completion_helpers.py` resets `last_chunk_time` on every chunk, but the ChatGPT codex backend (`chatgpt.com/backend-api/codex`) keeps a stalled stream alive with SSE keepalive pings while delivering zero content tokens. The unconditional reset defeats both this detector AND the httpx read timeout — a hung request stays alive until the backend gives up (~10 min) instead of killing+reconnecting at `HERMES_STREAM_STALE_TIMEOUT` (the immediate retry succeeds in seconds). A trivial "pong" request was measured at 615s before the fix. Gate the reset on chunks that carry real progress (content / reasoning / tool-call / function-call deltas). Keepalive pings still drive the diagnostic counters but no longer mask the stall. Adds a regression test (`tests/agent/test_stream_stale_ping_gate.py`) that mirrors the exact predicate shape so a future refactor must preserve the invariant. Originally developed against an older `run_agent.py` loop on a local branch; re-ported here to the current upstream helper location.
sh940701
force-pushed
the
pr/streaming-stale-timer-gate
branch
from
May 29, 2026 10:07
f0b6c19 to
f29db6a
Compare
13 tasks
teknium1
reviewed
Jul 13, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for identifying a real mismatch in the stale-stream path. Current main still resets last_chunk_time for every streamed chunk at agent/chat_completion_helpers.py:2318, while the polling loop says it must detect SSE-ping-only streams at agent/chat_completion_helpers.py:3053-3073.
Problems
tests/agent/test_stream_stale_ping_gate.py:31reimplements the proposed predicate locally rather than exercisinginterruptible_streaming_api_call(). That test can pass even if the production timer assignment atagent/chat_completion_helpers.py:2318is reverted or diverges.
Suggested changes
- Put the predicate in a small private helper in
agent/chat_completion_helpers.pyand test that helper directly, or use a fake stream through the real streaming path and verify empty-choice chunks let the actual stale detector close the client.
This is an automated hermes-sweeper review.
|
|
||
| import time | ||
|
|
||
|
|
Contributor
There was a problem hiding this comment.
This duplicates the production predicate instead of testing it. Please extract the gate into a private helper in agent/chat_completion_helpers.py and import it here, or drive the real streaming loop; otherwise a regression in the source assignment can leave this suite green.
13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes a 10-minute stream stall on the ChatGPT codex backend (
chatgpt.com/backend-api/codex).The
chat_completionsstreaming loop inagent/chat_completion_helpers.pyresetslast_chunk_timeon every chunk. That backend keeps a stalled stream alive with SSE keepalive pings while delivering zero content tokens. The unconditional reset defeats both this detector AND the httpx read timeout — a hung request stays alive until the backend gives up (~10 min) instead of being killed and retried atHERMES_STREAM_STALE_TIMEOUT(the immediate retry succeeds in seconds).A trivial "pong" request was measured at 615 s before the fix and ~4 s with the immediate retry.
The fix gates the
last_chunk_timereset on chunks that carry real progress (content/reasoning/reasoning_content/tool_calls/function_calldeltas). Keepalive pings still drive the per-attempt diagnostic counters but no longer mask the stall.Related Issue
Fixes #
(No related issue — opening this PR to start that conversation. Happy to file one separately if preferred.)
Type of Change
Changes Made
agent/chat_completion_helpers.py(around the chat_completions stream loop, current upstream line ~1761) — replace the unconditionallast_chunk_time["t"] = time.time()with a content-progress predicate that mirrors the OpenAI chunk shape: reset only whenchoices[0].deltahas at least one ofcontent,reasoning_content,reasoning,tool_calls,function_call. Diagnostic counters (_diag["first_chunk_at"],_diag["chunks"],_diag["bytes"]) still see every chunk.tests/agent/test_stream_stale_ping_gate.py— new file. 8 unit tests:stale_elapsedcorrectly grows past 90 s thresholdHow to Test
pytest tests/agent/test_stream_stale_ping_gate.py -q— 8 tests pass.pytest tests/ -q— passes (no regressions).HERMES_STREAM_STALE_TIMEOUT=90would not fire if the backend was sending pings; after, the timer fires on the actual content stall and the retry loop reconnects.Checklist
Code
fix(streaming):)pytest tests/ -qand the suite passestests/agent/test_stream_stale_ping_gate.py— 8 tests)Documentation & Housekeeping
cli-config.yaml.example— N/A (no new config keys;HERMES_STREAM_STALE_TIMEOUTalready exists)CONTRIBUTING.md/AGENTS.md— N/ANotes for reviewer
run_agent.pychunk loop (commit53ba42ad4). The fix is re-ported here onto the currentchat_completion_helpers.pysurface; the test file is placed next to the new location (not the legacytests/run_agent/path).chat_completion_helpers.py:1725,:1974,:1997) on what look like Anthropic / different-provider paths. They're left untouched in this PR because the ping-stall pattern was only verified onchatgpt.com/backend-api/codex. Happy to widen the predicate if the maintainer wants symmetry; I'd suggest doing that as a follow-up so this PR stays scoped.