mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-01 02:02:20 +00:00
fix(logging): handle ResponseCompletedEvent in anthropic_messages streaming spend log (#29394)
* fix(logging): handle ResponseCompletedEvent in anthropic_messages streaming spend log * fix(logging): extend terminal event handling to ResponseIncompleteEvent and ResponseFailedEvent; fix return type annotation
This commit is contained in:
parent
4b5b1070a2
commit
5a09db9ac1
2 changed files with 53 additions and 1 deletions
|
|
@ -3503,7 +3503,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
else:
|
||||
return None
|
||||
|
||||
def _handle_anthropic_messages_response_logging(self, result: Any) -> ModelResponse:
|
||||
def _handle_anthropic_messages_response_logging(
|
||||
self, result: Any
|
||||
) -> Union[ModelResponse, ResponsesAPIResponse]:
|
||||
"""
|
||||
Handles logging for Anthropic messages responses.
|
||||
|
||||
|
|
@ -3522,6 +3524,15 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
return result
|
||||
elif isinstance(result, ModelResponse):
|
||||
return result
|
||||
elif isinstance(
|
||||
result,
|
||||
(ResponseCompletedEvent, ResponseIncompleteEvent, ResponseFailedEvent),
|
||||
):
|
||||
# anthropic_messages() can route to OpenAI Responses API; in that path
|
||||
# the assembled streaming result is one of these terminal events rather than
|
||||
# a ModelResponse. Return the inner response so downstream handlers
|
||||
# (_transform_usage_objects, normalize_logging_result) can process it.
|
||||
return result.response
|
||||
|
||||
httpx_response = self.model_call_details.get("httpx_response", None)
|
||||
if httpx_response and isinstance(httpx_response, httpx.Response):
|
||||
|
|
|
|||
|
|
@ -3114,3 +3114,44 @@ def test_get_error_information_prefers_message_attribute_over_empty_str():
|
|||
)
|
||||
assert info["error_message"] == "real failure detail"
|
||||
assert info["error_code"] == "401"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"event_cls, event_type",
|
||||
[
|
||||
("ResponseCompletedEvent", "response.completed"),
|
||||
("ResponseIncompleteEvent", "response.incomplete"),
|
||||
("ResponseFailedEvent", "response.failed"),
|
||||
],
|
||||
)
|
||||
def test_handle_anthropic_messages_response_logging_with_terminal_responses_api_events(
|
||||
event_cls, event_type
|
||||
):
|
||||
"""Regression test for #28943: when anthropic_messages routes to OpenAI Responses
|
||||
API and stream=True, success_handler receives a terminal ResponsesAPI event instead
|
||||
of a ModelResponse. The handler must return the inner ResponsesAPIResponse rather
|
||||
than crashing with AnthropicResponse.model_validate."""
|
||||
import importlib
|
||||
|
||||
openai_types = importlib.import_module("litellm.types.llms.openai")
|
||||
EventClass = getattr(openai_types, event_cls)
|
||||
from litellm.types.llms.openai import ResponsesAPIResponse
|
||||
|
||||
logging_obj = LitellmLogging(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "hello"}],
|
||||
stream=True,
|
||||
call_type="anthropic_messages",
|
||||
start_time=time.time(),
|
||||
litellm_call_id="test-rce-123",
|
||||
function_id="test-fn",
|
||||
)
|
||||
|
||||
inner_response = ResponsesAPIResponse(
|
||||
id="resp_test", created_at=1700000000, output=[]
|
||||
)
|
||||
event = EventClass(type=event_type, response=inner_response)
|
||||
|
||||
result = logging_obj._handle_anthropic_messages_response_logging(result=event)
|
||||
|
||||
assert result is inner_response
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue