From 56f84c88569f26ec042b86f47729451584bc4d59 Mon Sep 17 00:00:00 2001 From: ammmanism Date: Sat, 11 Jul 2026 16:44:23 +0530 Subject: [PATCH 1/2] fix(anthropic): support max_retries for non-OpenAI providers Previously max_retries was silently dropped for every provider except OpenAI/Azure (see the TODO in litellm/utils.py). This wires max_retries through to LiteLLM's httpx transport so transient connection errors are retried. - Declare max_retries as a supported param for all providers in get_optional_params. - AnthropicConfig maps max_retries and drops it from the request body. - AsyncHTTPHandler/HTTPHandler build an httpx retry transport when max_retries is set (aiohttp default path unchanged). - Anthropic handler creates a retry-configured client per request. - Adds regression tests in tests/litellm/llms/anthropic/test_anthropic_max_retries.py. --- litellm/llms/anthropic/chat/handler.py | 35 +++++++- litellm/llms/anthropic/chat/transformation.py | 8 ++ litellm/llms/custom_httpx/http_handler.py | 46 +++++++++- litellm/utils.py | 10 ++- .../anthropic/test_anthropic_max_retries.py | 87 +++++++++++++++++++ 5 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 tests/litellm/llms/anthropic/test_anthropic_max_retries.py diff --git a/litellm/llms/anthropic/chat/handler.py b/litellm/llms/anthropic/chat/handler.py index c8872306e82f..746fc3c9e26f 100644 --- a/litellm/llms/anthropic/chat/handler.py +++ b/litellm/llms/anthropic/chat/handler.py @@ -11,6 +11,7 @@ Dict, List, Literal, + Optional, Tuple, Union, cast, @@ -229,8 +230,16 @@ async def acompletion_stream_function( data["stream"] = True + _stream_client = client + _stream_max_retries = optional_params.get("max_retries") if isinstance(optional_params, dict) else None + if _stream_client is None and isinstance(_stream_max_retries, int): + _stream_client = get_async_httpx_client( + llm_provider=litellm.LlmProviders.ANTHROPIC, + params={"max_retries": _stream_max_retries}, + ) + completion_stream, headers = await make_call( - client=client, + client=_stream_client, api_base=api_base, headers=headers, data=json.dumps(data), @@ -276,7 +285,13 @@ async def acompletion_function( headers={}, client: AsyncHTTPHandler | None = None, ) -> Union[ModelResponse, "CustomStreamWrapper"]: - async_handler = client or get_async_httpx_client(llm_provider=litellm.LlmProviders.ANTHROPIC) + _async_client_params: Optional[dict] = None + _async_max_retries = optional_params.get("max_retries") if isinstance(optional_params, dict) else None + if isinstance(_async_max_retries, int): + _async_client_params = {"max_retries": _async_max_retries} + async_handler = client or get_async_httpx_client( + llm_provider=litellm.LlmProviders.ANTHROPIC, params=_async_client_params + ) try: response = await async_handler.post( @@ -450,8 +465,16 @@ def completion( stream is True ): # if function call - fake the streaming (need complete blocks for output parsing in openai format) data["stream"] = stream + _sync_stream_client = client + _sync_stream_max_retries = ( + optional_params.get("max_retries") if isinstance(optional_params, dict) else None + ) + if _sync_stream_client is None and isinstance(_sync_stream_max_retries, int): + _sync_stream_client = _get_httpx_client( + params={"timeout": timeout, "max_retries": _sync_stream_max_retries} + ) completion_stream, headers = make_sync_call( - client=client, + client=_sync_stream_client, api_base=api_base, headers=headers, # type: ignore data=json.dumps(data), @@ -477,7 +500,11 @@ def completion( else: if client is None or not isinstance(client, HTTPHandler): - client = _get_httpx_client(params={"timeout": timeout}) + _sync_client_params: dict = {"timeout": timeout} + _sync_max_retries = optional_params.get("max_retries") + if isinstance(_sync_max_retries, int): + _sync_client_params["max_retries"] = _sync_max_retries + client = _get_httpx_client(params=_sync_client_params) else: client = client diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 9721b797584c..9e44f27c1fcf 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -447,6 +447,7 @@ def get_supported_openai_params(self, model: str): "speed", "context_management", "cache_control", + "max_retries", ] if ( @@ -1509,6 +1510,11 @@ def map_openai_params( elif param == "cache_control" and isinstance(value, dict): # Pass through top-level cache_control for automatic prompt caching optional_params["cache_control"] = value + elif param == "max_retries" and isinstance(value, int): + # Consumed by LiteLLM's httpx transport layer (see + # AsyncHTTPHandler/HTTPHandler). Kept off the request body below + # in transform_request so it is never sent to the Anthropic API. + optional_params["max_retries"] = value ## handle thinking tokens self.update_optional_params_with_thinking_tokens( @@ -1870,6 +1876,8 @@ def transform_request( # Remove internal LiteLLM parameters that should not be sent to Anthropic API optional_params.pop("is_vertex_request", None) optional_params.pop("client_metadata", None) + # max_retries is consumed by LiteLLM's httpx transport, not the Anthropic API. + optional_params.pop("max_retries", None) # ``top_k`` is a provider-specific kwarg that bypasses # ``map_openai_params``; gate it here, the single boundary shared by diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index 5cec763bb5d6..616bba3ec525 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -515,14 +515,17 @@ def __init__( client_alias: Optional[str] = None, # name for client in logs ssl_verify: Optional[VerifyTypes] = None, shared_session: Optional["ClientSession"] = None, + max_retries: Optional[int] = None, ): self.timeout = timeout self.event_hooks = event_hooks + self.max_retries = max_retries self.client = self.create_client( timeout=timeout, event_hooks=event_hooks, ssl_verify=ssl_verify, shared_session=shared_session, + max_retries=max_retries, ) self.client_alias = client_alias @@ -532,6 +535,7 @@ def create_client( event_hooks: Optional[Mapping[str, List[Callable[..., Any]]]], ssl_verify: Optional[VerifyTypes] = None, shared_session: Optional["ClientSession"] = None, + max_retries: Optional[int] = None, ) -> httpx.AsyncClient: # Get unified SSL configuration ssl_config = get_ssl_configuration(ssl_verify) @@ -548,6 +552,7 @@ def create_client( ssl_context=ssl_config if isinstance(ssl_config, ssl.SSLContext) else None, ssl_verify=ssl_config if isinstance(ssl_config, bool) else None, shared_session=shared_session, + max_retries=max_retries, ) # Get default headers (User-Agent, overridable via LITELLM_USER_AGENT) @@ -890,6 +895,7 @@ def _create_async_transport( ssl_context: Optional[ssl.SSLContext] = None, ssl_verify: Optional[bool] = None, shared_session: Optional["ClientSession"] = None, + max_retries: Optional[int] = None, ) -> Optional[Union[LiteLLMAiohttpTransport, AsyncHTTPTransport]]: """ - Creates a transport for httpx.AsyncClient @@ -897,6 +903,13 @@ def _create_async_transport( - [Default] It will return AiohttpTransport - Users can opt out of using AiohttpTransport by setting litellm.use_aiohttp_transport to False + - When ``max_retries`` is requested we use the httpx transport with a + built-in ``httpx.Retry`` policy (aiohttp has no native retry support in + LiteLLM's transport). This is an opt-in that only affects clients created + with ``max_retries`` set, so the default high-throughput aiohttp path is + unchanged. + + Notes on this handler: - Why AiohttpTransport? @@ -905,6 +918,12 @@ def _create_async_transport( - Why force ipv4? - Some users have seen httpx ConnectionError when using ipv6 - forcing ipv4 resolves the issue for them """ + ######################################################### + # Retry transport (opt-in via max_retries) + ######################################################### + if max_retries is not None and max_retries > 0: + return AsyncHTTPHandler._create_httpx_transport(max_retries=max_retries) + ######################################################### # AIOHTTP TRANSPORT is off by default ######################################################### @@ -1050,13 +1069,23 @@ def _create_aiohttp_transport( ) @staticmethod - def _create_httpx_transport() -> Optional[AsyncHTTPTransport]: + def _create_httpx_transport( + max_retries: Optional[int] = None, + ) -> Optional[AsyncHTTPTransport]: """ Creates an AsyncHTTPTransport - If force_ipv4 is True, it will create an AsyncHTTPTransport with local_address set to "0.0.0.0" - - [Default] If force_ipv4 is False, it will return None + - If ``max_retries`` is provided, the transport retries transient + connection errors using httpx's built-in ``retries`` policy before + surfacing the failure to the caller. + - [Default] If force_ipv4 is False and max_retries is None, it will return None """ + if max_retries is not None and max_retries > 0: + if litellm.force_ipv4: + return AsyncHTTPTransport(retries=max_retries, local_address="0.0.0.0") + return AsyncHTTPTransport(retries=max_retries) + if litellm.force_ipv4: return AsyncHTTPTransport(local_address="0.0.0.0") else: @@ -1073,6 +1102,7 @@ def __init__( disable_default_headers: Optional[ bool ] = False, # arize phoenix returns different API responses when user agent header in request + max_retries: Optional[int] = None, ): if timeout is None: timeout = _DEFAULT_TIMEOUT @@ -1088,7 +1118,7 @@ def __init__( default_headers = get_default_headers() if not disable_default_headers else None if client is None: - transport = self._create_sync_transport() + transport = self._create_sync_transport(max_retries=max_retries) # Create a client with a connection pool self.client = httpx.Client( @@ -1351,13 +1381,21 @@ def __del__(self) -> None: except Exception: pass - def _create_sync_transport(self) -> Optional[HTTPTransport]: + def _create_sync_transport(self, max_retries: Optional[int] = None) -> Optional[HTTPTransport]: """ Create an HTTP transport with IPv4 only if litellm.force_ipv4 is True. Otherwise, return None. Some users have seen httpx ConnectionError when using ipv6 - forcing ipv4 resolves the issue for them + + When ``max_retries`` is provided, the transport retries transient + connection errors via httpx's built-in ``retries`` policy. """ + if max_retries is not None and max_retries > 0: + if litellm.force_ipv4: + return HTTPTransport(retries=max_retries, local_address="0.0.0.0") + return HTTPTransport(retries=max_retries) + if litellm.force_ipv4: return HTTPTransport(local_address="0.0.0.0") else: diff --git a/litellm/utils.py b/litellm/utils.py index 19c2fe160854..f27e56eea4d2 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -3817,10 +3817,6 @@ def _check_valid_arg(supported_params: List[str]): continue if k == "n" and n == 1: # langchain sends n=1 as a default value continue # skip this param - if ( - k == "max_retries" - ): # TODO: This is a patch. We support max retries for OpenAI, Azure. For non OpenAI LLMs we need to add support for max retries - continue # skip this param # Always keeps this in elif code blocks else: unsupported_params[k] = non_default_params[k] @@ -3845,6 +3841,12 @@ def _check_valid_arg(supported_params: List[str]): supported_params = supported_params or [] allowed_openai_params = allowed_openai_params or [] supported_params.extend(allowed_openai_params) + # ``max_retries`` is supported across all providers (OpenAI/Azure retry at + # the SDK layer; non-OpenAI providers retry at LiteLLM's httpx transport + # layer when ``max_retries`` is set). Declare it supported everywhere so it + # is no longer silently dropped for non-OpenAI providers. + if "max_retries" not in supported_params: + supported_params.append("max_retries") _check_valid_arg( supported_params=supported_params or [], diff --git a/tests/litellm/llms/anthropic/test_anthropic_max_retries.py b/tests/litellm/llms/anthropic/test_anthropic_max_retries.py new file mode 100644 index 000000000000..1f12d6684663 --- /dev/null +++ b/tests/litellm/llms/anthropic/test_anthropic_max_retries.py @@ -0,0 +1,87 @@ +""" +Regression tests for `max_retries` support on non-OpenAI providers. + +`max_retries` was previously silently dropped for every provider except +OpenAI/Azure (see the TODO in `litellm/utils.py`). These tests lock in the +fix that: + +1. Declares `max_retries` as a supported param for all providers. +2. Maps it through `AnthropicConfig` without leaking it into the request body. +3. Wires it to LiteLLM's httpx transport so transient connection errors are + retried. +""" + +import httpx +import pytest + +from litellm.llms.anthropic.chat.transformation import AnthropicConfig +from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler + + +class TestAnthropicMaxRetriesSupported: + def test_max_retries_in_supported_params(self): + supported = AnthropicConfig().get_supported_openai_params(model="claude-3-5-sonnet-20241022") + assert "max_retries" in supported + + def test_map_openai_params_keeps_max_retries(self): + optional_params = AnthropicConfig().map_openai_params( + non_default_params={"max_retries": 3, "temperature": 0.5}, + optional_params={}, + model="claude-3-5-sonnet-20241022", + drop_params=False, + ) + assert optional_params.get("max_retries") == 3 + + def test_map_openai_params_ignores_non_int_max_retries(self): + optional_params = AnthropicConfig().map_openai_params( + non_default_params={"max_retries": "fast"}, + optional_params={}, + model="claude-3-5-sonnet-20241022", + drop_params=False, + ) + assert "max_retries" not in optional_params + + def test_transform_request_drops_max_retries_from_body(self): + data = AnthropicConfig().transform_request( + model="claude-3-5-sonnet-20241022", + messages=[{"role": "user", "content": "hi"}], + optional_params={"max_retries": 3, "temperature": 0.5}, + litellm_params={}, + headers={}, + ) + assert "max_retries" not in data + # sanity: real params are still present + assert data["temperature"] == 0.5 + + +class TestMaxRetriesHttpTransport: + def test_async_handler_builds_retry_transport(self): + handler = AsyncHTTPHandler(max_retries=3) + transport = handler.client._transport + assert isinstance(transport, httpx.AsyncHTTPTransport) + assert getattr(transport._pool, "_retries", 0) == 3 + + def test_sync_handler_builds_retry_transport(self): + handler = HTTPHandler(max_retries=2, timeout=5) + transport = handler.client._transport + assert isinstance(transport, httpx.HTTPTransport) + assert getattr(transport._pool, "_retries", 0) == 2 + + def test_default_handler_does_not_change_transport(self): + # Without max_retries the default (aiohttp) transport path is untouched. + handler = AsyncHTTPHandler() + assert not isinstance(handler.client._transport, httpx.AsyncHTTPTransport) + + +class TestMaxRetriesValidation: + @pytest.mark.parametrize("provider,model", [("anthropic", "claude-3-5-sonnet-20241022")]) + def test_completion_accepts_max_retries_without_error(self, provider, model): + """ + Reproduces the original bug: passing max_retries to a non-OpenAI provider + used to raise UnsupportedParamsError (or silently drop the param). It must + now be accepted as a valid param. + """ + from litellm.utils import get_supported_openai_params + + supported = get_supported_openai_params(model=model, custom_llm_provider=provider) + assert "max_retries" in supported From 342a634666a646a348002a3bc37cbddf99480450 Mon Sep 17 00:00:00 2001 From: ammmanism Date: Sun, 12 Jul 2026 12:46:51 +0530 Subject: [PATCH 2/2] fix(anthropic): scope max_retries to providers with retry wiring --- .../get_supported_openai_params.py | 10 ++ litellm/utils.py | 7 +- .../anthropic/test_anthropic_max_retries.py | 151 +++++++++++++++++- 3 files changed, 156 insertions(+), 12 deletions(-) diff --git a/litellm/litellm_core_utils/get_supported_openai_params.py b/litellm/litellm_core_utils/get_supported_openai_params.py index 19149da0316f..f9d27cd1f0b5 100644 --- a/litellm/litellm_core_utils/get_supported_openai_params.py +++ b/litellm/litellm_core_utils/get_supported_openai_params.py @@ -53,6 +53,16 @@ def get_supported_openai_params( if provider_config and request_type == "chat_completion": supported_params = provider_config.get_supported_openai_params(model=model) + allowed_openai_params = getattr(provider_config, "allowed_openai_params", None) + supported_params = supported_params or [] + allowed_openai_params = allowed_openai_params or [] + supported_params.extend(allowed_openai_params) + # ``max_retries`` is supported for providers with actual retry wiring + # (OpenAI/Azure retry at the SDK layer; anthropic retries at LiteLLM's + # httpx transport layer when ``max_retries`` is set). + providers_with_retry_support = {"openai", "azure", "anthropic"} + if custom_llm_provider in providers_with_retry_support and "max_retries" not in supported_params: + supported_params.append("max_retries") if base_model and base_model != model: base_model_params = provider_config.get_supported_openai_params(model=base_model) supported_params = list(dict.fromkeys([*supported_params, *base_model_params])) diff --git a/litellm/utils.py b/litellm/utils.py index f27e56eea4d2..9963345a5682 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -3841,11 +3841,8 @@ def _check_valid_arg(supported_params: List[str]): supported_params = supported_params or [] allowed_openai_params = allowed_openai_params or [] supported_params.extend(allowed_openai_params) - # ``max_retries`` is supported across all providers (OpenAI/Azure retry at - # the SDK layer; non-OpenAI providers retry at LiteLLM's httpx transport - # layer when ``max_retries`` is set). Declare it supported everywhere so it - # is no longer silently dropped for non-OpenAI providers. - if "max_retries" not in supported_params: + providers_with_retry_support = {"openai", "azure", "anthropic"} + if custom_llm_provider in providers_with_retry_support and "max_retries" not in supported_params: supported_params.append("max_retries") _check_valid_arg( diff --git a/tests/litellm/llms/anthropic/test_anthropic_max_retries.py b/tests/litellm/llms/anthropic/test_anthropic_max_retries.py index 1f12d6684663..d09158286ddb 100644 --- a/tests/litellm/llms/anthropic/test_anthropic_max_retries.py +++ b/tests/litellm/llms/anthropic/test_anthropic_max_retries.py @@ -2,10 +2,9 @@ Regression tests for `max_retries` support on non-OpenAI providers. `max_retries` was previously silently dropped for every provider except -OpenAI/Azure (see the TODO in `litellm/utils.py`). These tests lock in the -fix that: +OpenAI/Azure. These tests lock in the fix that: -1. Declares `max_retries` as a supported param for all providers. +1. Declares `max_retries` as a supported param for providers with retry wiring (openai, azure, anthropic). 2. Maps it through `AnthropicConfig` without leaking it into the request body. 3. Wires it to LiteLLM's httpx transport so transient connection errors are retried. @@ -15,7 +14,12 @@ import pytest from litellm.llms.anthropic.chat.transformation import AnthropicConfig -from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler +from litellm.llms.custom_httpx.http_handler import ( + AsyncHTTPHandler, + HTTPHandler, + get_async_httpx_client, + _get_httpx_client, +) class TestAnthropicMaxRetriesSupported: @@ -67,10 +71,92 @@ def test_sync_handler_builds_retry_transport(self): assert isinstance(transport, httpx.HTTPTransport) assert getattr(transport._pool, "_retries", 0) == 2 - def test_default_handler_does_not_change_transport(self): - # Without max_retries the default (aiohttp) transport path is untouched. + def test_async_handler_retry_transport_with_force_ipv4(self): + import litellm + + original_force_ipv4 = litellm.force_ipv4 + litellm.force_ipv4 = True + litellm.disable_aiohttp_transport = True + try: + transport = AsyncHTTPHandler._create_async_transport(max_retries=3) + assert isinstance(transport, httpx.AsyncHTTPTransport) + assert getattr(transport._pool, "_retries", 0) == 3 + assert transport._pool._local_address == "0.0.0.0" + finally: + litellm.force_ipv4 = original_force_ipv4 + litellm.disable_aiohttp_transport = False + + def test_sync_handler_retry_transport_with_force_ipv4(self): + import litellm + + original_force_ipv4 = litellm.force_ipv4 + litellm.force_ipv4 = True + litellm.disable_aiohttp_transport = True + try: + handler = HTTPHandler(max_retries=3, timeout=5) + transport = handler.client._transport + assert isinstance(transport, httpx.HTTPTransport) + assert getattr(transport._pool, "_retries", 0) == 3 + assert transport._pool._local_address == "0.0.0.0" + finally: + litellm.force_ipv4 = original_force_ipv4 + litellm.disable_aiohttp_transport = False + handler.close() + + def test_async_handler_force_ipv4_without_max_retries(self): + import litellm + + original_force_ipv4 = litellm.force_ipv4 + litellm.force_ipv4 = True + litellm.disable_aiohttp_transport = True + try: + transport = AsyncHTTPHandler._create_async_transport() + assert isinstance(transport, httpx.AsyncHTTPTransport) + assert transport._pool._local_address == "0.0.0.0" + finally: + litellm.force_ipv4 = original_force_ipv4 + litellm.disable_aiohttp_transport = False + + def test_default_handler_does_not_use_retry_transport(self): + # Without max_retries we do not use the retry transport. handler = AsyncHTTPHandler() - assert not isinstance(handler.client._transport, httpx.AsyncHTTPTransport) + transport = handler.client._transport + + # Check if aiohttp transport is available and enabled + aiohttp_enabled = AsyncHTTPHandler._should_use_aiohttp_transport() + + # Import the aiohttp transport type for checking + try: + from litellm.llms.custom_httpx.aiohttp_transport import LiteLLMAiohttpTransport + + HAS_AIOHTTP_TRANSPORT = True + except ImportError: + HAS_AIOHTTP_TRANSPORT = False + LiteLLMAiohttpTransport = None + + if aiohttp_enabled and HAS_AIOHTTP_TRANSPORT: + # When aiohttp is available and enabled, we should get the aiohttp transport + # which has no built-in retry mechanism in LiteLLM + assert isinstance(transport, LiteLLMAiohttpTransport) + else: + # When aiohttp is not available/disabled, we should get an httpx transport + # with explicit retries=0 (no retry) + assert isinstance(transport, httpx.AsyncHTTPTransport) + assert getattr(transport._pool, "_retries", 0) == 0 + + def test_async_handler_transport_none_when_aiohttp_disabled_and_ipv4_off(self): + import litellm + + original_force_ipv4 = litellm.force_ipv4 + original_disable = litellm.disable_aiohttp_transport + litellm.force_ipv4 = False + litellm.disable_aiohttp_transport = True + try: + transport = AsyncHTTPHandler._create_async_transport() + assert transport is None + finally: + litellm.force_ipv4 = original_force_ipv4 + litellm.disable_aiohttp_transport = original_disable class TestMaxRetriesValidation: @@ -85,3 +171,54 @@ def test_completion_accepts_max_retries_without_error(self, provider, model): supported = get_supported_openai_params(model=model, custom_llm_provider=provider) assert "max_retries" in supported + + def test_non_wired_provider_still_raises_for_max_retries(self): + """ + Regression test: providers without retry wiring should still raise + UnsupportedParamsError when max_retries is passed via completion(). + """ + from litellm.utils import get_supported_openai_params + + # Cohere doesn't have actual retry wiring yet + # get_supported_openai_params should NOT include max_retries for cohere + supported_params = get_supported_openai_params(model="command", custom_llm_provider="cohere") + assert "max_retries" not in supported_params, "max_retries should not be in supported params for cohere" + + +class TestMaxRetriesHandlerPaths: + def test_get_async_httpx_client_with_max_retries(self): + """Test that get_async_httpx_client creates client with max_retries when provided.""" + from litellm.types.utils import LlmProviders + import asyncio + + client = get_async_httpx_client(llm_provider=LlmProviders.ANTHROPIC, params={"max_retries": 5}) + try: + transport = client.client._transport + assert isinstance(transport, httpx.AsyncHTTPTransport) + assert getattr(transport._pool, "_retries", 0) == 5 + finally: + asyncio.run(client.close()) + + def test_get_httpx_client_with_max_retries(self): + """Test that _get_httpx_client creates client with max_retries when provided.""" + handler = _get_httpx_client(params={"max_retries": 4, "timeout": 30}) + try: + transport = handler.client._transport + assert isinstance(transport, httpx.HTTPTransport) + assert getattr(transport._pool, "_retries", 0) == 4 + finally: + handler.close() + + def test_get_async_httpx_client_without_max_retries(self): + """Test that get_async_httpx_client creates client without max_retries when not provided.""" + from litellm.types.utils import LlmProviders + from litellm.llms.custom_httpx.aiohttp_transport import LiteLLMAiohttpTransport + import asyncio + + client = get_async_httpx_client(llm_provider=LlmProviders.ANTHROPIC, params={}) + try: + transport = client.client._transport + # Should be LiteLLMAiohttpTransport when available (default) + assert isinstance(transport, LiteLLMAiohttpTransport) + finally: + asyncio.run(client.close())