feat: lazy load heavy imports to reduce memory usage at import time (#18592)

- Lazy load remove_index_from_tool_calls via __getattr__
- Lazy load _service_logger module via __getattr__ in __init__.py
- Lazy load audio_utils.utils with module-level caching in utils.py
- Remove direct imports that trigger heavy module loading

These changes reduce import-time memory usage by deferring imports
until they are actually needed, while maintaining performance through
caching for subsequent accesses.
This commit is contained in:
Alexsander Hamir
2026-01-02 13:51:28 -08:00
committed by GitHub
parent 705b54bf04
commit 3b1792d728
3 changed files with 38 additions and 4 deletions
+13 -1
View File
@@ -1058,7 +1058,7 @@ openai_video_generation_models = ["sora-2"]
# timeout is lazy-loaded via __getattr__
# get_llm_provider is lazy-loaded via __getattr__
from litellm.litellm_core_utils.core_helpers import remove_index_from_tool_calls
# remove_index_from_tool_calls is lazy-loaded via __getattr__
# Import KeyManagementSettings here (before utils import) because _key_management_settings
# is accessed during import time in secret_managers/main.py (via dd_tracing -> datadog -> _service_logger -> utils)
@@ -1507,6 +1507,7 @@ if TYPE_CHECKING:
get_first_chars_messages: Callable[..., str]
get_provider_fields: Callable[..., List]
get_valid_models: Callable[..., list]
remove_index_from_tool_calls: Callable[..., None]
# Response types - truly lazy loaded only (not in main.py or elsewhere)
ModelResponseListIterator: Type[Any]
@@ -1658,6 +1659,17 @@ def __getattr__(name: str) -> Any:
LoggingCallbackManager = __getattr__("LoggingCallbackManager")
_globals["logging_callback_manager"] = LoggingCallbackManager()
return _globals["logging_callback_manager"]
# Lazy load _service_logger module
if name == "_service_logger":
from ._lazy_imports import _get_litellm_globals
_globals = _get_litellm_globals()
# Check if already cached
if "_service_logger" not in _globals:
# Import the module lazily
import litellm._service_logger
_globals["_service_logger"] = litellm._service_logger
return _globals["_service_logger"]
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+2
View File
@@ -290,6 +290,7 @@ TYPES_NAMES = (
# LLM provider logic names that support lazy loading via _lazy_import_llm_provider_logic
LLM_PROVIDER_LOGIC_NAMES = (
"get_llm_provider",
"remove_index_from_tool_calls",
)
# Import maps for registry pattern - reduces repetition
@@ -393,6 +394,7 @@ _TYPES_IMPORT_MAP = {
_LLM_PROVIDER_LOGIC_IMPORT_MAP = {
"get_llm_provider": ("litellm.litellm_core_utils.get_llm_provider_logic", "get_llm_provider"),
"remove_index_from_tool_calls": ("litellm.litellm_core_utils.core_helpers", "remove_index_from_tool_calls"),
}
_LLM_CONFIGS_IMPORT_MAP = {
+23 -3
View File
@@ -48,9 +48,9 @@ from tiktoken import Encoding
from tokenizers import Tokenizer
import litellm
import litellm._service_logger # for storing API inputs, outputs, and metadata
import litellm.litellm_core_utils
import litellm.litellm_core_utils.audio_utils.utils
# audio_utils.utils is lazy-loaded - only imported when needed for transcription calls
import litellm.litellm_core_utils.json_validation_rule
import litellm.llms
import litellm.llms.gemini
@@ -157,6 +157,24 @@ from litellm.router_utils.get_retry_from_policy import (
reset_retry_policy,
)
from litellm.secret_managers.main import get_secret
# Cached lazy import for audio_utils.utils
# Module-level cache to avoid repeated imports while preserving memory benefits
_audio_utils_module = None
def _get_cached_audio_utils():
"""
Get cached audio_utils.utils module.
Lazy imports on first call to avoid loading audio_utils.utils at import time.
Subsequent calls use cached module for better performance.
"""
global _audio_utils_module
if _audio_utils_module is None:
import litellm.litellm_core_utils.audio_utils.utils
_audio_utils_module = litellm.litellm_core_utils.audio_utils.utils
return _audio_utils_module
from litellm.types.llms.anthropic import (
ANTHROPIC_API_ONLY_HEADERS,
AnthropicThinkingParam,
@@ -965,7 +983,9 @@ def function_setup( # noqa: PLR0915
or call_type == CallTypes.transcription.value
):
_file_obj: FileTypes = args[1] if len(args) > 1 else kwargs["file"]
file_checksum = litellm.litellm_core_utils.audio_utils.utils.get_audio_file_content_hash(
# Lazy import audio_utils.utils only when needed for transcription calls
audio_utils = _get_cached_audio_utils()
file_checksum = audio_utils.get_audio_file_content_hash(
file_obj=_file_obj
)
if "metadata" in kwargs: