Skip to content

feat(api): improve WebSocket recovery and delivery reporting - #4213

Open
markstuart-oai wants to merge 1 commit into
mainfrom
castiron/promotions/pr-381
Open

markstuart-oai wants to merge 1 commit into
mainfrom
castiron/promotions/pr-381

Conversation

@markstuart-oai

@markstuart-oai markstuart-oai commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Apply the configured recovery policy to eligible WebSocket closures and report queued frames that could not be sent.

Changes

  • Recover from close codes 1001 and 1005 when reconnection is enabled; normal close 1000 without pending work still ends iteration.
  • Preserve typed and raw frame order through recovery and after an initial write fails. Keep once and persistent listener registrations independent.
  • For callers waiting on an ordinary queued send, report a failure of their own frame even if another caller sent it. Report known terminal closure when a waiting caller's frame remains unattempted. Preserve the original error from an active transport write.
  • A final close prevents the old flusher from starting frames already reported unsent, while keeping those frames available for the reader's report.
  • If recovery takes over queued work, sends remain deferred and nonblocking. The reader reports the remaining frames if recovery later fails; a successful return during recovery does not confirm delivery.

Compatibility

This update is intended for a minor release. If your application treats close codes 1001 or 1005 as completion, disable recovery or abort the connection when that work is complete. Handle WebSocketConnectionClosedError and inspect unsent_frames (or unsent_messages for text) when queued work cannot be delivered. Messages retained after an initial write fails count toward the queue's byte limit. Handle WebSocketQueueFullError when a later message cannot fit. Existing typed-send behavior with overridden raw-send methods is preserved.

Validation

  • Local WebSocket, Live, Realtime, Responses, queue, lifecycle, and example tests: 2,780 passed, 2 expected skips, including 24 tests for Azure and Responses changes from public main. Full type, Ruff, and format checks passed.
  • Regression checks cover identical queued frames from concurrent callers, failures and cancellation, terminal close during an active send, and reader-owned recovery.

@markstuart-oai
markstuart-oai requested a review from a team as a code owner October 30, 2026 02:19
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T09:16:37.063686Z 528c49c New commits
🔒 Security Review ✅ Completed 2026-10-30T09:17:36.376581Z 528c49c New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 28da09aebd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/openai/_websocket_connection.py Outdated
@markstuart-oai
markstuart-oai force-pushed the castiron/promotions/pr-381 branch from 28da09a to 1047dfd Compare October 30, 2026 02:49
Comment thread tests/test_websocket_recovery_regressions.py Fixed
@markstuart-oai

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 1047dfd0db

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1047dfd0db

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/openai/_websocket_connection.py Outdated
@markstuart-oai
markstuart-oai force-pushed the castiron/promotions/pr-381 branch from 1047dfd to 0c98073 Compare October 30, 2026 04:32
Comment thread tests/test_websocket_initial_backlog.py Fixed
Comment thread tests/test_websocket_terminal_flush.py Fixed
Comment thread tests/test_websocket_terminal_flush.py Fixed
Comment thread tests/test_websocket_terminal_flush.py Fixed
@markstuart-oai

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 0c98073efc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 0c98073efc

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@jbeckwith-oai jbeckwith-oai 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.

Approved 0c98073 after coordinator and independent static review. No actionable findings.

Complete Git tree is identical to the previously reviewed and approved internal candidate https://github.com/openai/openai-python-internal/pull/381 at d538d364be0eb98826c6c535dd99f4dc55e9ef82. Coordinator tree-ID comparison and independent recursive blob comparison confirm zero file differences. Reviewed current public feedback and retained the prior static review conclusions.

Hosted CI: {'SUCCESS': 17, 'SKIPPED': 9}. No candidate code or tests executed locally. This is an automated review on Justin’s behalf; it does not constitute a separate human-only review or authorize a merge/release.

Comment thread src/openai/_websocket_connection.py Outdated
Comment thread src/openai/_send_queue.py
@markstuart-oai
markstuart-oai force-pushed the castiron/promotions/pr-381 branch from 0c98073 to 35598e8 Compare October 30, 2026 06:42
Comment thread tests/test_websocket_terminal_flush.py Fixed

@jbeckwith-oai jbeckwith-oai 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.

Approved 35598e8. Static re-review of the exact tree increment and relevant source, with an independent reviewer: recovery exclusively owns the reconnecting transition, and flushers recheck ownership/current transport after acquiring the batch. Declined preparation retains unconsumed frames and their byte budget. Idle conditional enqueue avoids redundant UTF-8 allocation while preserving the locked admission recheck. The regression tests cover the important overlapping-flush, stale-waiter, cancellation and encoding cases. No remaining findings.

Hosted checks at review: {'SUCCESS': 16, 'SKIPPED': 9, 'IN_PROGRESS': 1}. Retaining CI follow-up. No candidate code or tests executed locally.

Comment thread src/openai/_websocket_connection.py
Castiron-Internal-PR: openai/openai-python-internal#381
Castiron-Source-SHA: 4343ad7f8f297487a52e0f24c50a2e6f4335ca9b
Castiron-Public-Base-SHA: 0599203
@markstuart-oai
markstuart-oai force-pushed the castiron/promotions/pr-381 branch from 35598e8 to 528c49c Compare October 30, 2026 09:14

@jbeckwith-oai jbeckwith-oai 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.

Approved at 528c49c after coordinator and independent static re-review. Exact tree comparison against the previously reviewed commit is limited to the queue, connection wrapper, and two regression-test files. Per-frame preparation fixes the stale-transport finding: an ordinary flusher yields its unattempted tail when recovery owns the connection. Per-frame receipts preserve the correct caller's error, reentrant sends avoid waiting on their own flush, and terminal close evacuates unattempted frames while retaining them for reader reporting. No remaining actionable findings in this increment.

Hosted CI is still in progress; retaining follow-up. No candidate code or tests executed locally.

release.set()
if owner_fails:
with pytest.raises(OSError) as got:
await owner
await owner
assert got.value is error
else:
await owner
else:
await owner
if joiner is not None:
await joiner
await asyncio.wait_for(joined.wait(), timeout=3)
joiner.cancel()
with pytest.raises(asyncio.CancelledError):
await joiner
assert ws.attempts == ["A", "B"]
finally:
release.set()
await owner
finally:
release.set()
with pytest.raises(OSError, match="older write closed transport"):
await owner
await owner
if joiner is not None:
with pytest.raises(ConnectionClosedOK):
await joiner
await asyncio.wait_for(joined[index].wait(), timeout=3)
release.set()
with pytest.raises(OSError) as got:
await owner
await owner
assert got.value is first_error
with pytest.raises(OSError) as got:
await waiters[0]
for index, waiter in enumerate(waiters[1:], start=2):
if index == failed_index:
with pytest.raises(OSError) as got:
await waiter

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants