mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-09 22:31:41 +00:00
fix: register async client cleanup eagerly to prevent SSL exit errors.
Closes #23278
This commit is contained in:
parent
1103a8c620
commit
26fedb3fac
2 changed files with 28 additions and 7 deletions
|
|
@ -1650,6 +1650,15 @@ if TYPE_CHECKING:
|
|||
# Track if async client cleanup has been registered (for lazy loading)
|
||||
_async_client_cleanup_registered = False
|
||||
|
||||
# Register async client cleanup eagerly so it runs even when only eagerly-imported
|
||||
# symbols (e.g. `acompletion`, imported via `from .main import *`) are used.
|
||||
# Without this, __getattr__ is never triggered and SSL connections are left open
|
||||
# at process exit, causing "Fatal error on SSL transport" / "Event loop is closed"
|
||||
# tracebacks with asyncio.gather (see issue #23278).
|
||||
from litellm.llms.custom_httpx.async_client_cleanup import register_async_client_cleanup
|
||||
register_async_client_cleanup()
|
||||
_async_client_cleanup_registered = True
|
||||
|
||||
# Eager loading for backwards compatibility with VCR and other HTTP recording tools
|
||||
# When LITELLM_DISABLE_LAZY_LOADING is set, lazy-loaded attributes are loaded at import time
|
||||
# For now, this only affects encoding (tiktoken) as it was the only reported issue
|
||||
|
|
@ -1663,13 +1672,6 @@ if os.getenv("LITELLM_DISABLE_LAZY_LOADING", "").lower() in ("1", "true", "yes",
|
|||
|
||||
def __getattr__(name: str) -> Any:
|
||||
"""Lazy import handler with cached registry for improved performance."""
|
||||
global _async_client_cleanup_registered
|
||||
# Register async client cleanup on first access (only once)
|
||||
if not _async_client_cleanup_registered:
|
||||
from litellm.llms.custom_httpx.async_client_cleanup import register_async_client_cleanup
|
||||
register_async_client_cleanup()
|
||||
_async_client_cleanup_registered = True
|
||||
|
||||
# Use cached registry from _lazy_imports instead of importing tuples every time
|
||||
from ._lazy_imports import _get_lazy_import_registry
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,25 @@ async def test_atexit_cleanup():
|
|||
return False
|
||||
|
||||
|
||||
def test_async_client_cleanup_registered_at_import_time():
|
||||
"""
|
||||
Regression test for issue #23278: SSL transport errors with asyncio.gather.
|
||||
|
||||
acompletion is imported at module level via `from .main import *`, so
|
||||
__getattr__ is never triggered when users call litellm.acompletion().
|
||||
register_async_client_cleanup() must be called eagerly at import time,
|
||||
not lazily inside __getattr__, otherwise SSL connections are left open
|
||||
at process exit causing "Fatal error on SSL transport" errors.
|
||||
"""
|
||||
import litellm
|
||||
|
||||
assert litellm._async_client_cleanup_registered is True, (
|
||||
"register_async_client_cleanup() must be called at import time. "
|
||||
"If this fails, SSL connections will leak on process exit when "
|
||||
"acompletion() is used with asyncio.gather() (issue #23278)."
|
||||
)
|
||||
|
||||
|
||||
def test_new_event_loop_atexit():
|
||||
"""Test that the new atexit handler can create a fresh event loop"""
|
||||
print("\n" + "=" * 70)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue