Repository navigation
fix(realtime): surface an upstream handshake refusal as an error event and policy close - #42388
Conversation
…t and policy close Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
|
|
bugbot run |
|
| """A 401 from the upstream realtime handshake must reach the client as an | ||
| error event plus a 1008 close; on unfixed code the handler just logs and | ||
| returns, so the client sees an abnormal 1006 with no event.""" |
There was a problem hiding this comment.
Test docstrings violate policy
This explanatory docstring, and the matching one in tests/test_litellm/llms/openai/realtime/test_openai_realtime_handler.py:423-424, repeat behavior already stated by the test names. The repository's AGENTS.md directive permits comments only for necessary complex logic, tool directives, or TODO/FIXME notes. Remove both docstrings to satisfy this requirement before merging.
Context Used: AGENTS.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Fixed in 9c873e1: both docstrings are removed, the test names already say what is being checked
…rror Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit cd9636c. Configure here.
…t and policy close (BerriAI#42388) * fix(realtime): surface an upstream handshake refusal as an error event and policy close Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(realtime): tidy the handshake refusal e2e Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(realtime): keep upstream exception text out of the Azure client error Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(realtime): map handshake refusal close codes with a lookup Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> (cherry picked from commit 2bab39e)
…02.1) (#736) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/berriai/litellm](https://images.chainguard.dev/directory/image/wolfi-base/overview) ([source](https://github.com/BerriAI/litellm)) | patch | `v1.102.0` → `v1.102.1` | --- ### Release Notes <details> <summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary> ### [`v1.102.1`](https://github.com/BerriAI/litellm/releases/tag/v1.102.1) [Compare Source](BerriAI/litellm@v1.102.0...v1.102.1) ##### Verify Docker Image Signature All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](BerriAI/litellm@0112e53). **Verify using the pinned commit hash (recommended):** A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \ ghcr.io/berriai/litellm:v1.102.1 ``` **Verify using the release tag (convenience):** Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/v1.102.1/cosign.pub \ ghcr.io/berriai/litellm:v1.102.1 ``` Expected output: ``` The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key ``` *** ##### What's Changed - fix(anthropic): backport [#​42152](BerriAI/litellm#42152) and [#​42288](BerriAI/litellm#42288) to stable/1.102.x for v1.102.1 by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42538](BerriAI/litellm#42538) - feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42595](BerriAI/litellm#42595) - chore(release): backport [#​42388](BerriAI/litellm#42388) and [#​41462](BerriAI/litellm#41462) to stable/1.102.x by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42618](BerriAI/litellm#42618) **Full Changelog**: <BerriAI/litellm@v1.102.0...v1.102.1> </details> --- ### Configuration 📅 **Schedule**: (in timezone Europe/London) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDguMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEwOC4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.hayden.moe/hayden/phoebe/pulls/736
…02.1) (#2200) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/berriai/litellm](https://images.chainguard.dev/directory/image/wolfi-base/overview) ([source](https://github.com/BerriAI/litellm)) | patch | `v1.102.0` → `v1.102.1` | --- >⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/6) for more information. --- ### Release Notes <details> <summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary> ### [`v1.102.1`](https://github.com/BerriAI/litellm/releases/tag/v1.102.1) [Compare Source](BerriAI/litellm@v1.102.0...v1.102.1) #### Verify Docker Image Signature All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](BerriAI/litellm@0112e53). **Verify using the pinned commit hash (recommended):** A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \ ghcr.io/berriai/litellm:v1.102.1 ``` **Verify using the release tag (convenience):** Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/v1.102.1/cosign.pub \ ghcr.io/berriai/litellm:v1.102.1 ``` Expected output: ``` The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key ``` *** #### What's Changed - fix(anthropic): backport [#​42152](BerriAI/litellm#42152) and [#​42288](BerriAI/litellm#42288) to stable/1.102.x for v1.102.1 by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42538](BerriAI/litellm#42538) - feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42595](BerriAI/litellm#42595) - chore(release): backport [#​42388](BerriAI/litellm#42388) and [#​41462](BerriAI/litellm#41462) to stable/1.102.x by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42618](BerriAI/litellm#42618) **Full Changelog**: <BerriAI/litellm@v1.102.0...v1.102.1> </details> --- ### Configuration 📅 **Schedule**: (in timezone Europe/Rome) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDQuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEwNC4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.aresu.eu/GiorgioAresu/home-ops/pulls/2200
…02.1) (#267) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/berriai/litellm](https://images.chainguard.dev/directory/image/wolfi-base/overview) ([source](https://github.com/BerriAI/litellm)) | patch | `v1.102.0` → `v1.102.1` | --- ### Release Notes <details> <summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary> ### [`v1.102.1`](https://github.com/BerriAI/litellm/releases/tag/v1.102.1) [Compare Source](BerriAI/litellm@v1.102.0...v1.102.1) #### Verify Docker Image Signature All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](BerriAI/litellm@0112e53). **Verify using the pinned commit hash (recommended):** A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \ ghcr.io/berriai/litellm:v1.102.1 ``` **Verify using the release tag (convenience):** Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/v1.102.1/cosign.pub \ ghcr.io/berriai/litellm:v1.102.1 ``` Expected output: ``` The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key ``` *** #### What's Changed - fix(anthropic): backport [#​42152](BerriAI/litellm#42152) and [#​42288](BerriAI/litellm#42288) to stable/1.102.x for v1.102.1 by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42538](BerriAI/litellm#42538) - feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42595](BerriAI/litellm#42595) - chore(release): backport [#​42388](BerriAI/litellm#42388) and [#​41462](BerriAI/litellm#41462) to stable/1.102.x by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​42618](BerriAI/litellm#42618) **Full Changelog**: <BerriAI/litellm@v1.102.0...v1.102.1> </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDUuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEwNS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.greyrock.io/todd/home-ops/pulls/267
TLDR
Problem this solves:
InvalidStatusCode, which websockets 15 no longer raisesHow it solves it:
websockets.exceptions.InvalidStatusin every realtime relayerrorevent naming the upstream HTTP status, then close with a mapped codeInternal server errorevent and close 1011; the exception text stays in the proxy logUser Flow
Before: a developer connecting to a realtime deployment whose Azure credential is rejected sees the socket vanish with no explanation
wscat -c "wss://litellm-domain/v1/realtime?model=azure-realtime" -H "Authorization: Bearer sk-..."Connected (press CTRL+C to quit)Disconnected (code: 1006, reason: "")After: the same connection tells them exactly what the upstream said and closes cleanly
wscat -c "wss://litellm-domain/v1/realtime?model=azure-realtime" -H "Authorization: Bearer sk-..."Connected (press CTRL+C to quit){"type": "error", "error": {"type": "server_error", "message": "Upstream realtime handshake rejected with HTTP 401"}}Disconnected (code: 1008, reason: "Upstream realtime handshake rejected with HTTP 401")Relevant issues
Reported by a customer (Pylon #8933)
Affected release
Linear ticket
Resolves LIT-7058
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Live before and after with the reporter's client,
wscat, against real Azure OpenAI (gpt-realtime,api_version: 2025-08-28, GA protocol) and real OpenAIgpt-realtime. Each leg is one proxy booted from the named commit with--num_workers 2on a random free port, no database, model list from the config below. The checkouts share one venv throughPYTHONPATHbecause the PR changes no dependency, andlitellm.__file__was checked to resolve inside each worktree before the boot. The author's video of the same before and after run, captured on cd9636c: https://berriaillm.slack.com/archives/C0C04J86WTC/p1790054146396189?thread_ts=1790039088.400839&cid=C0C04J86WTCConfig shared by every leg (
AZURE_SWEDEN_API_BASE,AZURE_SWEDEN_API_KEY,OPENAI_API_KEY, andLITELLM_MASTER_KEYcome from the environment):Client, run interactively in a tmux pane per model (wscat only prints its
Disconnectedline in interactive mode), pane captured 14 s after connecting:Before (a59ecfb, the merge base, port 22510, 2 workers)
azure-realtime-refused, Azure refuses the websocket upgrade with HTTP 401:openai-realtime-refused, OpenAI accepts the upgrade and rejects the key in band (control):azure-realtime-validandopenai-realtime-valid(controls):session.createdarrived (sess_EQyQ5gs7fBc8Ahn3kkg8T,sess_EQyQKumAfI21IPiIGheLA) and both sockets stayed openAfter (cd9636c, the PR tip, port 49813, 2 workers)
azure-realtime-refused:openai-realtime-refused(control): identical to before, the in-band OpenAIinvalid_api_keyevent, the relayed server_error, thenDisconnected (code: 3000, reason: "invalid_request_error.invalid_api_key")azure-realtime-validandopenai-realtime-valid(controls):session.createdarrived (sess_EQyQBiguK2SA0B6FyAwMv,sess_EQyQQBCXCTo43ydZ5WKKA) and both sockets stayed openMerged into current main (d1afec12b9 = cd9636c merged into f275be5, port 37420, 2 workers)
Same four results as After:
azure-realtime-refusedgot the error event andDisconnected (code: 1008, reason: "Upstream realtime handshake rejected with HTTP 401"),openai-realtime-refusedstill closed 3000 in band, and both valid deployments gotsession.created(sess_EQyUUxwESdjWJnwXX0b45,sess_EQyUkuIi1uNw0oxGeWw5Q)Observations from the run:
OpenAI relay, upstream refuses the upgrade (side leg at a59ecfb and cd9636c)
The OpenAI relay's new branch needs a host that refuses the HTTP upgrade, and OpenAI's realtime endpoint never does (it accepts a bad key and refuses in band, the control above).
OpenAIRealtime._construct_urlkeeps theapi_basehost and sets the path to/v1/realtime, so a deployment withapi_base: https://platform.openai.comdialswss://platform.openai.com/v1/realtime, which refuses the upgrade with HTTP 403. One extra proxy per commit, booted the same way as the legs above (--num_workers 2, no database, random free port), with this config:Before (a59ecfb, port 56557):
npx --yes wscat -c 'ws://localhost:56557/v1/realtime?model=openai-realtime-refused-upgrade' -H 'Authorization: Bearer <master key>'No error event: the dead
InvalidStatusCodebranch is skipped and the generic except closes 1011 with the exception text as the reasonAfter (cd9636c, port 42468):
npx --yes wscat -c 'ws://localhost:42468/v1/realtime?model=openai-realtime-refused-upgrade' -H 'Authorization: Bearer <master key>'Type
🐛 Bug Fix
✅ Test
Caveats (if any)
No severe, high, or medium caveats at cd9636c
Low
Each of these stays as is because fixing it costs more than it returns: every new commit re-enters the bot, CI, and QA loop, and CircleCI runs the branch tip, so a fix that landed on main after this branch's merge base reaches the non-required jobs below only through the merge itself, while merging main in would reset the bot verdicts and the per-commit QA
websocket.close(code=e.status_code)could not have worked even when the branch was reachable. A finer map (404, 5xx) would be a new client contract nobody has asked forInternal server errorevent before its 1011, while the OpenAI relay's generic except still closes 1011 with the redacted exception text in the reason and no event (pre-existing on both relays). Unifying the two generic paths is follow-up scope, since it widens the diff past the handshake refusal this PR fixesclose_after_upstream_handshake_refusalswallows a failedsend_textbut does not wrapwebsocket.close; a close that raises lands in the proxy layer's existing generic except and finally, which already close the client socket, so wrapping it would only hide that failureInvalidStatusbranch logs a full traceback (verbose_proxy_logger.exception) per refused handshake, one stack per bad-credential connection in the proxy log, the same way its pre-existing generic branch does. Cosmetic, and a.warningswap is a re-review round for a log lineCloseCodeis imported inside the helper's functions rather than at module level. Tidier at module level, not worth a re-review roundscripts/type_check_gate.pyand budget gates were not runnable in the authoring environment (no PyPI access to build the typecheck venv); basedpyright was run directly on the changed files with no new diagnostics, and CircleCIlintis green at the tipunitat cd9636c (2201155, same on the first run 2201017) is red only ontests/unit/enterprise/enterprise_callbacks/test_secret_detection.py::test_scan_message_stays_linear_on_adversarial_credential_lines[assignment-flood](wall-clock bound, 11.1 s against 10 s); main's own pipelines fail it the same way (90021, 90023, 90056), and test(unit): make bedrock collector and secret scan timing tests deterministic #42405 (691c0d6, merged 2026-09-22 16:30Z, after this branch's merge base) made that test deterministic on mainllm_translation_testingat cd9636c (2201156, the identical list on the first run 2201043) is red on 12 tests, none touching realtime: 10 intests/llm_translation/test_fireworks_ai_translation.py(test_transform_inline_no_longer_addedx5,test_document_inlining_examplex2,test_global_disable_flag_no_longer_adds_transform_inlinex2,test_global_disable_flag_with_transform_messages_helper), red on main since fix(registry): add MAI-Image-2.5-Pro pricing, fix Fireworks/Together entries, absorb verified open registry PRs, add Groq deprecation and Bedrock regional Qwen3 Next pricing #34941 (701c2b7, in this branch's merge base) flippedsupports_visionon the cost-map row those tests assert against (90056, 90060);test_optional_params.py::test_drop_nested_params_add_prop_and_strict[hosted_vllm-my-vllm-model], fixed on main by fix(utils): stop a nested additional_drop_params entry from crashing openai-compatible calls #42492 (d47e72f, merged 2026-09-22 17:50Z, after this branch's merge base); andtest_bedrock_moonshot.py::TestBedrockMoonshotInvoke::test_json_response_format_stream, red on the same main runslocal_testing_part1at cd9636c (2201154) is red ontests/local_testing/test_get_model_info.py::test_get_model_info_bedrock_cross_region_capability_parityand::test_get_model_info_bedrock_models; both went red on main with chore(prices): sync OpenRouter prices: 7 models #42261 (1fbfb46, in this branch's merge base) and main has failed them on every run since, 89979 through 90060, none touching realtimee2e_ui_testingat cd9636c is red only ontests/mcp/mcpTools.spec.ts"MCP Tools tab lists the tools the upstream server advertises" on both runs (2201001: 1 failed, 153 passed; rerun 2201153: 1 failed, 151 passed, 2 flaky that passed on retry); main failed that same spec on 90021, 90023, and 90036, and test(e2e-ui): check the MCP Tools tab against the upstream's own tools/list #42397 (b96842f, merged after this branch's merge base) fixed it, so main's 90056 and 90060 passintegration-cost(2201099: 23test_case_bills_expected_costcases over gpt-5.3-codex, gpt-5.5-pro, gpt-5.6, and azure gpt-5.6 streams billing 0.0) andintegration-providers(2201098:test_fal_ai_video_wire::test_fal_h3_video_create_uses_canonical_body_and_status_path) at cd9636c fail the same lists as main's 90021 and 90023; fix(logging): price terminal Responses stream events from their inner response #42385 (3bbbf7f, merged after this branch's merge base) fixed both on main, so main's 90036, 90056, and 90060 pass both jobs, and LIT-8312 tracks the fal rede2e-testsbuild 16377 at cd9636c (the triggeredlitellm-e2e-prbuild 629) is red on 8 of 186 selected tests, none of them realtime: sixtest_xiaomi_mimo_e2e.pycases with an upstream 401Invalid API Key,test_embeddings_endpoint_e2e.py::test_cohere_embeddings_returns_vectortiming out, andtest_ocr_rust_e2e.py::test_rust_ocr_response[mistral]rate limited with a 429; the PR'stest_upstream_handshake_refusal_is_an_error_event_and_policy_close[azure-bad-key]passed in that build/live-pr-risk at cd9636c
Breaking: none observed. Every path driven live (Azure refused, Azure valid, OpenAI refused in band, OpenAI valid, and the OpenAI relay against an upgrade-refusing host) answered the same on the merge base, the tip, and the tip merged into main, except the handshake refusals the PR targets: Azure 401 on all three legs and the OpenAI relay's 403 on the side leg
Backward incompatible: the client-visible outcome of an upstream handshake refusal changes from a bare 1006 with no frame (Azure relay) or a 1011 from the generic except (OpenAI relay,
llm_http_handlerrealtime and responses websocket, proxy fallback, where theInvalidStatusCodebranch was dead) to anerrorevent plus 1008 for 401/403, 1013 for 429, 1011 otherwise. That change is the fix the linked ticket asks for. The Azure relay's generic failure now also sends a fixedInternal server errorevent before the 1011 it already sent; observable only on a non-handshake failure, not driven live hereRegression risk:
llm_http_handler.async_realtime(thebase_llm_http_handlerroute,vertex_aiincluded) andasync_realtime_responses, theproxy_server.realtime_websocket_endpointfallback (reachable only when a handler letsInvalidStatusescape, which none of the changed handlers do now), and the Azure generic-except error event are covered by the unit tests in the four mapped test files but were not driven live, because no provider in this rig refuses the upgrade on those routes (OpenAI's realtime endpoint accepts it and refuses in band; the OpenAI relay's own refusal branch was driven live through the side leg above)Dependency graph, from the string dispatch in
litellm/realtime_api/main.py:azure_realtime.async_realtimeverified live on all three legs,openai_realtime.async_realtimeverified live on the accepted-upgrade path (all three legs) and on the refused-upgrade path (side leg at the merge base and the tip),base_llm_http_handler.async_realtimeandasync_realtime_responsesunit-tested,proxy_server.realtime_websocket_endpointfallback unit-tested,_ProxyClientWebSocket.send_text(new duck-typed method the helper needs) verified live on the Azure leg,bedrock_realtimeandxai_realtimeuntouched and carry noInvalidStatusbranch. The legacy suites undertests/llm_translation/realtime/run in CircleCIrealtime_translation_testing, green at the tip.uv.lockandpyproject.tomlunchangedNot verified: Bedrock, xAI, and Vertex AI realtime refusals (no refusing deployment available on those routes), the responses websocket refusal, and the Azure generic-except path (needs a non-handshake failure)
QA runbook
errorevent and a 1008 close instead of a bare 1006AZURE_API_BASEset to a real Azure OpenAI resource that hosts agpt-realtimedeployment{"model_name": "azure-realtime-refused", "litellm_params": {"model": "azure/gpt-realtime", "api_key": "invalid-e2e-key", "api_version": "2025-08-28", "realtime_protocol": "GA"}}wscat -c "ws://localhost:4000/v1/realtime?model=azure-realtime-refused" -H "Authorization: Bearer <key>"{"type": "error", "error": {"type": "server_error", "message": "Upstream realtime handshake rejected with HTTP 401"}}Disconnected (code: 1008, reason: "Upstream realtime handshake rejected with HTTP 401")Final Attestation
Link to Devin session: https://app.devin.ai/sessions/5865afbb93d74388884fa3cf89f5ec0d
Open in Devin Desktop: https://app.devin.ai/desktop/session/5865afbb93d74388884fa3cf89f5ec0d?variant=devin