mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-08 22:21:35 +00:00
fix: prevent httpx DeprecationWarning memory leak in AsyncHTTPHandler (#16024)
* fix: prevent httpx DeprecationWarning memory leak in AsyncHTTPHandler
Route bytes/str to content= parameter instead of data= to avoid deprecation warning that causes memory leak
* refactor: extract data/content preparation into helper function
Create _prepare_request_data_and_content() helper to DRY up the logic
for routing data/content parameters correctly in httpx requests.
This helper prevents httpx DeprecationWarnings (which cause memory leaks)
by moving bytes/str from data= to content= parameter while keeping
dict/Mapping in data= parameter.
Applied the helper consistently across all HTTP methods in both
AsyncHTTPHandler and HTTPHandler classes:
- post(), put(), patch(), delete()
- single_connection_post_request()
Related to: b850ed1188
* fix: Python 3.8 compatibility - use Tuple instead of tuple in type hints
Replace lowercase tuple[...] with typing.Tuple[...] in http_handler.py
to fix 'TypeError: type object is not subscriptable' on Python 3.8
This commit is contained in:
parent
5bba1e8405
commit
4939793ade
1 changed files with 97 additions and 23 deletions
|
|
@ -3,7 +3,7 @@ import os
|
|||
import ssl
|
||||
import sys
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Union
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Union
|
||||
|
||||
import certifi
|
||||
import httpx
|
||||
|
|
@ -47,6 +47,46 @@ headers = {
|
|||
_DEFAULT_TIMEOUT = httpx.Timeout(timeout=5.0, connect=5.0)
|
||||
|
||||
|
||||
def _prepare_request_data_and_content(
|
||||
data: Optional[Union[dict, str, bytes]] = None,
|
||||
content: Any = None,
|
||||
) -> Tuple[Optional[Union[dict, Mapping]], Any]:
|
||||
"""
|
||||
Helper function to route data/content parameters correctly for httpx requests
|
||||
|
||||
This prevents httpx DeprecationWarnings that cause memory leaks.
|
||||
|
||||
Background:
|
||||
- httpx shows a DeprecationWarning when you pass bytes/str to `data=`
|
||||
- It wants you to use `content=` instead for bytes/str
|
||||
- The warning itself leaks memory when triggered repeatedly
|
||||
|
||||
Solution:
|
||||
- Move bytes/str from `data=` to `content=` before calling build_request
|
||||
- Keep dicts in `data=` (that's still the correct parameter for dicts)
|
||||
|
||||
Args:
|
||||
data: Request data (can be dict, str, or bytes)
|
||||
content: Request content (raw bytes/str)
|
||||
|
||||
Returns:
|
||||
Tuple of (request_data, request_content) properly routed for httpx
|
||||
"""
|
||||
request_data = None
|
||||
request_content = content
|
||||
|
||||
if data is not None:
|
||||
if isinstance(data, (bytes, str)):
|
||||
# Bytes/strings belong in content= (only if not already provided)
|
||||
if content is None:
|
||||
request_content = data
|
||||
else:
|
||||
# dict/Mapping stays in data= parameter
|
||||
request_data = data
|
||||
|
||||
return request_data, request_content
|
||||
|
||||
|
||||
def get_ssl_configuration(
|
||||
ssl_verify: Optional[VerifyTypes] = None,
|
||||
) -> Union[bool, str, ssl.SSLContext]:
|
||||
|
|
@ -301,17 +341,20 @@ class AsyncHTTPHandler:
|
|||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
req = self.client.build_request(
|
||||
"POST",
|
||||
url,
|
||||
data=data, # type: ignore
|
||||
data=request_data,
|
||||
json=json,
|
||||
params=params,
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
files=files,
|
||||
content=content,
|
||||
)
|
||||
content=request_content,
|
||||
)
|
||||
response = await self.client.send(req, stream=stream)
|
||||
response.raise_for_status()
|
||||
return response
|
||||
|
|
@ -364,19 +407,23 @@ class AsyncHTTPHandler:
|
|||
async def put(
|
||||
self,
|
||||
url: str,
|
||||
data: Optional[Union[dict, str]] = None, # type: ignore
|
||||
data: Optional[Union[dict, str, bytes]] = None, # type: ignore
|
||||
json: Optional[dict] = None,
|
||||
params: Optional[dict] = None,
|
||||
headers: Optional[dict] = None,
|
||||
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
||||
stream: bool = False,
|
||||
content: Any = None,
|
||||
):
|
||||
try:
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
req = self.client.build_request(
|
||||
"PUT", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore
|
||||
"PUT", url, data=request_data, json=json, params=params, headers=headers, timeout=timeout, content=request_content # type: ignore
|
||||
)
|
||||
response = await self.client.send(req)
|
||||
response.raise_for_status()
|
||||
|
|
@ -424,19 +471,23 @@ class AsyncHTTPHandler:
|
|||
async def patch(
|
||||
self,
|
||||
url: str,
|
||||
data: Optional[Union[dict, str]] = None, # type: ignore
|
||||
data: Optional[Union[dict, str, bytes]] = None, # type: ignore
|
||||
json: Optional[dict] = None,
|
||||
params: Optional[dict] = None,
|
||||
headers: Optional[dict] = None,
|
||||
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
||||
stream: bool = False,
|
||||
content: Any = None,
|
||||
):
|
||||
try:
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
req = self.client.build_request(
|
||||
"PATCH", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore
|
||||
"PATCH", url, data=request_data, json=json, params=params, headers=headers, timeout=timeout, content=request_content # type: ignore
|
||||
)
|
||||
response = await self.client.send(req)
|
||||
response.raise_for_status()
|
||||
|
|
@ -484,18 +535,23 @@ class AsyncHTTPHandler:
|
|||
async def delete(
|
||||
self,
|
||||
url: str,
|
||||
data: Optional[Union[dict, str]] = None, # type: ignore
|
||||
data: Optional[Union[dict, str, bytes]] = None, # type: ignore
|
||||
json: Optional[dict] = None,
|
||||
params: Optional[dict] = None,
|
||||
headers: Optional[dict] = None,
|
||||
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
||||
stream: bool = False,
|
||||
content: Any = None,
|
||||
):
|
||||
try:
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
req = self.client.build_request(
|
||||
"DELETE", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore
|
||||
"DELETE", url, data=request_data, json=json, params=params, headers=headers, timeout=timeout, content=request_content # type: ignore
|
||||
)
|
||||
response = await self.client.send(req, stream=stream)
|
||||
response.raise_for_status()
|
||||
|
|
@ -543,8 +599,11 @@ class AsyncHTTPHandler:
|
|||
|
||||
Used for retrying connection client errors.
|
||||
"""
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
req = client.build_request(
|
||||
"POST", url, data=data, json=json, params=params, headers=headers, content=content # type: ignore
|
||||
"POST", url, data=request_data, json=json, params=params, headers=headers, content=request_content # type: ignore
|
||||
)
|
||||
response = await client.send(req, stream=stream)
|
||||
response.raise_for_status()
|
||||
|
|
@ -798,21 +857,24 @@ class HTTPHandler:
|
|||
logging_obj: Optional[LiteLLMLoggingObject] = None,
|
||||
):
|
||||
try:
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
if timeout is not None:
|
||||
req = self.client.build_request(
|
||||
"POST",
|
||||
url,
|
||||
data=data, # type: ignore
|
||||
data=request_data, # type: ignore
|
||||
json=json,
|
||||
params=params,
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
files=files,
|
||||
content=content, # type: ignore
|
||||
content=request_content, # type: ignore
|
||||
)
|
||||
else:
|
||||
req = self.client.build_request(
|
||||
"POST", url, data=data, json=json, params=params, headers=headers, files=files, content=content # type: ignore
|
||||
"POST", url, data=request_data, json=json, params=params, headers=headers, files=files, content=request_content # type: ignore
|
||||
)
|
||||
response = self.client.send(req, stream=stream)
|
||||
response.raise_for_status()
|
||||
|
|
@ -840,21 +902,25 @@ class HTTPHandler:
|
|||
def patch(
|
||||
self,
|
||||
url: str,
|
||||
data: Optional[Union[dict, str]] = None,
|
||||
data: Optional[Union[dict, str, bytes]] = None,
|
||||
json: Optional[Union[dict, str]] = None,
|
||||
params: Optional[dict] = None,
|
||||
headers: Optional[dict] = None,
|
||||
stream: bool = False,
|
||||
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
||||
content: Any = None,
|
||||
):
|
||||
try:
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
if timeout is not None:
|
||||
req = self.client.build_request(
|
||||
"PATCH", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore
|
||||
"PATCH", url, data=request_data, json=json, params=params, headers=headers, timeout=timeout, content=request_content # type: ignore
|
||||
)
|
||||
else:
|
||||
req = self.client.build_request(
|
||||
"PATCH", url, data=data, json=json, params=params, headers=headers # type: ignore
|
||||
"PATCH", url, data=request_data, json=json, params=params, headers=headers, content=request_content # type: ignore
|
||||
)
|
||||
response = self.client.send(req, stream=stream)
|
||||
response.raise_for_status()
|
||||
|
|
@ -883,21 +949,25 @@ class HTTPHandler:
|
|||
def put(
|
||||
self,
|
||||
url: str,
|
||||
data: Optional[Union[dict, str]] = None,
|
||||
data: Optional[Union[dict, str, bytes]] = None,
|
||||
json: Optional[Union[dict, str]] = None,
|
||||
params: Optional[dict] = None,
|
||||
headers: Optional[dict] = None,
|
||||
stream: bool = False,
|
||||
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
||||
content: Any = None,
|
||||
):
|
||||
try:
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
if timeout is not None:
|
||||
req = self.client.build_request(
|
||||
"PUT", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore
|
||||
"PUT", url, data=request_data, json=json, params=params, headers=headers, timeout=timeout, content=request_content # type: ignore
|
||||
)
|
||||
else:
|
||||
req = self.client.build_request(
|
||||
"PUT", url, data=data, json=json, params=params, headers=headers # type: ignore
|
||||
"PUT", url, data=request_data, json=json, params=params, headers=headers, content=request_content # type: ignore
|
||||
)
|
||||
response = self.client.send(req, stream=stream)
|
||||
return response
|
||||
|
|
@ -913,21 +983,25 @@ class HTTPHandler:
|
|||
def delete(
|
||||
self,
|
||||
url: str,
|
||||
data: Optional[Union[dict, str]] = None, # type: ignore
|
||||
data: Optional[Union[dict, str, bytes]] = None, # type: ignore
|
||||
json: Optional[dict] = None,
|
||||
params: Optional[dict] = None,
|
||||
headers: Optional[dict] = None,
|
||||
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
||||
stream: bool = False,
|
||||
content: Any = None,
|
||||
):
|
||||
try:
|
||||
# Prepare data/content parameters to prevent httpx DeprecationWarning (memory leak fix)
|
||||
request_data, request_content = _prepare_request_data_and_content(data, content)
|
||||
|
||||
if timeout is not None:
|
||||
req = self.client.build_request(
|
||||
"DELETE", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore
|
||||
"DELETE", url, data=request_data, json=json, params=params, headers=headers, timeout=timeout, content=request_content # type: ignore
|
||||
)
|
||||
else:
|
||||
req = self.client.build_request(
|
||||
"DELETE", url, data=data, json=json, params=params, headers=headers # type: ignore
|
||||
"DELETE", url, data=request_data, json=json, params=params, headers=headers, content=request_content # type: ignore
|
||||
)
|
||||
response = self.client.send(req, stream=stream)
|
||||
response.raise_for_status()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue