From a5fed1d67f7acd503b93398e46ce6f0c8477829f Mon Sep 17 00:00:00 2001 From: Alexsander Hamir Date: Fri, 2 Jan 2026 12:55:08 -0800 Subject: [PATCH] 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. --- litellm/__init__.py | 3 ++- litellm/_lazy_imports.py | 9 +++++++++ litellm/_lazy_imports_registry.py | 11 +++++++++++ litellm/realtime_api/main.py | 2 +- .../router_utils/pattern_match_deployments.py | 2 +- tests/test_litellm/test_lazy_imports.py | 18 ++++++++++++++++++ 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 791767b04a6..4f912d16fc9 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -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] diff --git a/litellm/_lazy_imports.py b/litellm/_lazy_imports.py index c1b3e1df976..5ea9dd9b321 100644 --- a/litellm/_lazy_imports.py +++ b/litellm/_lazy_imports.py @@ -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 # ============================================================================ diff --git a/litellm/_lazy_imports_registry.py b/litellm/_lazy_imports_registry.py index e2f80a14391..01be9c095d4 100644 --- a/litellm/_lazy_imports_registry.py +++ b/litellm/_lazy_imports_registry.py @@ -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", ] diff --git a/litellm/realtime_api/main.py b/litellm/realtime_api/main.py index fe686598141..e26a2477b1b 100644 --- a/litellm/realtime_api/main.py +++ b/litellm/realtime_api/main.py @@ -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 diff --git a/litellm/router_utils/pattern_match_deployments.py b/litellm/router_utils/pattern_match_deployments.py index c6804b1ad4c..69d6ab9b6e2 100644 --- a/litellm/router_utils/pattern_match_deployments.py +++ b/litellm/router_utils/pattern_match_deployments.py @@ -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 diff --git a/tests/test_litellm/test_lazy_imports.py b/tests/test_litellm/test_lazy_imports.py index 0eaedaab601..7de99495b79 100644 --- a/tests/test_litellm/test_lazy_imports.py +++ b/tests/test_litellm/test_lazy_imports.py @@ -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) +