Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions litellm/proxy/proxy_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,12 @@
if db_writer_client is not None:
await db_writer_client.close() # type: ignore[reportGeneralTypeIssues]

from litellm.llms.custom_httpx.async_client_cleanup import (
close_litellm_async_clients,
)

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
litellm.llms.custom_httpx.async_client_cleanup
begins an import cycle.
Comment on lines +707 to +709

await close_litellm_async_clients()

# flush remaining langfuse logs
if "langfuse" in litellm.success_callback:
try:
Expand Down
32 changes: 32 additions & 0 deletions tests/test_litellm/proxy/proxy_server/test_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,38 @@ async def test_proxy_shutdown_event_prisma_disconnect_raises_error(monkeypatch):
await proxy_shutdown_event()


@pytest.mark.asyncio
async def test_proxy_shutdown_event_closes_cached_aiohttp_sessions(monkeypatch):
"""Regression: the shutdown handler must close cached async HTTP clients so
aiohttp does not log 'Unclosed client session'. A BaseLLMAIOHTTPHandler
living in litellm.in_memory_llm_clients_cache holds an open ClientSession;
after proxy_shutdown_event runs the session must be closed."""
import litellm
from litellm.llms.custom_httpx.aiohttp_handler import BaseLLMAIOHTTPHandler

fake_jwt = MagicMock()
fake_jwt.close = AsyncMock()
monkeypatch.setattr(ps, "jwt_handler", fake_jwt, raising=False)
monkeypatch.setattr(ps, "prisma_client", None, raising=False)
monkeypatch.setattr(ps, "db_writer_client", None, raising=False)
monkeypatch.setattr(litellm, "cache", None, raising=False)
monkeypatch.setattr(litellm, "success_callback", [], raising=False)

handler = BaseLLMAIOHTTPHandler()
session = handler._get_async_client_session()
assert not session.closed

cache_dict = litellm.in_memory_llm_clients_cache.cache_dict
cache_dict["witness-aiohttp-handler"] = handler
try:
await proxy_shutdown_event()
assert session.closed
finally:
cache_dict.pop("witness-aiohttp-handler", None)
if not session.closed:
await session.close()


# ---------------------------------------------------------------------------
# _initialize_shared_aiohttp_session
# ---------------------------------------------------------------------------
Expand Down
Loading