From 8f647dd25bd6f1c3b60a6f71acb006d40275178a Mon Sep 17 00:00:00 2001 From: Alexsander Hamir Date: Mon, 15 Dec 2025 12:13:51 -0800 Subject: [PATCH] [Refactor] litellm/init.py: lazy load caches (#18001) --- litellm/__init__.py | 10 +++++-- litellm/_lazy_imports.py | 39 +++++++++++++++++++++++++ tests/test_litellm/test_lazy_imports.py | 20 +++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index bd7b03064dd..67fd8a7b075 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -26,7 +26,6 @@ from typing import ( ) from litellm.types.integrations.datadog_llm_obs import DatadogLLMObsInitParams from litellm.types.integrations.datadog import DatadogInitParams -from litellm.caching.caching import Cache, DualCache, RedisCache, InMemoryCache from litellm.caching.llm_caching_handler import LLMClientCache from litellm.types.llms.bedrock import COHERE_EMBEDDING_INPUT_TYPES from litellm.types.utils import ( @@ -332,7 +331,7 @@ caching: bool = ( caching_with_models: bool = ( False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 ) -cache: Optional[Cache] = ( +cache: Optional["Cache"] = ( None # cache object <- use this - https://docs.litellm.ai/docs/caching ) default_in_memory_ttl: Optional[float] = None @@ -1516,6 +1515,7 @@ def set_global_gitlab_config(config: Dict[str, Any]) -> None: if TYPE_CHECKING: from litellm.types.utils import ModelInfo as _ModelInfoType from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler + from litellm.caching.caching import Cache # Cost calculator functions cost_per_token: Callable[..., Tuple[float, float]] @@ -1567,6 +1567,7 @@ def __getattr__(name: str) -> Any: COST_CALCULATOR_NAMES, LITELLM_LOGGING_NAMES, UTILS_NAMES, + CACHING_NAMES, HTTP_HANDLER_NAMES, ) @@ -1585,6 +1586,11 @@ def __getattr__(name: str) -> Any: from ._lazy_imports import _lazy_import_utils return _lazy_import_utils(name) + # Lazy load caching classes + if name in CACHING_NAMES: + from ._lazy_imports import _lazy_import_caching + return _lazy_import_caching(name) + # Lazy-load HTTP handler singletons used across the codebase if name in HTTP_HANDLER_NAMES: from ._lazy_imports import _lazy_import_http_handlers diff --git a/litellm/_lazy_imports.py b/litellm/_lazy_imports.py index 17772682599..c9655f0d2ff 100644 --- a/litellm/_lazy_imports.py +++ b/litellm/_lazy_imports.py @@ -34,6 +34,14 @@ UTILS_NAMES = ( "ModelResponseListIterator", "get_valid_models", ) +# Caching / cache classes that support lazy loading via _lazy_import_caching +CACHING_NAMES = ( + "Cache", + "DualCache", + "RedisCache", + "InMemoryCache", +) + # HTTP handler names that support lazy loading via _lazy_import_http_handlers HTTP_HANDLER_NAMES = ( "module_level_aclient", @@ -271,6 +279,37 @@ def _lazy_import_cost_calculator(name: str) -> Any: raise AttributeError(f"Cost calculator lazy import: unknown attribute {name!r}") +def _lazy_import_caching(name: str) -> Any: + """Lazy import for caching module classes.""" + _globals = _get_litellm_globals() + + if name == "Cache": + from litellm.caching.caching import Cache as _Cache + + _globals["Cache"] = _Cache + return _Cache + + if name == "DualCache": + from litellm.caching.caching import DualCache as _DualCache + + _globals["DualCache"] = _DualCache + return _DualCache + + if name == "RedisCache": + from litellm.caching.caching import RedisCache as _RedisCache + + _globals["RedisCache"] = _RedisCache + return _RedisCache + + if name == "InMemoryCache": + from litellm.caching.caching import InMemoryCache as _InMemoryCache + + _globals["InMemoryCache"] = _InMemoryCache + return _InMemoryCache + + raise AttributeError(f"Caching lazy import: unknown attribute {name!r}") + + def _lazy_import_litellm_logging(name: str) -> Any: """Lazy import for litellm_logging module.""" _globals = _get_litellm_globals() diff --git a/tests/test_litellm/test_lazy_imports.py b/tests/test_litellm/test_lazy_imports.py index 42060579842..c623ecc4b0e 100644 --- a/tests/test_litellm/test_lazy_imports.py +++ b/tests/test_litellm/test_lazy_imports.py @@ -12,10 +12,12 @@ from litellm._lazy_imports import ( COST_CALCULATOR_NAMES, LITELLM_LOGGING_NAMES, UTILS_NAMES, + CACHING_NAMES, HTTP_HANDLER_NAMES, _lazy_import_cost_calculator, _lazy_import_litellm_logging, _lazy_import_utils, + _lazy_import_caching, _lazy_import_http_handlers, ) @@ -80,6 +82,21 @@ def test_utils_lazy_imports(): _verify_only_requested_name_imported(name, UTILS_NAMES) +def test_caching_lazy_imports(): + """Test that all caching classes can be lazy imported.""" + # Test each name individually - only that name should be imported + for name in CACHING_NAMES: + # Clear all names before importing just one + _clear_names_from_globals(CACHING_NAMES) + + cls = _lazy_import_caching(name) + assert cls is not None + assert name in litellm.__dict__ + + # Verify only the requested name is in globals, not the others + _verify_only_requested_name_imported(name, CACHING_NAMES) + + def test_http_handler_lazy_imports(): """Test that HTTP handler singletons can be lazy imported.""" for name in HTTP_HANDLER_NAMES: @@ -103,3 +120,6 @@ def test_unknown_attribute_raises_error(): with pytest.raises(AttributeError): _lazy_import_utils("unknown") + with pytest.raises(AttributeError): + _lazy_import_caching("unknown") +