From 40347d487414d3f79b0ebdc334a01d954ca5b207 Mon Sep 17 00:00:00 2001 From: yassin Date: Tue, 1 Sep 2026 15:43:15 +0000 Subject: [PATCH] fix: correct SlackAlerting lazy mapping and keep eager encoding path importable Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/__init__.py | 19 +++++++++---------- litellm/_lazy_imports.py | 5 ++++- litellm/_lazy_imports_registry.py | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 7cae1187de7..d6f363ddc58 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -2151,16 +2151,6 @@ if TYPE_CHECKING: # Track if async client cleanup has been registered (for lazy loading) _async_client_cleanup_registered = False -# Eager loading for backwards compatibility with VCR and other HTTP recording tools -# When LITELLM_DISABLE_LAZY_LOADING is set, lazy-loaded attributes are loaded at import time -# For now, this only affects encoding (tiktoken) as it was the only reported issue -# See: https://github.com/BerriAI/litellm/issues/18659 -# This ensures encoding is initialized before VCR starts recording HTTP requests -if os.getenv("LITELLM_DISABLE_LAZY_LOADING", "").lower() in ("1", "true", "yes", "on"): - # Load encoding at import time (pre-#18070 behavior) - # This ensures encoding is initialized before VCR starts recording - from .main import encoding - def __getattr__(name: str) -> Any: """Lazy import handler with cached registry for improved performance.""" @@ -2377,3 +2367,12 @@ __all__ = list(STAR_IMPORT_PUBLIC_NAMES) # mutable-ok: star imports require __a # ALL_LITELLM_RESPONSE_TYPES is lazy-loaded via __getattr__ to avoid loading utils at import time + +# Eager loading for backwards compatibility with VCR and other HTTP recording tools +# When LITELLM_DISABLE_LAZY_LOADING is set, lazy-loaded attributes are loaded at import time +# For now, this only affects encoding (tiktoken) as it was the only reported issue +# See: https://github.com/BerriAI/litellm/issues/18659 +# This ensures encoding is initialized before VCR starts recording HTTP requests +# This block stays at the bottom so __getattr__ can resolve attributes main.py needs during its import +if os.getenv("LITELLM_DISABLE_LAZY_LOADING", "").lower() in ("1", "true", "yes", "on"): + from .main import encoding diff --git a/litellm/_lazy_imports.py b/litellm/_lazy_imports.py index 4c1c2a45bed..0e11d085b0e 100644 --- a/litellm/_lazy_imports.py +++ b/litellm/_lazy_imports.py @@ -81,7 +81,10 @@ def _get_utils_globals() -> dict[str, object]: 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__ + cached: Final = sys.modules.get("litellm.utils") + if cached is not None: + return cached.__dict__ + return importlib.import_module("litellm.utils").__dict__ def _get_module_level_client_timeout(litellm_globals: Mapping[str, Any]) -> "float | httpx.Timeout | None": diff --git a/litellm/_lazy_imports_registry.py b/litellm/_lazy_imports_registry.py index 522706107cd..a150c7688a3 100644 --- a/litellm/_lazy_imports_registry.py +++ b/litellm/_lazy_imports_registry.py @@ -2030,7 +2030,7 @@ _SDK_SYMBOLS_IMPORT_MAP: Final[Mapping[str, tuple[str, str]]] = MappingProxyType "SerializerFunctionWrapHandler": ("litellm.assistants.main", "SerializerFunctionWrapHandler"), "ServiceUnavailableError": ("litellm.exceptions", "ServiceUnavailableError"), "ShellToolParam": ("litellm.types.llms.openai", "ShellToolParam"), - "SlackAlerting": ("litellm.integrations", "SlackAlerting"), + "SlackAlerting": ("litellm.integrations.SlackAlerting.slack_alerting", "SlackAlerting"), "StandardLoggingRoutingDecision": ("litellm.types.utils", "StandardLoggingRoutingDecision"), "StreamingChoices": ("litellm.types.utils", "StreamingChoices"), "SyncCursorPage": ("litellm.assistants.main", "SyncCursorPage"),