mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-17 04:34:25 +00:00
Merge 2776dec9cf into 001457af8b
This commit is contained in:
commit
2eae216c37
2 changed files with 59 additions and 1 deletions
|
|
@ -2310,7 +2310,17 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
isinstance(callback, CustomLogger)
|
||||
and is_sync_request
|
||||
and self.call_type
|
||||
!= CallTypes.pass_through.value # pass-through endpoints call async_log_success_event
|
||||
not in (
|
||||
# both endpoints are always dispatched through the
|
||||
# async wrapper, which already calls
|
||||
# async_log_success_event. anthropic_messages
|
||||
# (/v1/messages) sets no acompletion-style flag, so
|
||||
# is_sync_request is True here and the sync pass would
|
||||
# otherwise re-dispatch every CustomLogger, doubling
|
||||
# OTEL spans + cost/success callbacks.
|
||||
CallTypes.pass_through.value,
|
||||
CallTypes.anthropic_messages.value,
|
||||
)
|
||||
): # custom logger class
|
||||
if self.stream and complete_streaming_response is None:
|
||||
callback.log_stream_event(
|
||||
|
|
|
|||
|
|
@ -3707,3 +3707,51 @@ def test_set_cost_breakdown_stores_reasoning_cost():
|
|||
cost_for_built_in_tools_cost_usd_dollar=0.0,
|
||||
)
|
||||
assert "reasoning_cost" not in no_reasoning.cost_breakdown
|
||||
|
||||
|
||||
def test_success_handler_skips_sync_callbacks_for_anthropic_messages(logging_obj):
|
||||
"""anthropic_messages (/v1/messages) is always dispatched through the async
|
||||
wrapper, so the sync success_handler pass must not re-dispatch CustomLogger
|
||||
callbacks -- the async handler is the canonical path.
|
||||
|
||||
Regression for the duplicate ``litellm_request`` OTEL spans + double
|
||||
success/cost callbacks on non-streaming /v1/messages. Unlike acompletion,
|
||||
anthropic_messages sets no async flag in litellm_params, so
|
||||
_is_sync_litellm_request is True and the sync pass would otherwise fire.
|
||||
"""
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
|
||||
class DummyLogger(CustomLogger):
|
||||
pass
|
||||
|
||||
logging_obj.stream = False
|
||||
logging_obj.call_type = "anthropic_messages"
|
||||
logging_obj.model_call_details["litellm_params"] = {}
|
||||
logging_obj.litellm_params = {}
|
||||
|
||||
dummy_logger = DummyLogger()
|
||||
dummy_logger.log_success_event = MagicMock()
|
||||
dummy_logger.log_stream_event = MagicMock()
|
||||
|
||||
model_response = ModelResponse(
|
||||
id="resp-123",
|
||||
model="gpt-4o-mini",
|
||||
choices=[
|
||||
{
|
||||
"message": {"role": "assistant", "content": "hello"},
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
}
|
||||
],
|
||||
usage={"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
|
||||
)
|
||||
|
||||
with patch.object(
|
||||
logging_obj,
|
||||
"get_combined_callback_list",
|
||||
return_value=[dummy_logger],
|
||||
):
|
||||
logging_obj.success_handler(result=model_response)
|
||||
|
||||
dummy_logger.log_success_event.assert_not_called()
|
||||
dummy_logger.log_stream_event.assert_not_called()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue