mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
[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:
parent
c0239a0a79
commit
9a608e3749
6 changed files with 386 additions and 105 deletions
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
381
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue