From c6c80874886ff9fab46d7575269a5f4bcb30dd41 Mon Sep 17 00:00:00 2001 From: milan-berri Date: Fri, 3 Apr 2026 20:59:05 +0200 Subject: [PATCH] fix(anthropic): pass logging_obj to client.post for litellm_overhead_time_ms (#24071) When LITELLM_DETAILED_TIMING=true, litellm_overhead_time_ms was null for Anthropic because the handler did not pass logging_obj to client.post(), so track_llm_api_timing could not set llm_api_duration_ms. Pass logging_obj=logging_obj at all four post() call sites (make_call, make_sync_call, acompletion, completion). Add test to ensure make_call passes logging_obj to client.post. Made-with: Cursor --- litellm/llms/anthropic/chat/handler.py | 21 ++++++++++-- .../chat/test_anthropic_chat_handler.py | 33 +++++++++++++++++-- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/litellm/llms/anthropic/chat/handler.py b/litellm/llms/anthropic/chat/handler.py index 9f2ddcae2c7..0f020c3a953 100644 --- a/litellm/llms/anthropic/chat/handler.py +++ b/litellm/llms/anthropic/chat/handler.py @@ -89,7 +89,12 @@ async def make_call( try: response = await client.post( - api_base, headers=headers, data=data, stream=True, timeout=timeout + api_base, + headers=headers, + data=data, + stream=True, + timeout=timeout, + logging_obj=logging_obj, ) except httpx.HTTPStatusError as e: error_headers = getattr(e, "headers", None) @@ -142,7 +147,12 @@ def make_sync_call( try: response = client.post( - api_base, headers=headers, data=data, stream=True, timeout=timeout + api_base, + headers=headers, + data=data, + stream=True, + timeout=timeout, + logging_obj=logging_obj, ) except httpx.HTTPStatusError as e: error_headers = getattr(e, "headers", None) @@ -266,7 +276,11 @@ class AnthropicChatCompletion(BaseLLM): try: response = await async_handler.post( - api_base, headers=headers, json=data, timeout=timeout + api_base, + headers=headers, + json=data, + timeout=timeout, + logging_obj=logging_obj, ) except Exception as e: ## LOGGING @@ -469,6 +483,7 @@ class AnthropicChatCompletion(BaseLLM): headers=headers, data=json.dumps(data), timeout=timeout, + logging_obj=logging_obj, ) except Exception as e: status_code = getattr(e, "status_code", 500) diff --git a/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_handler.py b/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_handler.py index 20427e8cc94..bc40919525e 100644 --- a/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_handler.py +++ b/tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_handler.py @@ -1,7 +1,9 @@ -from unittest.mock import MagicMock +from unittest.mock import AsyncMock, MagicMock + +import pytest from litellm.constants import RESPONSE_FORMAT_TOOL_NAME -from litellm.llms.anthropic.chat.handler import ModelResponseIterator +from litellm.llms.anthropic.chat.handler import ModelResponseIterator, make_call from litellm.types.llms.openai import ( ChatCompletionToolCallChunk, ChatCompletionToolCallFunctionChunk, @@ -9,6 +11,33 @@ from litellm.types.llms.openai import ( from litellm.types.responses.main import OutputCodeInterpreterCall +@pytest.mark.asyncio +async def test_make_call_passes_logging_obj_to_client_post(): + """make_call must pass logging_obj to client.post so track_llm_api_timing can set llm_api_duration_ms for litellm_overhead_time_ms.""" + mock_client = AsyncMock() + mock_response = MagicMock() + mock_response.aiter_lines = MagicMock(return_value=iter([b'data: {"type":"message_start"}\n', b'data: {"type":"message_delta"}\n'])) + mock_client.post.return_value = mock_response + + logging_obj = MagicMock() + + await make_call( + client=mock_client, + api_base="https://api.anthropic.com/v1/messages", + headers={}, + data="{}", + model="claude-3-5-haiku", + messages=[{"role": "user", "content": "Hi"}], + logging_obj=logging_obj, + timeout=60.0, + json_mode=False, + ) + + mock_client.post.assert_called_once() + call_kwargs = mock_client.post.call_args[1] + assert call_kwargs.get("logging_obj") is logging_obj + + def test_redacted_thinking_content_block_delta(): chunk = { "type": "content_block_start",