fix(passthrough): run async success handler for SDK pass-through streaming - #29312
Conversation
…aming dispatch_success_handlers classified anthropic_messages and google generate_content streaming as sync SDK requests, since their call_type is not pass_through_endpoint and their litellm_params carries no acompletion flag. Only the sync success_handler ran, so async-only loggers (CustomLogger.async_log_success_event) never recorded the assembled stream; this broke test_anthropic_messages_litellm_router_streaming_with_logging and test_async_streaming_with_logging. _route_streaming_logging_to_handler now passes prefer_async_handlers=True, which is always valid because it runs from an async context (anthropic_messages, google_genai, and proxy pass-through stream tasks). This restores async dispatch while keeping the final-stream dedupe guard intact. Adds a regression test asserting the async handler runs and the sync handler does not for an anthropic_messages streaming call.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
da57dac
into
litellm_litellm__duplicate_cc_trce
Greptile SummaryThis PR fixes a regression where async-only loggers (e.g.,
Confidence Score: 5/5Safe to merge — the change is a one-liner addition of a flag to an already-existing The fix is minimal and well-scoped: No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/pass_through_endpoints/streaming_handler.py | Passes prefer_async_handlers=True to dispatch_success_handlers so async loggers run for SDK pass-through streaming (anthropic_messages, google_genai) whose call_type is not pass_through_endpoint |
| tests/pass_through_unit_tests/test_unit_test_streaming.py | Adds regression test verifying that _route_streaming_logging_to_handler awaits the async success handler (and skips the sync handler) for an anthropic_messages streaming call; uses only mocks, no real network calls |
Reviews (1): Last reviewed commit: "fix(passthrough): run async success hand..." | Re-trigger Greptile
Relevant issues
Fixes two CircleCI failures on #29311 (internal copy of #29089). This PR targets that PR's branch so the fix lands directly on it.
What was failing and why
Two streaming-plus-logging tests were red on #29311:
pass_through_unit_testing:tests/pass_through_unit_tests/test_anthropic_messages_passthrough.py::TestAnthropicBedrockAPI::test_anthropic_messages_litellm_router_streaming_with_loggingand theTestAnthropicDirectAPIvariant, both with "Logging payload should not be None".google_generate_content_endpoint_testing:tests/unified_google_tests/test_google_ai_studio.py::TestGoogleGenAIStudio::test_async_streaming_with_logging, withstandard_logging_object is not Nonefailing.Both SDK streaming paths (
aanthropic_messages, googleagenerate_content) export their assembled stream throughPassThroughStreamingHandler._route_streaming_logging_to_handler, which #29089 changed to calldispatch_success_handlers. That method treated these calls as synchronous SDK requests because theircall_typeis notpass_through_endpointand theirlitellm_paramscarries noacompletionflag. As a result only the syncsuccess_handlerran, so async-only loggers (CustomLogger.async_log_success_event) never fired and the standard logging payload stayedNone.The other two CircleCI reds on #29311 are not caused by this branch:
local_testing_part1fails ontest_vertex_ai_llama_tool_callingwith a VertexACCESS_TOKEN_EXPIREDerror, andbatches_testingfails ontest_async_file_and_batchwith a Bedrock "account is not authorized" error. Both are environment/credential issues.The fix
_route_streaming_logging_to_handlernow passesprefer_async_handlers=True. That is always valid because the function only runs from an async context (anthropic_messages, google_genai, and proxy pass-through stream tasks). It restores the pre-#29089 behavior of awaitingasync_success_handlerand conditionally submitting the sync handler for legacy string callbacks, while keeping the newhas_dispatched_final_stream_successdedupe guard that #29089 added to stop the duplicate Claude Code traces.Pre-Submission checklist
Screenshots / Proof of Fix
The new regression test
test_route_streaming_logging_runs_async_handler_for_sdk_passthroughconstructs a realLoggingobject configured like ananthropic_messagesstreaming call, drives the production_route_streaming_logging_to_handler, and asserts the async handler is awaited once and the sync handler is not called. It fails before the change (async awaited 0 times) and passes after.To confirm against the originally failing CircleCI cases with real provider keys set:
Type
🐛 Bug Fix
Changes
litellm/proxy/pass_through_endpoints/streaming_handler.py: passprefer_async_handlers=Truewhen routing assembled-stream logging, so async loggers run for SDK pass-through streaming whosecall_typeis notpass_through_endpoint.tests/pass_through_unit_tests/test_unit_test_streaming.py: add a regression test that drives_route_streaming_logging_to_handlerfor ananthropic_messagesstreaming call and asserts the async success handler runs.Generated by Claude Code