feat: Add lazy loading for get_llm_provider to reduce import time

- Add get_llm_provider to lazy import registry system
- Remove eager import from __init__.py
- Fix circular import by updating internal modules to import directly from source:
  - realtime_api/main.py
  - router_utils/pattern_match_deployments.py
- Add type stub for get_llm_provider in TYPE_CHECKING block
- Add tests for lazy loading of LLM provider logic functions

This reduces initial import time and memory usage by deferring the import
of get_llm_provider until it's actually accessed.
This commit is contained in:
Alexsander Hamir 2026-01-02 12:55:08 -08:00
parent e2f3eaefab
commit a5fed1d67f
6 changed files with 42 additions and 3 deletions

View file

@ -1057,7 +1057,7 @@ openai_image_generation_models = ["dall-e-2", "dall-e-3"]
openai_video_generation_models = ["sora-2"]
# timeout is lazy-loaded via __getattr__
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
# get_llm_provider is lazy-loaded via __getattr__
from litellm.litellm_core_utils.core_helpers import remove_index_from_tool_calls
# Import KeyManagementSettings here (before utils import) because _key_management_settings
@ -1507,6 +1507,7 @@ if TYPE_CHECKING:
get_first_chars_messages: Callable[..., str]
get_provider_fields: Callable[..., List]
get_valid_models: Callable[..., list]
get_llm_provider: Callable[..., Tuple[str, str, Optional[str], Optional[str]]]
# Response types - truly lazy loaded only (not in main.py or elsewhere)
ModelResponseListIterator: Type[Any]

View file

@ -34,6 +34,7 @@ from ._lazy_imports_registry import (
DOTPROMPT_NAMES,
LLM_CONFIG_NAMES,
TYPES_NAMES,
LLM_PROVIDER_LOGIC_NAMES,
# Import maps
_UTILS_IMPORT_MAP,
_COST_CALCULATOR_IMPORT_MAP,
@ -45,6 +46,7 @@ from ._lazy_imports_registry import (
_DOTPROMPT_IMPORT_MAP,
_TYPES_IMPORT_MAP,
_LLM_CONFIGS_IMPORT_MAP,
_LLM_PROVIDER_LOGIC_IMPORT_MAP,
)
@ -181,6 +183,8 @@ def _get_lazy_import_registry() -> dict[str, Callable[[str], Any]]:
_LAZY_IMPORT_REGISTRY[name] = _lazy_import_llm_configs
for name in TYPES_NAMES:
_LAZY_IMPORT_REGISTRY[name] = _lazy_import_types
for name in LLM_PROVIDER_LOGIC_NAMES:
_LAZY_IMPORT_REGISTRY[name] = _lazy_import_llm_provider_logic
return _LAZY_IMPORT_REGISTRY
@ -297,6 +301,11 @@ def _lazy_import_litellm_logging(name: str) -> Any:
"""Handler for litellm_logging module (Logging, modify_integration)"""
return _generic_lazy_import(name, _LITELLM_LOGGING_IMPORT_MAP, "Litellm logging")
def _lazy_import_llm_provider_logic(name: str) -> Any:
"""Handler for LLM provider logic functions (get_llm_provider, etc.)"""
return _generic_lazy_import(name, _LLM_PROVIDER_LOGIC_IMPORT_MAP, "LLM provider logic")
# ============================================================================
# SPECIAL HANDLERS
# ============================================================================

View file

@ -287,6 +287,11 @@ TYPES_NAMES = (
# is accessed during import time in secret_managers/main.py
)
# LLM provider logic names that support lazy loading via _lazy_import_llm_provider_logic
LLM_PROVIDER_LOGIC_NAMES = (
"get_llm_provider",
)
# Import maps for registry pattern - reduces repetition
_UTILS_IMPORT_MAP = {
"exception_type": (".utils", "exception_type"),
@ -386,6 +391,10 @@ _TYPES_IMPORT_MAP = {
"LoggingCallbackManager": ("litellm.litellm_core_utils.logging_callback_manager", "LoggingCallbackManager"),
}
_LLM_PROVIDER_LOGIC_IMPORT_MAP = {
"get_llm_provider": ("litellm.litellm_core_utils.get_llm_provider_logic", "get_llm_provider"),
}
_LLM_CONFIGS_IMPORT_MAP = {
"AmazonConverseConfig": (".llms.bedrock.chat.converse_transformation", "AmazonConverseConfig"),
"OpenAILikeChatConfig": (".llms.openai_like.chat.handler", "OpenAILikeChatConfig"),
@ -587,6 +596,7 @@ __all__ = [
"DOTPROMPT_NAMES",
"LLM_CONFIG_NAMES",
"TYPES_NAMES",
"LLM_PROVIDER_LOGIC_NAMES",
# Import maps
"_UTILS_IMPORT_MAP",
"_COST_CALCULATOR_IMPORT_MAP",
@ -598,5 +608,6 @@ __all__ = [
"_DOTPROMPT_IMPORT_MAP",
"_TYPES_IMPORT_MAP",
"_LLM_CONFIGS_IMPORT_MAP",
"_LLM_PROVIDER_LOGIC_IMPORT_MAP",
]

View file

@ -3,7 +3,7 @@
from typing import Any, Optional, cast
import litellm
from litellm import get_llm_provider
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
from litellm.constants import REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES
from litellm.llms.base_llm.realtime.transformation import BaseRealtimeConfig
from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler

View file

@ -7,7 +7,7 @@ import re
from re import Match
from typing import Dict, List, Optional, Tuple
from litellm import get_llm_provider
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
from litellm._logging import verbose_router_logger

View file

@ -33,6 +33,8 @@ from litellm._lazy_imports import (
_lazy_import_llm_configs,
TYPES_NAMES,
_lazy_import_types,
LLM_PROVIDER_LOGIC_NAMES,
_lazy_import_llm_provider_logic,
)
@ -218,6 +220,9 @@ def test_unknown_attribute_raises_error():
with pytest.raises(AttributeError):
_lazy_import_types("unknown")
with pytest.raises(AttributeError):
_lazy_import_llm_provider_logic("unknown")
def test_llm_config_lazy_imports():
"""Test that LLM config classes can be lazy imported."""
@ -246,3 +251,16 @@ def test_types_lazy_imports():
_verify_only_requested_name_imported(name, TYPES_NAMES)
def test_llm_provider_logic_lazy_imports():
"""Test that LLM provider logic functions can be lazy imported."""
for name in LLM_PROVIDER_LOGIC_NAMES:
_clear_names_from_globals(LLM_PROVIDER_LOGIC_NAMES)
func = _lazy_import_llm_provider_logic(name)
assert func is not None
assert callable(func)
assert name in litellm.__dict__
_verify_only_requested_name_imported(name, LLM_PROVIDER_LOGIC_NAMES)