[Fix] Rollback to httpx==0.27.0 (#11146)

* fix - fix - use old httpx version

* fix - fix - use old httpx version

* fix: test

* fix: comment
This commit is contained in:
Ishaan Jaff 2025-05-26 10:44:11 -07:00
parent c0239a0a79
commit 9a608e3749
6 changed files with 386 additions and 105 deletions

View file

@ -1,12 +1,72 @@
import asyncio
import contextlib
import typing
from typing import Callable, Union
import aiohttp
import httpx
from aiohttp.client import ClientSession
from httpx_aiohttp import AiohttpTransport
from aiohttp.client import ClientResponse, ClientSession
from litellm._logging import verbose_logger
AIOHTTP_EXC_MAP = {
aiohttp.ServerTimeoutError: httpx.TimeoutException,
aiohttp.ConnectionTimeoutError: httpx.ConnectTimeout,
aiohttp.SocketTimeoutError: httpx.ReadTimeout,
aiohttp.ClientConnectorError: httpx.ConnectError,
aiohttp.ClientPayloadError: httpx.ReadError,
aiohttp.ClientProxyConnectionError: httpx.ProxyError,
}
@contextlib.contextmanager
def map_aiohttp_exceptions() -> typing.Iterator[None]:
try:
yield
except Exception as exc:
mapped_exc = None
for from_exc, to_exc in AIOHTTP_EXC_MAP.items():
if not isinstance(exc, from_exc): # type: ignore
continue
if mapped_exc is None or issubclass(to_exc, mapped_exc):
mapped_exc = to_exc
if mapped_exc is None: # pragma: no cover
raise
message = str(exc)
raise mapped_exc(message) from exc
class AiohttpResponseStream(httpx.AsyncByteStream):
CHUNK_SIZE = 1024 * 16
def __init__(self, aiohttp_response: ClientResponse) -> None:
self._aiohttp_response = aiohttp_response
async def __aiter__(self) -> typing.AsyncIterator[bytes]:
with map_aiohttp_exceptions():
async for chunk in self._aiohttp_response.content.iter_chunked(
self.CHUNK_SIZE
):
yield chunk
async def aclose(self) -> None:
with map_aiohttp_exceptions():
await self._aiohttp_response.__aexit__(None, None, None)
class AiohttpTransport(httpx.AsyncBaseTransport):
def __init__(
self, client: ClientSession | typing.Callable[[], ClientSession]
) -> None:
self.client = client
async def aclose(self) -> None:
if isinstance(self.client, ClientSession):
await self.client.close()
class LiteLLMAiohttpTransport(AiohttpTransport):
"""

View file

@ -490,13 +490,9 @@ class AsyncHTTPHandler:
- Some users have seen httpx ConnectionError when using ipv6 - forcing ipv4 resolves the issue for them
"""
#########################################################
# AIOHTTP TRANSPORT is used by default
# httpx_aiohttp is included in litellm docker images and pip when python 3.9+ is used
# AIOHTTP TRANSPORT is off by default
#########################################################
if (
AsyncHTTPHandler._should_use_aiohttp_transport()
and AsyncHTTPHandler.aiohttp_transport_exists()
):
if AsyncHTTPHandler._should_use_aiohttp_transport():
return AsyncHTTPHandler._create_aiohttp_transport(
ssl_context=ssl_context, ssl_verify=ssl_verify
)
@ -562,22 +558,6 @@ class AsyncHTTPHandler:
else:
return None
@staticmethod
def aiohttp_transport_exists() -> bool:
"""
Returns True if `httpx-aiohttp` is installed.
`httpx-aiohttp` only supports python 3.9+
For users on python 3.8, we will use `httpx.AsyncClient` instead of `httpx-aiohttp`.
"""
try:
import importlib.util
return importlib.util.find_spec("httpx_aiohttp") is not None
except Exception:
return False
class HTTPHandler:
def __init__(

381
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,6 @@ Documentation = "https://docs.litellm.ai"
[tool.poetry.dependencies]
python = ">=3.8.1,<4.0, !=3.9.7"
httpx = ">=0.23.0"
httpx-aiohttp = {version = ">=0.1.4", python = ">=3.9"}
openai = ">=1.68.2"
python-dotenv = ">=0.2.0"
tiktoken = ">=0.7.0"

View file

@ -1,7 +1,6 @@
# LITELLM PROXY DEPENDENCIES #
anyio==4.5.0 # openai + http req.
httpx==0.28.1 # Pin Httpx dependency
httpx-aiohttp==0.1.4 # for aiohttp transport
httpx==0.27.0 # Pin Httpx dependency
openai==1.81.0 # openai req.
fastapi==0.115.5 # server dep
backoff==2.2.1 # server dep

View file

@ -45,24 +45,6 @@ async def test_ssl_security_level(monkeypatch):
# assert "DEFAULT@SECLEVEL=1" in ssl_context.get_ciphers()
@pytest.mark.asyncio
async def test_aiohttp_transport_creation():
"""Test that aiohttp transport is created when enabled and available"""
litellm.use_aiohttp_transport = True
# Mock aiohttp_transport_exists to return True
with patch.object(AsyncHTTPHandler, "aiohttp_transport_exists", return_value=True):
transport = AsyncHTTPHandler._create_async_transport()
# Verify we got an aiohttp transport
assert isinstance(transport, LiteLLMAiohttpTransport)
# Verify the transport creates valid client sessions
if isinstance(transport, LiteLLMAiohttpTransport):
client_session = transport._get_valid_client_session()
assert isinstance(client_session, ClientSession)
@pytest.mark.asyncio
async def test_force_ipv4_transport():
"""Test transport creation with force_ipv4 enabled"""