From 64242d216a3617386c5facf5ca56fe248d7d9adb Mon Sep 17 00:00:00 2001 From: Alexsander Hamir Date: Mon, 5 Jan 2026 09:19:00 -0800 Subject: [PATCH] refactor: migrate utils.py lazy imports to registry pattern - Refactored utils.py __getattr__ to use cached registry pattern (similar to __init__.py) - Added UTILS_MODULE_NAMES tuple and _UTILS_MODULE_IMPORT_MAP to _lazy_imports_registry.py - Added _get_utils_globals() helper function to _lazy_imports.py - Added _lazy_import_utils_module() handler function for utils module lazy imports - Updated _get_lazy_import_registry() to include utils module lazy imports - Removed redundant _get_utils_globals() from utils.py (now in _lazy_imports.py) - Added comprehensive tests for utils module lazy imports in test_lazy_imports.py This refactoring: - Reduces code duplication (from 670+ lines to ~10 lines in __getattr__) - Improves maintainability (new lazy imports just need registry entry) - Maintains consistency with __init__.py lazy import pattern - All existing functionality preserved and tested --- litellm/_lazy_imports.py | 51 ++ litellm/_lazy_imports_registry.py | 149 ++++++ litellm/utils.py | 683 +----------------------- tests/test_litellm/test_lazy_imports.py | 31 ++ 4 files changed, 240 insertions(+), 674 deletions(-) diff --git a/litellm/_lazy_imports.py b/litellm/_lazy_imports.py index 5ea9dd9b321..3bfeba2e394 100644 --- a/litellm/_lazy_imports.py +++ b/litellm/_lazy_imports.py @@ -35,6 +35,7 @@ from ._lazy_imports_registry import ( LLM_CONFIG_NAMES, TYPES_NAMES, LLM_PROVIDER_LOGIC_NAMES, + UTILS_MODULE_NAMES, # Import maps _UTILS_IMPORT_MAP, _COST_CALCULATOR_IMPORT_MAP, @@ -47,6 +48,7 @@ from ._lazy_imports_registry import ( _TYPES_IMPORT_MAP, _LLM_CONFIGS_IMPORT_MAP, _LLM_PROVIDER_LOGIC_IMPORT_MAP, + _UTILS_MODULE_IMPORT_MAP, ) @@ -59,6 +61,16 @@ def _get_litellm_globals() -> dict: """ return sys.modules["litellm"].__dict__ + +def _get_utils_globals() -> dict: + """ + Get the globals dictionary of the utils module. + + This is where we cache imported attributes so we don't import them twice. + When you do `litellm.utils.some_function`, it gets stored in this dictionary. + """ + return sys.modules["litellm.utils"].__dict__ + # These are special lazy loaders for things that are used internally # They're separate from the main lazy import system because they have specific use cases @@ -185,6 +197,8 @@ def _get_lazy_import_registry() -> dict[str, Callable[[str], Any]]: _LAZY_IMPORT_REGISTRY[name] = _lazy_import_types for name in LLM_PROVIDER_LOGIC_NAMES: _LAZY_IMPORT_REGISTRY[name] = _lazy_import_llm_provider_logic + for name in UTILS_MODULE_NAMES: + _LAZY_IMPORT_REGISTRY[name] = _lazy_import_utils_module return _LAZY_IMPORT_REGISTRY @@ -306,6 +320,43 @@ 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") + +def _lazy_import_utils_module(name: str) -> Any: + """ + Handler for utils module lazy imports. + + This uses a custom implementation because utils module needs to use + _get_utils_globals() instead of _get_litellm_globals() for caching. + """ + # Check if this attribute exists in our map + if name not in _UTILS_MODULE_IMPORT_MAP: + raise AttributeError(f"Utils module lazy import: unknown attribute {name!r}") + + # Get the cache (where we store imported things) - use utils globals + _globals = _get_utils_globals() + + # If we've already imported it, just return the cached version + if name in _globals: + return _globals[name] + + # Look up where to find this attribute + module_path, attr_name = _UTILS_MODULE_IMPORT_MAP[name] + + # Import the module + if module_path.startswith("."): + module = importlib.import_module(module_path, package="litellm") + else: + module = importlib.import_module(module_path) + + # Get the actual attribute from the module + value = getattr(module, attr_name) + + # Cache it so we don't have to import again next time + _globals[name] = value + + # Return it + return value + # ============================================================================ # SPECIAL HANDLERS # ============================================================================ diff --git a/litellm/_lazy_imports_registry.py b/litellm/_lazy_imports_registry.py index 93fa8b39af2..9d443e42246 100644 --- a/litellm/_lazy_imports_registry.py +++ b/litellm/_lazy_imports_registry.py @@ -294,6 +294,80 @@ LLM_PROVIDER_LOGIC_NAMES = ( "remove_index_from_tool_calls", ) +# Utils module names that support lazy loading via _lazy_import_utils_module +# These are attributes accessed from litellm.utils module +UTILS_MODULE_NAMES = ( + "encoding", + "BaseVectorStore", + "CredentialAccessor", + "exception_type", + "get_error_message", + "_get_response_headers", + "get_llm_provider", + "_is_non_openai_azure_model", + "get_supported_openai_params", + "LiteLLMResponseObjectHandler", + "_handle_invalid_parallel_tool_calls", + "convert_to_model_response_object", + "convert_to_streaming_response", + "convert_to_streaming_response_async", + "get_api_base", + "ResponseMetadata", + "_parse_content_for_reasoning", + "LiteLLMLoggingObject", + "redact_message_input_output_from_logging", + "CustomStreamWrapper", + "BaseGoogleGenAIGenerateContentConfig", + "BaseOCRConfig", + "BaseSearchConfig", + "BaseTextToSpeechConfig", + "BedrockModelInfo", + "CohereModelInfo", + "MistralOCRConfig", + "Rules", + "AsyncHTTPHandler", + "HTTPHandler", + "get_num_retries_from_retry_policy", + "reset_retry_policy", + "get_secret", + "get_coroutine_checker", + "get_litellm_logging_class", + "get_set_callbacks", + "get_litellm_metadata_from_kwargs", + "map_finish_reason", + "process_response_headers", + "delete_nested_value", + "is_nested_path", + "_get_base_model_from_litellm_call_metadata", + "get_litellm_params", + "_ensure_extra_body_is_safe", + "get_formatted_prompt", + "get_response_headers", + "update_response_metadata", + "executor", + "BaseAnthropicMessagesConfig", + "BaseAudioTranscriptionConfig", + "BaseBatchesConfig", + "BaseContainerConfig", + "BaseEmbeddingConfig", + "BaseImageEditConfig", + "BaseImageGenerationConfig", + "BaseImageVariationConfig", + "BasePassthroughConfig", + "BaseRealtimeConfig", + "BaseRerankConfig", + "BaseVectorStoreConfig", + "BaseVectorStoreFilesConfig", + "BaseVideoConfig", + "ANTHROPIC_API_ONLY_HEADERS", + "AnthropicThinkingParam", + "RerankResponse", + "ChatCompletionDeltaToolCallChunk", + "ChatCompletionToolCallChunk", + "ChatCompletionToolCallFunctionChunk", + "LiteLLM_Params", +) + # Import maps for registry pattern - reduces repetition _UTILS_IMPORT_MAP = { "exception_type": (".utils", "exception_type"), @@ -586,6 +660,79 @@ _LLM_CONFIGS_IMPORT_MAP = { "AmazonNovaChatConfig": (".llms.amazon_nova.chat.transformation", "AmazonNovaChatConfig"), } +# Import map for utils module lazy imports +_UTILS_MODULE_IMPORT_MAP = { + "encoding": ("litellm.main", "encoding"), + "BaseVectorStore": ("litellm.integrations.vector_store_integrations.base_vector_store", "BaseVectorStore"), + "CredentialAccessor": ("litellm.litellm_core_utils.credential_accessor", "CredentialAccessor"), + "exception_type": ("litellm.litellm_core_utils.exception_mapping_utils", "exception_type"), + "get_error_message": ("litellm.litellm_core_utils.exception_mapping_utils", "get_error_message"), + "_get_response_headers": ("litellm.litellm_core_utils.exception_mapping_utils", "_get_response_headers"), + "get_llm_provider": ("litellm.litellm_core_utils.get_llm_provider_logic", "get_llm_provider"), + "_is_non_openai_azure_model": ("litellm.litellm_core_utils.get_llm_provider_logic", "_is_non_openai_azure_model"), + "get_supported_openai_params": ("litellm.litellm_core_utils.get_supported_openai_params", "get_supported_openai_params"), + "LiteLLMResponseObjectHandler": ("litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response", "LiteLLMResponseObjectHandler"), + "_handle_invalid_parallel_tool_calls": ("litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response", "_handle_invalid_parallel_tool_calls"), + "convert_to_model_response_object": ("litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response", "convert_to_model_response_object"), + "convert_to_streaming_response": ("litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response", "convert_to_streaming_response"), + "convert_to_streaming_response_async": ("litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response", "convert_to_streaming_response_async"), + "get_api_base": ("litellm.litellm_core_utils.llm_response_utils.get_api_base", "get_api_base"), + "ResponseMetadata": ("litellm.litellm_core_utils.llm_response_utils.response_metadata", "ResponseMetadata"), + "_parse_content_for_reasoning": ("litellm.litellm_core_utils.prompt_templates.common_utils", "_parse_content_for_reasoning"), + "LiteLLMLoggingObject": ("litellm.litellm_core_utils.redact_messages", "LiteLLMLoggingObject"), + "redact_message_input_output_from_logging": ("litellm.litellm_core_utils.redact_messages", "redact_message_input_output_from_logging"), + "CustomStreamWrapper": ("litellm.litellm_core_utils.streaming_handler", "CustomStreamWrapper"), + "BaseGoogleGenAIGenerateContentConfig": ("litellm.llms.base_llm.google_genai.transformation", "BaseGoogleGenAIGenerateContentConfig"), + "BaseOCRConfig": ("litellm.llms.base_llm.ocr.transformation", "BaseOCRConfig"), + "BaseSearchConfig": ("litellm.llms.base_llm.search.transformation", "BaseSearchConfig"), + "BaseTextToSpeechConfig": ("litellm.llms.base_llm.text_to_speech.transformation", "BaseTextToSpeechConfig"), + "BedrockModelInfo": ("litellm.llms.bedrock.common_utils", "BedrockModelInfo"), + "CohereModelInfo": ("litellm.llms.cohere.common_utils", "CohereModelInfo"), + "MistralOCRConfig": ("litellm.llms.mistral.ocr.transformation", "MistralOCRConfig"), + "Rules": ("litellm.litellm_core_utils.rules", "Rules"), + "AsyncHTTPHandler": ("litellm.llms.custom_httpx.http_handler", "AsyncHTTPHandler"), + "HTTPHandler": ("litellm.llms.custom_httpx.http_handler", "HTTPHandler"), + "get_num_retries_from_retry_policy": ("litellm.router_utils.get_retry_from_policy", "get_num_retries_from_retry_policy"), + "reset_retry_policy": ("litellm.router_utils.get_retry_from_policy", "reset_retry_policy"), + "get_secret": ("litellm.secret_managers.main", "get_secret"), + "get_coroutine_checker": ("litellm.litellm_core_utils.cached_imports", "get_coroutine_checker"), + "get_litellm_logging_class": ("litellm.litellm_core_utils.cached_imports", "get_litellm_logging_class"), + "get_set_callbacks": ("litellm.litellm_core_utils.cached_imports", "get_set_callbacks"), + "get_litellm_metadata_from_kwargs": ("litellm.litellm_core_utils.core_helpers", "get_litellm_metadata_from_kwargs"), + "map_finish_reason": ("litellm.litellm_core_utils.core_helpers", "map_finish_reason"), + "process_response_headers": ("litellm.litellm_core_utils.core_helpers", "process_response_headers"), + "delete_nested_value": ("litellm.litellm_core_utils.dot_notation_indexing", "delete_nested_value"), + "is_nested_path": ("litellm.litellm_core_utils.dot_notation_indexing", "is_nested_path"), + "_get_base_model_from_litellm_call_metadata": ("litellm.litellm_core_utils.get_litellm_params", "_get_base_model_from_litellm_call_metadata"), + "get_litellm_params": ("litellm.litellm_core_utils.get_litellm_params", "get_litellm_params"), + "_ensure_extra_body_is_safe": ("litellm.litellm_core_utils.llm_request_utils", "_ensure_extra_body_is_safe"), + "get_formatted_prompt": ("litellm.litellm_core_utils.llm_response_utils.get_formatted_prompt", "get_formatted_prompt"), + "get_response_headers": ("litellm.litellm_core_utils.llm_response_utils.get_headers", "get_response_headers"), + "update_response_metadata": ("litellm.litellm_core_utils.llm_response_utils.response_metadata", "update_response_metadata"), + "executor": ("litellm.litellm_core_utils.thread_pool_executor", "executor"), + "BaseAnthropicMessagesConfig": ("litellm.llms.base_llm.anthropic_messages.transformation", "BaseAnthropicMessagesConfig"), + "BaseAudioTranscriptionConfig": ("litellm.llms.base_llm.audio_transcription.transformation", "BaseAudioTranscriptionConfig"), + "BaseBatchesConfig": ("litellm.llms.base_llm.batches.transformation", "BaseBatchesConfig"), + "BaseContainerConfig": ("litellm.llms.base_llm.containers.transformation", "BaseContainerConfig"), + "BaseEmbeddingConfig": ("litellm.llms.base_llm.embedding.transformation", "BaseEmbeddingConfig"), + "BaseImageEditConfig": ("litellm.llms.base_llm.image_edit.transformation", "BaseImageEditConfig"), + "BaseImageGenerationConfig": ("litellm.llms.base_llm.image_generation.transformation", "BaseImageGenerationConfig"), + "BaseImageVariationConfig": ("litellm.llms.base_llm.image_variations.transformation", "BaseImageVariationConfig"), + "BasePassthroughConfig": ("litellm.llms.base_llm.passthrough.transformation", "BasePassthroughConfig"), + "BaseRealtimeConfig": ("litellm.llms.base_llm.realtime.transformation", "BaseRealtimeConfig"), + "BaseRerankConfig": ("litellm.llms.base_llm.rerank.transformation", "BaseRerankConfig"), + "BaseVectorStoreConfig": ("litellm.llms.base_llm.vector_store.transformation", "BaseVectorStoreConfig"), + "BaseVectorStoreFilesConfig": ("litellm.llms.base_llm.vector_store_files.transformation", "BaseVectorStoreFilesConfig"), + "BaseVideoConfig": ("litellm.llms.base_llm.videos.transformation", "BaseVideoConfig"), + "ANTHROPIC_API_ONLY_HEADERS": ("litellm.types.llms.anthropic", "ANTHROPIC_API_ONLY_HEADERS"), + "AnthropicThinkingParam": ("litellm.types.llms.anthropic", "AnthropicThinkingParam"), + "RerankResponse": ("litellm.types.rerank", "RerankResponse"), + "ChatCompletionDeltaToolCallChunk": ("litellm.types.llms.openai", "ChatCompletionDeltaToolCallChunk"), + "ChatCompletionToolCallChunk": ("litellm.types.llms.openai", "ChatCompletionToolCallChunk"), + "ChatCompletionToolCallFunctionChunk": ("litellm.types.llms.openai", "ChatCompletionToolCallFunctionChunk"), + "LiteLLM_Params": ("litellm.types.router", "LiteLLM_Params"), +} + # Export all name tuples and import maps for use in _lazy_imports.py __all__ = [ # Name tuples @@ -602,6 +749,7 @@ __all__ = [ "LLM_CONFIG_NAMES", "TYPES_NAMES", "LLM_PROVIDER_LOGIC_NAMES", + "UTILS_MODULE_NAMES", # Import maps "_UTILS_IMPORT_MAP", "_COST_CALCULATOR_IMPORT_MAP", @@ -614,5 +762,6 @@ __all__ = [ "_TYPES_IMPORT_MAP", "_LLM_CONFIGS_IMPORT_MAP", "_LLM_PROVIDER_LOGIC_IMPORT_MAP", + "_UTILS_MODULE_IMPORT_MAP", ] diff --git a/litellm/utils.py b/litellm/utils.py index 65d793fa12f..7bd98decc34 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -615,15 +615,6 @@ def get_applied_guardrails(kwargs: Dict[str, Any]) -> List[str]: return applied_guardrails -def _get_utils_globals() -> dict: - """ - Get the globals dictionary of the utils module. - - This is where we cache imported attributes so we don't import them twice. - """ - return sys.modules[__name__].__dict__ - - def load_credentials_from_list(kwargs: dict): """ Updates kwargs with the credentials if credential_name in kwarg @@ -8749,672 +8740,16 @@ def should_run_mock_completion( return False -def __getattr__(name: str) -> Any: # noqa: PLR0915 - """Lazy import handler for utils module""" - _globals = _get_utils_globals() +def __getattr__(name: str) -> Any: + """Lazy import handler for utils module with cached registry for improved performance.""" + # Use cached registry from _lazy_imports instead of importing tuples every time + from litellm._lazy_imports import _get_lazy_import_registry - # Lazy load encoding from main.py to avoid heavy tiktoken import - if name == "encoding": - # Check if already cached - if "encoding" not in _globals: - from litellm.main import encoding as _encoding - _globals["encoding"] = _encoding - return _globals["encoding"] + registry = _get_lazy_import_registry() - # Lazy load BaseVectorStore to avoid loading it at module import time - if name == "BaseVectorStore": - # Check if already cached - if "BaseVectorStore" not in _globals: - from litellm.integrations.vector_store_integrations.base_vector_store import ( - BaseVectorStore as _BaseVectorStore, - ) - _globals["BaseVectorStore"] = _BaseVectorStore - return _globals["BaseVectorStore"] - - # Lazy load CredentialAccessor to avoid loading it at module import time - if name == "CredentialAccessor": - # Check if already cached - if "CredentialAccessor" not in _globals: - from litellm.litellm_core_utils.credential_accessor import ( - CredentialAccessor as _CredentialAccessor, - ) - _globals["CredentialAccessor"] = _CredentialAccessor - return _globals["CredentialAccessor"] - - # Lazy load exception_mapping_utils functions to avoid loading at module import time - if name == "exception_type": - # Check if already cached - if "exception_type" not in _globals: - from litellm.litellm_core_utils.exception_mapping_utils import ( - exception_type as _exception_type, - ) - _globals["exception_type"] = _exception_type - return _globals["exception_type"] - - if name == "get_error_message": - # Check if already cached - if "get_error_message" not in _globals: - from litellm.litellm_core_utils.exception_mapping_utils import ( - get_error_message as _get_error_message, - ) - _globals["get_error_message"] = _get_error_message - return _globals["get_error_message"] - - if name == "_get_response_headers": - # Check if already cached - if "_get_response_headers" not in _globals: - from litellm.litellm_core_utils.exception_mapping_utils import ( - _get_response_headers as __get_response_headers, - ) - _globals["_get_response_headers"] = __get_response_headers - return _globals["_get_response_headers"] - - # Lazy load get_llm_provider_logic functions to avoid loading at module import time - if name == "get_llm_provider": - # Check if already cached - if "get_llm_provider" not in _globals: - from litellm.litellm_core_utils.get_llm_provider_logic import ( - get_llm_provider as _get_llm_provider, - ) - _globals["get_llm_provider"] = _get_llm_provider - return _globals["get_llm_provider"] - - if name == "_is_non_openai_azure_model": - # Check if already cached - if "_is_non_openai_azure_model" not in _globals: - from litellm.litellm_core_utils.get_llm_provider_logic import ( - _is_non_openai_azure_model as __is_non_openai_azure_model, - ) - _globals["_is_non_openai_azure_model"] = __is_non_openai_azure_model - return _globals["_is_non_openai_azure_model"] - - # Lazy load get_supported_openai_params to avoid loading at module import time - if name == "get_supported_openai_params": - # Check if already cached - if "get_supported_openai_params" not in _globals: - from litellm.litellm_core_utils.get_supported_openai_params import ( - get_supported_openai_params as _get_supported_openai_params, - ) - _globals["get_supported_openai_params"] = _get_supported_openai_params - return _globals["get_supported_openai_params"] - - # Lazy load convert_dict_to_response functions to avoid loading at module import time - if name == "LiteLLMResponseObjectHandler": - # Check if already cached - if "LiteLLMResponseObjectHandler" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( - LiteLLMResponseObjectHandler as _LiteLLMResponseObjectHandler, - ) - _globals["LiteLLMResponseObjectHandler"] = _LiteLLMResponseObjectHandler - return _globals["LiteLLMResponseObjectHandler"] - - if name == "_handle_invalid_parallel_tool_calls": - # Check if already cached - if "_handle_invalid_parallel_tool_calls" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( - _handle_invalid_parallel_tool_calls as __handle_invalid_parallel_tool_calls, - ) - _globals["_handle_invalid_parallel_tool_calls"] = __handle_invalid_parallel_tool_calls - return _globals["_handle_invalid_parallel_tool_calls"] - - if name == "convert_to_model_response_object": - # Check if already cached - if "convert_to_model_response_object" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( - convert_to_model_response_object as _convert_to_model_response_object, - ) - _globals["convert_to_model_response_object"] = _convert_to_model_response_object - return _globals["convert_to_model_response_object"] - - if name == "convert_to_streaming_response": - # Check if already cached - if "convert_to_streaming_response" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( - convert_to_streaming_response as _convert_to_streaming_response, - ) - _globals["convert_to_streaming_response"] = _convert_to_streaming_response - return _globals["convert_to_streaming_response"] - - if name == "convert_to_streaming_response_async": - # Check if already cached - if "convert_to_streaming_response_async" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( - convert_to_streaming_response_async as _convert_to_streaming_response_async, - ) - _globals["convert_to_streaming_response_async"] = _convert_to_streaming_response_async - return _globals["convert_to_streaming_response_async"] - - # Lazy load get_api_base to avoid loading at module import time - if name == "get_api_base": - # Check if already cached - if "get_api_base" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.get_api_base import ( - get_api_base as _get_api_base, - ) - _globals["get_api_base"] = _get_api_base - return _globals["get_api_base"] - - # Lazy load ResponseMetadata to avoid loading at module import time - if name == "ResponseMetadata": - # Check if already cached - if "ResponseMetadata" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.response_metadata import ( - ResponseMetadata as _ResponseMetadata, - ) - _globals["ResponseMetadata"] = _ResponseMetadata - return _globals["ResponseMetadata"] - - # Lazy load _parse_content_for_reasoning to avoid loading at module import time - if name == "_parse_content_for_reasoning": - # Check if already cached - if "_parse_content_for_reasoning" not in _globals: - from litellm.litellm_core_utils.prompt_templates.common_utils import ( - _parse_content_for_reasoning as __parse_content_for_reasoning, - ) - _globals["_parse_content_for_reasoning"] = __parse_content_for_reasoning - return _globals["_parse_content_for_reasoning"] - - # Lazy load redact_messages to avoid loading at module import time - if name == "LiteLLMLoggingObject": - # Check if already cached - if "LiteLLMLoggingObject" not in _globals: - from litellm.litellm_core_utils.redact_messages import ( - LiteLLMLoggingObject as _LiteLLMLoggingObject, - ) - _globals["LiteLLMLoggingObject"] = _LiteLLMLoggingObject - return _globals["LiteLLMLoggingObject"] - - if name == "redact_message_input_output_from_logging": - # Check if already cached - if "redact_message_input_output_from_logging" not in _globals: - from litellm.litellm_core_utils.redact_messages import ( - redact_message_input_output_from_logging as _redact_message_input_output_from_logging, - ) - _globals["redact_message_input_output_from_logging"] = _redact_message_input_output_from_logging - return _globals["redact_message_input_output_from_logging"] - - # Lazy load CustomStreamWrapper to avoid loading at module import time - if name == "CustomStreamWrapper": - # Check if already cached - if "CustomStreamWrapper" not in _globals: - from litellm.litellm_core_utils.streaming_handler import ( - CustomStreamWrapper as _CustomStreamWrapper, - ) - _globals["CustomStreamWrapper"] = _CustomStreamWrapper - return _globals["CustomStreamWrapper"] - - # Lazy load BaseGoogleGenAIGenerateContentConfig to avoid loading at module import time - if name == "BaseGoogleGenAIGenerateContentConfig": - # Check if already cached - if "BaseGoogleGenAIGenerateContentConfig" not in _globals: - from litellm.llms.base_llm.google_genai.transformation import ( - BaseGoogleGenAIGenerateContentConfig as _BaseGoogleGenAIGenerateContentConfig, - ) - _globals["BaseGoogleGenAIGenerateContentConfig"] = _BaseGoogleGenAIGenerateContentConfig - return _globals["BaseGoogleGenAIGenerateContentConfig"] - - # Lazy load BaseOCRConfig to avoid loading at module import time - if name == "BaseOCRConfig": - # Check if already cached - if "BaseOCRConfig" not in _globals: - from litellm.llms.base_llm.ocr.transformation import ( - BaseOCRConfig as _BaseOCRConfig, - ) - _globals["BaseOCRConfig"] = _BaseOCRConfig - return _globals["BaseOCRConfig"] - - # Lazy load BaseSearchConfig to avoid loading at module import time - if name == "BaseSearchConfig": - # Check if already cached - if "BaseSearchConfig" not in _globals: - from litellm.llms.base_llm.search.transformation import ( - BaseSearchConfig as _BaseSearchConfig, - ) - _globals["BaseSearchConfig"] = _BaseSearchConfig - return _globals["BaseSearchConfig"] - - # Lazy load BaseTextToSpeechConfig to avoid loading at module import time - if name == "BaseTextToSpeechConfig": - # Check if already cached - if "BaseTextToSpeechConfig" not in _globals: - from litellm.llms.base_llm.text_to_speech.transformation import ( - BaseTextToSpeechConfig as _BaseTextToSpeechConfig, - ) - _globals["BaseTextToSpeechConfig"] = _BaseTextToSpeechConfig - return _globals["BaseTextToSpeechConfig"] - - # Lazy load BedrockModelInfo to avoid loading at module import time - if name == "BedrockModelInfo": - # Check if already cached - if "BedrockModelInfo" not in _globals: - from litellm.llms.bedrock.common_utils import ( - BedrockModelInfo as _BedrockModelInfo, - ) - _globals["BedrockModelInfo"] = _BedrockModelInfo - return _globals["BedrockModelInfo"] - - # Lazy load CohereModelInfo to avoid loading at module import time - if name == "CohereModelInfo": - # Check if already cached - if "CohereModelInfo" not in _globals: - from litellm.llms.cohere.common_utils import ( - CohereModelInfo as _CohereModelInfo, - ) - _globals["CohereModelInfo"] = _CohereModelInfo - return _globals["CohereModelInfo"] - - # Lazy load MistralOCRConfig to avoid loading at module import time - if name == "MistralOCRConfig": - # Check if already cached - if "MistralOCRConfig" not in _globals: - from litellm.llms.mistral.ocr.transformation import ( - MistralOCRConfig as _MistralOCRConfig, - ) - _globals["MistralOCRConfig"] = _MistralOCRConfig - return _globals["MistralOCRConfig"] - - # Lazy load Rules to avoid loading at module import time - if name == "Rules": - # Check if already cached - if "Rules" not in _globals: - from litellm.litellm_core_utils.rules import Rules as _Rules - _globals["Rules"] = _Rules - return _globals["Rules"] - - # Lazy load AsyncHTTPHandler and HTTPHandler to avoid loading at module import time - if name == "AsyncHTTPHandler": - # Check if already cached - if "AsyncHTTPHandler" not in _globals: - from litellm.llms.custom_httpx.http_handler import ( - AsyncHTTPHandler as _AsyncHTTPHandler, - ) - _globals["AsyncHTTPHandler"] = _AsyncHTTPHandler - return _globals["AsyncHTTPHandler"] - - if name == "HTTPHandler": - # Check if already cached - if "HTTPHandler" not in _globals: - from litellm.llms.custom_httpx.http_handler import ( - HTTPHandler as _HTTPHandler, - ) - _globals["HTTPHandler"] = _HTTPHandler - return _globals["HTTPHandler"] - - # Lazy load get_num_retries_from_retry_policy and reset_retry_policy to avoid loading at module import time - if name == "get_num_retries_from_retry_policy": - # Check if already cached - if "get_num_retries_from_retry_policy" not in _globals: - from litellm.router_utils.get_retry_from_policy import ( - get_num_retries_from_retry_policy as _get_num_retries_from_retry_policy, - ) - _globals["get_num_retries_from_retry_policy"] = _get_num_retries_from_retry_policy - return _globals["get_num_retries_from_retry_policy"] - - if name == "reset_retry_policy": - # Check if already cached - if "reset_retry_policy" not in _globals: - from litellm.router_utils.get_retry_from_policy import ( - reset_retry_policy as _reset_retry_policy, - ) - _globals["reset_retry_policy"] = _reset_retry_policy - return _globals["reset_retry_policy"] - - # Lazy load get_secret to avoid loading at module import time - if name == "get_secret": - # Check if already cached - if "get_secret" not in _globals: - from litellm.secret_managers.main import get_secret as _get_secret - _globals["get_secret"] = _get_secret - return _globals["get_secret"] - - # Lazy load cached_imports functions to avoid loading at module import time - if name == "get_coroutine_checker": - # Check if already cached - if "get_coroutine_checker" not in _globals: - from litellm.litellm_core_utils.cached_imports import ( - get_coroutine_checker as _get_coroutine_checker, - ) - _globals["get_coroutine_checker"] = _get_coroutine_checker - return _globals["get_coroutine_checker"] - - if name == "get_litellm_logging_class": - # Check if already cached - if "get_litellm_logging_class" not in _globals: - from litellm.litellm_core_utils.cached_imports import ( - get_litellm_logging_class as _get_litellm_logging_class, - ) - _globals["get_litellm_logging_class"] = _get_litellm_logging_class - return _globals["get_litellm_logging_class"] - - if name == "get_set_callbacks": - # Check if already cached - if "get_set_callbacks" not in _globals: - from litellm.litellm_core_utils.cached_imports import ( - get_set_callbacks as _get_set_callbacks, - ) - _globals["get_set_callbacks"] = _get_set_callbacks - return _globals["get_set_callbacks"] - - # Lazy load core_helpers functions to avoid loading at module import time - if name == "get_litellm_metadata_from_kwargs": - # Check if already cached - if "get_litellm_metadata_from_kwargs" not in _globals: - from litellm.litellm_core_utils.core_helpers import ( - get_litellm_metadata_from_kwargs as _get_litellm_metadata_from_kwargs, - ) - _globals["get_litellm_metadata_from_kwargs"] = _get_litellm_metadata_from_kwargs - return _globals["get_litellm_metadata_from_kwargs"] - - if name == "map_finish_reason": - # Check if already cached - if "map_finish_reason" not in _globals: - from litellm.litellm_core_utils.core_helpers import ( - map_finish_reason as _map_finish_reason, - ) - _globals["map_finish_reason"] = _map_finish_reason - return _globals["map_finish_reason"] - - if name == "process_response_headers": - # Check if already cached - if "process_response_headers" not in _globals: - from litellm.litellm_core_utils.core_helpers import ( - process_response_headers as _process_response_headers, - ) - _globals["process_response_headers"] = _process_response_headers - return _globals["process_response_headers"] - - # Lazy load dot_notation_indexing functions to avoid loading at module import time - if name == "delete_nested_value": - # Check if already cached - if "delete_nested_value" not in _globals: - from litellm.litellm_core_utils.dot_notation_indexing import ( - delete_nested_value as _delete_nested_value, - ) - _globals["delete_nested_value"] = _delete_nested_value - return _globals["delete_nested_value"] - - if name == "is_nested_path": - # Check if already cached - if "is_nested_path" not in _globals: - from litellm.litellm_core_utils.dot_notation_indexing import ( - is_nested_path as _is_nested_path, - ) - _globals["is_nested_path"] = _is_nested_path - return _globals["is_nested_path"] - - # Lazy load get_litellm_params functions to avoid loading at module import time - if name == "_get_base_model_from_litellm_call_metadata": - # Check if already cached - if "_get_base_model_from_litellm_call_metadata" not in _globals: - from litellm.litellm_core_utils.get_litellm_params import ( - _get_base_model_from_litellm_call_metadata as __get_base_model_from_litellm_call_metadata, - ) - _globals["_get_base_model_from_litellm_call_metadata"] = __get_base_model_from_litellm_call_metadata - return _globals["_get_base_model_from_litellm_call_metadata"] - - if name == "get_litellm_params": - # Check if already cached - if "get_litellm_params" not in _globals: - from litellm.litellm_core_utils.get_litellm_params import ( - get_litellm_params as _get_litellm_params, - ) - _globals["get_litellm_params"] = _get_litellm_params - return _globals["get_litellm_params"] - - # Lazy load _ensure_extra_body_is_safe to avoid loading at module import time - if name == "_ensure_extra_body_is_safe": - # Check if already cached - if "_ensure_extra_body_is_safe" not in _globals: - from litellm.litellm_core_utils.llm_request_utils import ( - _ensure_extra_body_is_safe as __ensure_extra_body_is_safe, - ) - _globals["_ensure_extra_body_is_safe"] = __ensure_extra_body_is_safe - return _globals["_ensure_extra_body_is_safe"] - - # Lazy load get_formatted_prompt to avoid loading at module import time - if name == "get_formatted_prompt": - # Check if already cached - if "get_formatted_prompt" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.get_formatted_prompt import ( - get_formatted_prompt as _get_formatted_prompt, - ) - _globals["get_formatted_prompt"] = _get_formatted_prompt - return _globals["get_formatted_prompt"] - - # Lazy load get_response_headers to avoid loading at module import time - if name == "get_response_headers": - # Check if already cached - if "get_response_headers" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.get_headers import ( - get_response_headers as _get_response_headers, - ) - _globals["get_response_headers"] = _get_response_headers - return _globals["get_response_headers"] - - # Lazy load update_response_metadata to avoid loading at module import time - if name == "update_response_metadata": - # Check if already cached - if "update_response_metadata" not in _globals: - from litellm.litellm_core_utils.llm_response_utils.response_metadata import ( - update_response_metadata as _update_response_metadata, - ) - _globals["update_response_metadata"] = _update_response_metadata - return _globals["update_response_metadata"] - - # Lazy load executor to avoid loading at module import time - if name == "executor": - # Check if already cached - if "executor" not in _globals: - from litellm.litellm_core_utils.thread_pool_executor import ( - executor as _executor, - ) - _globals["executor"] = _executor - return _globals["executor"] - - # Lazy load BaseAnthropicMessagesConfig to avoid loading at module import time - if name == "BaseAnthropicMessagesConfig": - # Check if already cached - if "BaseAnthropicMessagesConfig" not in _globals: - from litellm.llms.base_llm.anthropic_messages.transformation import ( - BaseAnthropicMessagesConfig as _BaseAnthropicMessagesConfig, - ) - _globals["BaseAnthropicMessagesConfig"] = _BaseAnthropicMessagesConfig - return _globals["BaseAnthropicMessagesConfig"] - - # Lazy load BaseAudioTranscriptionConfig to avoid loading at module import time - if name == "BaseAudioTranscriptionConfig": - # Check if already cached - if "BaseAudioTranscriptionConfig" not in _globals: - from litellm.llms.base_llm.audio_transcription.transformation import ( - BaseAudioTranscriptionConfig as _BaseAudioTranscriptionConfig, - ) - _globals["BaseAudioTranscriptionConfig"] = _BaseAudioTranscriptionConfig - return _globals["BaseAudioTranscriptionConfig"] - - # Lazy load BaseBatchesConfig to avoid loading at module import time - if name == "BaseBatchesConfig": - # Check if already cached - if "BaseBatchesConfig" not in _globals: - from litellm.llms.base_llm.batches.transformation import ( - BaseBatchesConfig as _BaseBatchesConfig, - ) - _globals["BaseBatchesConfig"] = _BaseBatchesConfig - return _globals["BaseBatchesConfig"] - - # Lazy load BaseContainerConfig to avoid loading at module import time - if name == "BaseContainerConfig": - # Check if already cached - if "BaseContainerConfig" not in _globals: - from litellm.llms.base_llm.containers.transformation import ( - BaseContainerConfig as _BaseContainerConfig, - ) - _globals["BaseContainerConfig"] = _BaseContainerConfig - return _globals["BaseContainerConfig"] - - # Lazy load BaseEmbeddingConfig to avoid loading at module import time - if name == "BaseEmbeddingConfig": - # Check if already cached - if "BaseEmbeddingConfig" not in _globals: - from litellm.llms.base_llm.embedding.transformation import ( - BaseEmbeddingConfig as _BaseEmbeddingConfig, - ) - _globals["BaseEmbeddingConfig"] = _BaseEmbeddingConfig - return _globals["BaseEmbeddingConfig"] - - # Lazy load BaseImageEditConfig to avoid loading at module import time - if name == "BaseImageEditConfig": - # Check if already cached - if "BaseImageEditConfig" not in _globals: - from litellm.llms.base_llm.image_edit.transformation import ( - BaseImageEditConfig as _BaseImageEditConfig, - ) - _globals["BaseImageEditConfig"] = _BaseImageEditConfig - return _globals["BaseImageEditConfig"] - - # Lazy load BaseImageGenerationConfig to avoid loading at module import time - if name == "BaseImageGenerationConfig": - # Check if already cached - if "BaseImageGenerationConfig" not in _globals: - from litellm.llms.base_llm.image_generation.transformation import ( - BaseImageGenerationConfig as _BaseImageGenerationConfig, - ) - _globals["BaseImageGenerationConfig"] = _BaseImageGenerationConfig - return _globals["BaseImageGenerationConfig"] - - # Lazy load BaseImageVariationConfig to avoid loading at module import time - if name == "BaseImageVariationConfig": - # Check if already cached - if "BaseImageVariationConfig" not in _globals: - from litellm.llms.base_llm.image_variations.transformation import ( - BaseImageVariationConfig as _BaseImageVariationConfig, - ) - _globals["BaseImageVariationConfig"] = _BaseImageVariationConfig - return _globals["BaseImageVariationConfig"] - - # Lazy load BasePassthroughConfig to avoid loading at module import time - if name == "BasePassthroughConfig": - # Check if already cached - if "BasePassthroughConfig" not in _globals: - from litellm.llms.base_llm.passthrough.transformation import ( - BasePassthroughConfig as _BasePassthroughConfig, - ) - _globals["BasePassthroughConfig"] = _BasePassthroughConfig - return _globals["BasePassthroughConfig"] - - # Lazy load BaseRealtimeConfig to avoid loading at module import time - if name == "BaseRealtimeConfig": - # Check if already cached - if "BaseRealtimeConfig" not in _globals: - from litellm.llms.base_llm.realtime.transformation import ( - BaseRealtimeConfig as _BaseRealtimeConfig, - ) - _globals["BaseRealtimeConfig"] = _BaseRealtimeConfig - return _globals["BaseRealtimeConfig"] - - # Lazy load BaseRerankConfig to avoid loading at module import time - if name == "BaseRerankConfig": - # Check if already cached - if "BaseRerankConfig" not in _globals: - from litellm.llms.base_llm.rerank.transformation import ( - BaseRerankConfig as _BaseRerankConfig, - ) - _globals["BaseRerankConfig"] = _BaseRerankConfig - return _globals["BaseRerankConfig"] - - # Lazy load BaseVectorStoreConfig to avoid loading at module import time - if name == "BaseVectorStoreConfig": - # Check if already cached - if "BaseVectorStoreConfig" not in _globals: - from litellm.llms.base_llm.vector_store.transformation import ( - BaseVectorStoreConfig as _BaseVectorStoreConfig, - ) - _globals["BaseVectorStoreConfig"] = _BaseVectorStoreConfig - return _globals["BaseVectorStoreConfig"] - - # Lazy load BaseVectorStoreFilesConfig to avoid loading at module import time - if name == "BaseVectorStoreFilesConfig": - # Check if already cached - if "BaseVectorStoreFilesConfig" not in _globals: - from litellm.llms.base_llm.vector_store_files.transformation import ( - BaseVectorStoreFilesConfig as _BaseVectorStoreFilesConfig, - ) - _globals["BaseVectorStoreFilesConfig"] = _BaseVectorStoreFilesConfig - return _globals["BaseVectorStoreFilesConfig"] - - # Lazy load BaseVideoConfig to avoid loading at module import time - if name == "BaseVideoConfig": - # Check if already cached - if "BaseVideoConfig" not in _globals: - from litellm.llms.base_llm.videos.transformation import ( - BaseVideoConfig as _BaseVideoConfig, - ) - _globals["BaseVideoConfig"] = _BaseVideoConfig - return _globals["BaseVideoConfig"] - - # Lazy load ANTHROPIC_API_ONLY_HEADERS to avoid loading at module import time - if name == "ANTHROPIC_API_ONLY_HEADERS": - # Check if already cached - if "ANTHROPIC_API_ONLY_HEADERS" not in _globals: - from litellm.types.llms.anthropic import ( - ANTHROPIC_API_ONLY_HEADERS as _ANTHROPIC_API_ONLY_HEADERS, - ) - _globals["ANTHROPIC_API_ONLY_HEADERS"] = _ANTHROPIC_API_ONLY_HEADERS - return _globals["ANTHROPIC_API_ONLY_HEADERS"] - - # Lazy load AnthropicThinkingParam to avoid loading at module import time - if name == "AnthropicThinkingParam": - # Check if already cached - if "AnthropicThinkingParam" not in _globals: - from litellm.types.llms.anthropic import ( - AnthropicThinkingParam as _AnthropicThinkingParam, - ) - _globals["AnthropicThinkingParam"] = _AnthropicThinkingParam - return _globals["AnthropicThinkingParam"] - - # Lazy load RerankResponse to avoid loading at module import time - if name == "RerankResponse": - # Check if already cached - if "RerankResponse" not in _globals: - from litellm.types.rerank import RerankResponse as _RerankResponse - _globals["RerankResponse"] = _RerankResponse - return _globals["RerankResponse"] - - # Lazy load ChatCompletionDeltaToolCallChunk to avoid loading at module import time - if name == "ChatCompletionDeltaToolCallChunk": - # Check if already cached - if "ChatCompletionDeltaToolCallChunk" not in _globals: - from litellm.types.llms.openai import ( - ChatCompletionDeltaToolCallChunk as _ChatCompletionDeltaToolCallChunk, - ) - _globals["ChatCompletionDeltaToolCallChunk"] = _ChatCompletionDeltaToolCallChunk - return _globals["ChatCompletionDeltaToolCallChunk"] - - # Lazy load ChatCompletionToolCallChunk to avoid loading at module import time - if name == "ChatCompletionToolCallChunk": - # Check if already cached - if "ChatCompletionToolCallChunk" not in _globals: - from litellm.types.llms.openai import ( - ChatCompletionToolCallChunk as _ChatCompletionToolCallChunk, - ) - _globals["ChatCompletionToolCallChunk"] = _ChatCompletionToolCallChunk - return _globals["ChatCompletionToolCallChunk"] - - # Lazy load ChatCompletionToolCallFunctionChunk to avoid loading at module import time - if name == "ChatCompletionToolCallFunctionChunk": - # Check if already cached - if "ChatCompletionToolCallFunctionChunk" not in _globals: - from litellm.types.llms.openai import ( - ChatCompletionToolCallFunctionChunk as _ChatCompletionToolCallFunctionChunk, - ) - _globals["ChatCompletionToolCallFunctionChunk"] = _ChatCompletionToolCallFunctionChunk - return _globals["ChatCompletionToolCallFunctionChunk"] - - # Lazy load LiteLLM_Params to avoid loading at module import time - if name == "LiteLLM_Params": - # Check if already cached - if "LiteLLM_Params" not in _globals: - from litellm.types.router import LiteLLM_Params as _LiteLLM_Params - _globals["LiteLLM_Params"] = _LiteLLM_Params - return _globals["LiteLLM_Params"] + # Check if name is in registry and call the cached handler function + if name in registry: + handler_func = registry[name] + return handler_func(name) raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/tests/test_litellm/test_lazy_imports.py b/tests/test_litellm/test_lazy_imports.py index 7de99495b79..660933efac5 100644 --- a/tests/test_litellm/test_lazy_imports.py +++ b/tests/test_litellm/test_lazy_imports.py @@ -35,6 +35,8 @@ from litellm._lazy_imports import ( _lazy_import_types, LLM_PROVIDER_LOGIC_NAMES, _lazy_import_llm_provider_logic, + UTILS_MODULE_NAMES, + _lazy_import_utils_module, ) @@ -45,6 +47,13 @@ def _clear_names_from_globals(names: tuple): del litellm.__dict__[name] +def _clear_names_from_utils_globals(names: tuple): + """Clear all names from litellm.utils globals.""" + for name in names: + if name in litellm.utils.__dict__: + del litellm.utils.__dict__[name] + + def _verify_only_requested_name_imported(name: str, all_names: tuple): """Verify that only the requested name is in globals, not the others.""" for other_name in all_names: @@ -52,6 +61,13 @@ def _verify_only_requested_name_imported(name: str, all_names: tuple): assert other_name not in litellm.__dict__, f"{other_name} should not be imported when importing {name}" +def _verify_only_requested_name_imported_in_utils(name: str, all_names: tuple): + """Verify that only the requested name is in utils globals, not the others.""" + for other_name in all_names: + if other_name != name: + assert other_name not in litellm.utils.__dict__, f"{other_name} should not be imported when importing {name}" + + def test_cost_calculator_lazy_imports(): """Test that all cost calculator functions can be lazy imported.""" # Test each name individually - only that name should be imported @@ -223,6 +239,9 @@ def test_unknown_attribute_raises_error(): with pytest.raises(AttributeError): _lazy_import_llm_provider_logic("unknown") + with pytest.raises(AttributeError): + _lazy_import_utils_module("unknown") + def test_llm_config_lazy_imports(): """Test that LLM config classes can be lazy imported.""" @@ -264,3 +283,15 @@ def test_llm_provider_logic_lazy_imports(): _verify_only_requested_name_imported(name, LLM_PROVIDER_LOGIC_NAMES) + +def test_utils_module_lazy_imports(): + """Test that utils module attributes can be lazy imported.""" + for name in UTILS_MODULE_NAMES: + _clear_names_from_utils_globals(UTILS_MODULE_NAMES) + + obj = _lazy_import_utils_module(name) + assert obj is not None + assert name in litellm.utils.__dict__ + + _verify_only_requested_name_imported_in_utils(name, UTILS_MODULE_NAMES) +