Hosted Responses API tools with no Chat Completions equivalent were forwarded
verbatim, so Codex 0.140+ got a 400 from the provider on every turn. The bridge
now drops tool_search and local_shell the same way it drops computer_use,
image_generation, and shell, and also drops parallel_tool_calls when no chat
tools remain, since chat completions only accepts it alongside tools
Resolves the conflicts with the WebSocket request defaults from main (PR #41881):
the relay keeps both custom_llm_provider and request_defaults, and a masked
response.create frame is re-serialized when the defaults changed it.
Keeps the first-frame routing hints (input, previous_response_id) out of the
deployment request defaults so they never get injected into later frames on the
same connection, with a regression test.
A deployment whose reasoning_effort is an object is copied through as
reasoning the way the HTTP mapper does it instead of being dropped, and
the relay re-asserts the response.create frame type after merging
extra_body so a type key inside it can never replace it. The lazy
OpenAPI snapshot goes back to main: the earlier regeneration came from a
Python 3.14 interpreter dedenting docstrings, which CI on 3.12 rejects
A content-less candidate is now kept as a choice whenever it carries a
finishReason, with the raw value on the choice's provider_specific_fields.
NO_IMAGE, IMAGE_RECITATION, IMAGE_OTHER and ESCALATION map to content_filter;
UNEXPECTED_TOOL_CALL and MISSING_THOUGHT_SIGNATURE map to stop. The
/v1/responses bridge reports content_filter and refusal as incomplete with
incomplete_details, and tool calls and reasoning no longer leak from one
candidate into the next.
RateLimitError and InternalServerError now carry the provider body, so
the OpenAI exception mapper keeps upstream codes like cyber_policy and
the upstream message instead of a generic mapped one
The proxy's response.failed event prefers the upstream body's code,
message, and type over the mapped exception's, and numeric error codes
in an error event map to their own HTTP status
The Responses id security hook keeps the id a client addressed under
`_litellm_addressed_response_id` in the request body so internal retries can
re-authorize it. On a model without a native Responses config that body is
bridged into `completion()` kwargs, the key was treated as a provider param,
and providers rejected it, so every follow-up turn carrying
`previous_response_id` returned 400.
Register the key in `all_litellm_params` so it is dropped before any provider
request, and share one constant between the hook and the param list.
Each route now has litellm/rust_bridge/<route>/{entrypoints,callbacks}.py and a
public dispatch module (litellm/chat_completions/dispatch.py,
litellm/responses/dispatch.py, litellm/messages/dispatch.py) that binds the
public call to the legacy Python signature, builds a frozen request, and asks
the runtime to pick Rust or Python from the catalog. The legacy implementations
stay in litellm/main.py, litellm/responses/main.py and the anthropic messages
handler, and litellm/__init__.py re-exports the dispatch names over them the
same way it already does for ocr
The per-handler shims in rust_bridge/chat_completions/native.py and
rust_bridge/messages/native.py are removed along with their call sites in the
anthropic and bedrock chat handlers and the http handler. The exception
mapping that every callbacks module repeated moves to rust_bridge/failures.py
and the signature binding helpers to rust_bridge/public_call.py
Move each route's bridge module under litellm/rust_bridge/<route>/ so a folder
means a Rust implementation exists while the catalog row says whether it is
used. OCR now keeps the Python implementation in litellm/ocr/main.py and the
Rust selection in litellm/ocr/rust.py, removing litellm/ocr/legacy.py
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>