fix(streaming): retry Responses before semantic output - #4580
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 35505dc4b1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ff839a0 to
5d44424
Compare
|
基于最新代码,合并了这个PR,跑了5个小时,未再见这个错误 |
|
HTTP 408 |
|
Related complete recovery work is now available in #4643. #4580 safely retains syntactic The new mode is bounded by attempts, retry-start window, bytes, and concurrent recoveries, and fails open to ordinary streaming on overflow/saturation. Maintainers can therefore keep #4580 as the minimal default-path fix or use #4643 for the stronger delayed-response guarantee. |
|
@yinkev We reproduced an adjacent pre-semantic failure after integrating this PR locally. The missing case is a complete provisional lifecycle frame, rather than only the split
This also reproduced after the client had successfully compacted the session to 24,987 input tokens (about a 96 KiB request body): four attempts returned HTTP 200 with zero usage and then closed before Would it make sense for #4580 to keep only provisional Responses lifecycle events ( This is narrower than the full-stream opt-in recovery in #4643: it never replays after semantic output, so it preserves the duplicate-output safety boundary described in #4527 while allowing downstream users to carry a single focused PR. |
|
Yes, that was the remaining hole. I pushed
The regression now matches your reproduction: attempt 1 emits a complete I kept #4643 separate because this PR still stops strictly at the pre-semantic retry boundary rather than adding full-attempt recovery. |
bf72dd3 to
032f5c8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 032f5c8bda
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7b28c7ac64
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 07aaac58be
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
补充一个生产环境中的剩余 case:我们本地集成了这个 PR,并扩展到缓存 2026-08-01 最近一小时的统计:
这说明 #4580 覆盖的“首个语义输出前”重试边界是有效的,但还不能处理首个文本、reasoning 或 tool 输出已经提交后发生的 incomplete EOF。这个 case 无法安全地在当前低延迟路径中直接重放,否则可能重复输出或重复工具调用;它更接近 #4643 的完整 attempt 缓冲、丢弃失败 attempt、只释放成功 attempt 的恢复模型。 建议至少在本 PR 的范围说明或回归说明中保留这个边界,并确保后续 #4643 覆盖:首个语义事件之后、 |
Merge the upstream fix/streaming branch (75140f6) that keeps OpenAI Responses SSE prefixes and provisional lifecycle events retryable inside the bounded bootstrap window, commits on the first output-bearing or terminal event, and discards failed attempts' buffered chunks. Post-merge fixes: - interrupt an in-flight bootstrap retry when the endpoint commits (select on the commit request vs the retry outcome) so the keep-alive heartbeat is not stalled by the retry attempt - gate provisional-frame holding/pausing on bootstrap retries being enabled or a committer present; default config keeps first-payload latency unchanged - keep-alive tick prefers a pending bootstrap failure over committing a heartbeat so pre-commit errors keep JSON error status - update PR tests to main's response.failed terminal error format and deterministic synchronization; drop a test whose intent is covered by the sync retry-header and commit-interrupt tests
Summary
response.queued,response.created, andresponse.in_progressas retryable even after their complete JSON payload arrives;[DONE], or unknown event;Refs #4527.
Root cause
Streaming bootstrap originally treated every non-empty translated chunk as the point of no return. The first fix kept split
event:prefixes retryable, but a complete provisional frame still committed because any non-emptydata:payload was considered semantic:If the upstream disconnected after that frame but before output or a terminal event, the client had still received no usable response content, yet
streaming.bootstrap-retrieswas disabled.The commitment rule is now event-aware only for the OpenAI Responses protocol. Provisional lifecycle events remain buffered under the existing 32-chunk / 64 KiB cap. All other protocols retain their existing first-deliverable-payload behavior. Broader recovery after semantic output remains outside this PR.
Tests
Added coverage for:
data:frames;[DONE], and unknown events committing the response;response.createdframe and then a 408-style error;Validation: