fix(logging): pass through Response*Event when /v1/messages bridges OpenAI Responses API - #29413
Conversation
…penAI Responses API When /v1/messages is routed to a backend that returns OpenAI Responses API stream events instead of native Anthropic shape, the success handler crashes inside AnthropicResponse.model_validate(result), which is then caught as "[Non-Blocking]" and silently drops the spend_logs row. The client-facing stream is already translated to anthropic-shape SSE by AnthropicResponsesStreamWrapper, so the logging path can safely return the raw event untouched. Add an early return in _handle_anthropic_messages_response_logging for streaming requests when result is a ResponseCompletedEvent / ResponseIncompleteEvent / ResponseFailedEvent. Fixes BerriAI#28595 Fixes BerriAI#28943
Greptile SummaryThis PR fixes a silent logging failure in
Confidence Score: 5/5Safe to merge — the change is a minimal guard that prevents a pre-existing crash in a single method, and the non-streaming path is unchanged. The fix is a two-line guard that mirrors the adjacent pattern already present in the same function. The imports for the three event types have existed since earlier commits. The streaming cost-tracking path already handles these event types correctly; this change simply prevents the crash that was aborting the logging chain before that path could be reached. Tests cover all three event types and verify the non-streaming guard is not over-applied. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/litellm_logging.py | Adds an early return in _handle_anthropic_messages_response_logging for streaming Response*Event objects, preventing a crash in AnthropicResponse.model_validate when the /v1/messages route bridges through the OpenAI Responses API. Updates the return type annotation from ModelResponse to Any to match all existing return branches. |
| tests/test_litellm/litellm_core_utils/test_litellm_logging.py | Adds a parametrized regression test covering all three Response*Event types in the streaming path, and a guard test that confirms non-streaming requests still route through AnthropicResponse.model_validate. All tests use mocks and make no real network calls. |
Reviews (1): Last reviewed commit: "fix(logging): pass through Response*Even..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
@silencedoctor — could you add a screenshot or short video showing that this change works as expected? It really helps reviewers verify the fix quickly. Thanks! |
|
@Sameerlite Before fix: parent commit After fix: PR head So the screenshots show the actual issue clearly: this PR does not change the
|
|
Compare with my PR #28985 |
|
fix #28985 |


Relevant issues
Fixes #28595
Fixes #28943
Changes
When
/v1/messagesis routed to a backend that returns OpenAI Responses API stream events instead of native Anthropic shape (e.g. an OpenAI-Responses-compatible Ollama backend), the success handler crashes insideAnthropicResponse.model_validate(result). The exception is caught by_success_handler_helper_fnas[Non-Blocking], so the client gets a normal streaming response — but the spend_logs row is silently dropped and the UI shows no entry.AnthropicResponsesStreamWrapper.async_anthropic_sse_wrapperalready translates the events back to anthropic-shape SSE for the client. The logging path does not need to re-translate; passing the raw event through preserves usage fields for downstream cost tracking.This PR adds an early return in
_handle_anthropic_messages_response_loggingfor streaming requests whenresultis aResponseCompletedEvent/ResponseIncompleteEvent/ResponseFailedEvent, and updates the return type annotation fromModelResponsetoAnyto match the existing branches that return non-ModelResponse objects.Non-streaming requests are unaffected — a
Response*Eventarriving on a non-streaming path is still unexpected and continues into the existingAnthropicResponse.model_validatebranch.Testing
ResponseCompletedEvent,ResponseIncompleteEvent,ResponseFailedEvent): withstream=Trueandcall_type=\"anthropic_messages\", the handler returns the event untouched and never callsAnthropicResponse.model_validateAnthropicResponse.model_validateforResponse*Eventinputs (preventing accidental over-swallowing)litellm_logging.py, re-run, all three fail — proving the tests aren't no-ops)Type
🐛 Bug Fix