Skip to content

fix(logging): handle ResponseCompletedEvent in anthropic_messages spe… - #28628

Closed
V-3604 wants to merge 2 commits into
BerriAI:litellm_internal_stagingfrom
V-3604:fix/anthropic-messages-responses-bridge-spend-logging
Closed

fix(logging): handle ResponseCompletedEvent in anthropic_messages spe…#28628
V-3604 wants to merge 2 commits into
BerriAI:litellm_internal_stagingfrom
V-3604:fix/anthropic-messages-responses-bridge-spend-logging

Conversation

@V-3604

@V-3604 V-3604 commented May 22, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Fixes 28595

Pre-Submission checklist

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

1

Type

🐛 Bug Fix

Changes

When /v1/messages routes to a non-Anthropic backend via the Responses API, the streaming success handler delivers a ResponseCompletedEvent to _handle_anthropic_messages_response_logging. That function had no branch for this type and fell through to AnthropicResponse.model_validate(result), which raised a ValidationError because ResponseCompletedEvent is not an anthropic-shaped object. The exception was caught by _success_handler_helper_fn as "Non-Blocking", so client received the correct response but the request was silently dropped from LiteLLM_SpendLogs.

The fix adds two guards before the existing httpx_response branch -
If result is a ResponseCompletedEvent, ResponseIncompleteEvent, or ResponseFailedEvent, unwrap result.response (the ResponsesAPIResponse); if it is already a ResponsesAPIResponse, return it directly. Both types flow through the downstream logging pipeline correctly. _transform_usage_objects already handles ResponsesAPIResponse and converts its usage format for spend logging.

@greptile-apps

greptile-apps Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds guards in _handle_anthropic_messages_response_logging so that ResponseCompletedEvent, ResponseIncompleteEvent, ResponseFailedEvent, and ResponsesAPIResponse objects are recognized and returned instead of falling through to AnthropicResponse.model_validate, which was throwing a ValidationError and silently dropping LiteLLM_SpendLogs entries.

  • The ValidationError crash is fixed for all four event/response types; four unit tests cover each new branch in isolation.
  • For the non-streaming anthropic_messages path the fix is complete — _process_hidden_params_and_response_cost builds the standard logging payload correctly.
  • For the streaming path, _handle_anthropic_messages_response_logging unwraps ResponseCompletedEventResponsesAPIResponse before _get_assembled_streaming_response runs; that function has no ResponsesAPIResponse branch and returns None, so complete_streaming_response is never set and spend-log rows are still dropped.

Confidence Score: 4/5

Safe to merge for the crash fix; the streaming spend-logging gap was already broken before and remains so.

The fix correctly prevents the ValidationError crash for all four event types, and the non-streaming logging path works end-to-end. The streaming path still does not produce a complete_streaming_response because _get_assembled_streaming_response has no branch for an already-unwrapped ResponsesAPIResponse, so spend-log rows for streaming anthropic_messages Responses API calls continue to be silently dropped — the same symptom the PR states it fixes.

litellm/litellm_core_utils/litellm_logging.py — specifically the _get_assembled_streaming_response method which needs a ResponsesAPIResponse passthrough branch to complete the streaming fix.

Important Files Changed

Filename Overview
litellm/litellm_core_utils/litellm_logging.py Adds ResponseCompletedEvent/ResponseIncompleteEvent/ResponseFailedEvent/ResponsesAPIResponse guards to _handle_anthropic_messages_response_logging; fixes the ValidationError crash but does not fix spend logging for streaming because _get_assembled_streaming_response still returns None for ResponsesAPIResponse, leaving complete_streaming_response unset.
tests/test_litellm/litellm_core_utils/test_litellm_logging.py Adds four unit tests covering all new branches in _handle_anthropic_messages_response_logging; all tests are properly mocked with no network calls, but they test the helper in isolation and do not verify that _get_assembled_streaming_response produces a non-None complete_streaming_response for the streaming path.

Reviews (2): Last reviewed commit: "test(logging): add ResponseFailedEvent c..." | Re-trigger Greptile

Comment thread tests/test_litellm/litellm_core_utils/test_litellm_logging.py
@codecov

codecov Bot commented May 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Vigilans

Copy link
Copy Markdown
Contributor

Fixes 28595 in the PR body is missing # — should be Fixes #28595.

This also fixes the AnthropicResponse validation crash (Error 2) reported in #27091 — same root cause, ResponseIncompleteEvent variant.

@greptileai request re-review

Comment on lines +3453 to +3459
if isinstance(
result,
(ResponseCompletedEvent, ResponseIncompleteEvent, ResponseFailedEvent),
):
return result.response
elif isinstance(result, ResponsesAPIResponse):
return result

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.

P1 Streaming spend-log still dropped after this fix

_handle_anthropic_messages_response_logging is called inside _success_handler_helper_fn before _get_assembled_streaming_response. After the fix, a ResponseCompletedEvent is converted to ResponsesAPIResponse here and returned to the caller. _get_assembled_streaming_response then receives a ResponsesAPIResponse, falls through to its else: return None branch (lines 3424-3425), and returns None. Because complete_streaming_response is None, neither response_cost nor standard_logging_object is ever set for the streaming path, so the LiteLLM_SpendLogs row is still silently dropped.

The non-streaming path is fine (self.stream is not True triggers _process_hidden_params_and_response_cost at line 1891 which builds the payload). For the streaming path, _get_assembled_streaming_response needs a ResponsesAPIResponse passthrough branch identical to the one that already exists for ModelResponse (line 3398).

@V-3604

V-3604 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Closing - superseded by #29394 and #28985, which landed this exact fix in _handle_anthropic_messages_response_logging. #28985 covers the same issue (#28595).

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.

2 participants