diff --git a/litellm/router.py b/litellm/router.py index 8175a5c5c40..c47eea31f81 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -431,7 +431,7 @@ def _anthropic_stream_raised_error_status(error: Exception) -> int | None: def _anthropic_stream_fallback_error_for_raised( - error: Exception, model: str, llm_provider: str, has_generated_content: bool + error: Exception, model: str, has_generated_content: bool ) -> "MidStreamFallbackError | None": """ A provider iterator that fails mid-stream by raising (Bedrock surfaces @@ -454,7 +454,7 @@ def _anthropic_stream_fallback_error_for_raised( return MidStreamFallbackError( message=str(error), model=model, - llm_provider=llm_provider, + llm_provider="anthropic", original_exception=error, is_pre_first_chunk=True, ) @@ -5059,8 +5059,6 @@ class Router: has_generated_content = False # rebind-ok: set once real content is seen, or the buffer cap is hit buffered_lifecycle_chunks: tuple[bytes, ...] = () # rebind-ok: flushed once committed or on decline model: Final = cast(str, initial_kwargs.get("model")) # cast-ok: kwargs always carries the model group - custom_llm_provider: Final = initial_kwargs.get("custom_llm_provider") - llm_provider: Final = custom_llm_provider if isinstance(custom_llm_provider, str) else "anthropic" try: async for chunk in source_iterator: if _anthropic_stream_forwards_ping_live( @@ -5109,7 +5107,6 @@ class Router: has_generated_content, buffered_lifecycle_chunks, model, - llm_provider, initial_kwargs, wrapper, ): @@ -5130,7 +5127,6 @@ class Router: has_generated_content: bool, buffered_lifecycle_chunks: tuple[bytes, ...], model: str, - llm_provider: str, initial_kwargs: dict[str, Any], # mutable-ok: handed to _aanthropic_messages_fallback_attempt, which mutates it wrapper: "FallbackAwareAnthropicMessagesStream", ) -> AsyncGenerator[bytes, None]: @@ -5158,7 +5154,7 @@ class Router: fallback_error: Final = ( stream_error if isinstance(stream_error, MidStreamFallbackError) - else _anthropic_stream_fallback_error_for_raised(stream_error, model, llm_provider, has_generated_content) + else _anthropic_stream_fallback_error_for_raised(stream_error, model, has_generated_content) ) if fallback_error is None: raise stream_error diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index bb0ab43ead9..752bbf942d1 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -4,6 +4,7 @@ import json import logging import os import threading +from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock, patch import httpx @@ -10264,14 +10265,28 @@ async def test_anthropic_messages_raised_provider_error_before_content_triggers_ assert source.closed is True +class _AnthropicMessagesStringStatusError(Exception): + def __init__(self): + super().__init__("bad request") + self.status_code = "400" + + +class _AnthropicMessagesResponseOnlyStatusError(Exception): + def __init__(self): + super().__init__("bad request") + self.response = SimpleNamespace(status_code=400) + + @pytest.mark.asyncio @pytest.mark.parametrize( "raised_error", [ BedrockError(status_code=400, message='validationException {"message": "Malformed input"}'), BedrockError(status_code=424, message='modelStreamErrorException {"message": "Model stream error"}'), + _AnthropicMessagesStringStatusError(), + _AnthropicMessagesResponseOnlyStatusError(), ], - ids=["400", "424"], + ids=["400", "424", "str-400", "response-only-400"], ) async def test_anthropic_messages_raised_non_retriable_provider_error_propagates_unchanged(raised_error): """A raised 4xx (other than 429) is a client error no other deployment can @@ -10295,7 +10310,7 @@ async def test_anthropic_messages_raised_non_retriable_provider_error_propagates async for chunk in wrapped: collected.append(chunk) - with pytest.raises(BedrockError) as exc_info: + with pytest.raises(type(raised_error)) as exc_info: await _consume() assert collected == []