48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
"""
|
|
Verifies that the httpx client used by AsyncOpenAI is NOT closed
|
|
when AsyncHTTPHandler instances are garbage collected.
|
|
"""
|
|
|
|
import asyncio
|
|
import gc
|
|
import httpx
|
|
from litellm.llms.openai.common_utils import BaseOpenAILLM
|
|
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
|
|
|
|
|
|
async def test_httpx_client_not_closed_by_handler_gc():
|
|
"""
|
|
Before the fix: _get_async_http_client() returned handler.client,
|
|
so when handler was GC'd its __del__ closed the client.
|
|
After the fix: returns a standalone httpx.AsyncClient, no handler involved.
|
|
"""
|
|
# Get the client the same way AsyncOpenAI would
|
|
client = BaseOpenAILLM._get_async_http_client()
|
|
assert isinstance(client, httpx.AsyncClient)
|
|
|
|
# Simulate what the old code did: create an AsyncHTTPHandler and GC it
|
|
handler = AsyncHTTPHandler()
|
|
handler_client = handler.client
|
|
del handler
|
|
gc.collect()
|
|
|
|
# The client from _get_async_http_client should still be open
|
|
# because it's NOT tied to any AsyncHTTPHandler
|
|
assert not client.is_closed, "Client was closed prematurely!"
|
|
|
|
# Verify it can actually send (build a request without sending)
|
|
try:
|
|
req = client.build_request("GET", "https://example.com")
|
|
print("PASS: Client is still usable after handler GC")
|
|
except RuntimeError as e:
|
|
if "closed" in str(e):
|
|
print(f"FAIL: {e}")
|
|
raise
|
|
raise
|
|
|
|
await client.aclose()
|
|
print("All checks passed!")
|
|
|
|
|
|
asyncio.run(test_httpx_client_not_closed_by_handler_gc())
|