diff --git a/litellm/proxy/hooks/__init__.py b/litellm/proxy/hooks/__init__.py index 8714dd5f3d2..ae856598736 100644 --- a/litellm/proxy/hooks/__init__.py +++ b/litellm/proxy/hooks/__init__.py @@ -11,6 +11,7 @@ from .parallel_request_limiter import _PROXY_MaxParallelRequestsHandler from .parallel_request_limiter_v3 import _PROXY_MaxParallelRequestsHandler_v3 from .responses_id_security import ResponsesIDSecurity from .sensitive_data_routing import _PROXY_SensitiveDataRoutingHandler +from litellm._logging import verbose_proxy_logger # List of all available hooks that can be enabled. # Defined before the enterprise import below so that any module re-imported @@ -47,10 +48,8 @@ def get_proxy_hook( try: from enterprise.enterprise_hooks import ENTERPRISE_PROXY_HOOKS -except ImportError: - ENTERPRISE_PROXY_HOOKS = {} - - -### update PROXY_HOOKS with ENTERPRISE_PROXY_HOOKS ### - -PROXY_HOOKS.update(ENTERPRISE_PROXY_HOOKS) + PROXY_HOOKS.update(ENTERPRISE_PROXY_HOOKS) +except ImportError as e: + verbose_proxy_logger.warning( + f"Could not import enterprise hooks — enterprise features disabled: {e}" + ) diff --git a/tests/test_litellm/proxy/hooks/test_proxy_hooks_init.py b/tests/test_litellm/proxy/hooks/test_proxy_hooks_init.py index a6edd3db944..df4dac7c201 100644 --- a/tests/test_litellm/proxy/hooks/test_proxy_hooks_init.py +++ b/tests/test_litellm/proxy/hooks/test_proxy_hooks_init.py @@ -49,3 +49,38 @@ def test_isolation_module_does_not_pull_in_proxy_utils(): importlib.import_module("litellm.llms.base_llm.managed_resources.isolation") assert "litellm.proxy.utils" not in sys.modules assert "litellm.proxy.management_endpoints.common_utils" not in sys.modules + +def test_enterprise_hooks_import_failure_logs_warning(monkeypatch, caplog): + """ + If the `enterprise` / `litellm_enterprise` package is unavailable + (e.g. missing from a hardened non_root Docker image), hooks/__init__.py + should log a warning and continue, instead of letting the ImportError + propagate and crash the whole proxy at import time. + """ + import builtins + import importlib + import logging + import sys + + real_import = builtins.__import__ + + def fake_import(name, *args, **kwargs): + if name == "enterprise" or name.startswith("enterprise."): + raise ImportError("No module named 'enterprise'") + return real_import(name, *args, **kwargs) + + monkeypatch.setattr(builtins, "__import__", fake_import) + sys.modules.pop("litellm.proxy.hooks", None) + + with caplog.at_level(logging.WARNING): + import litellm.proxy.hooks as hooks_module + importlib.reload(hooks_module) + + assert any( + "enterprise hooks" in record.message.lower() + for record in caplog.records + ), "Expected a warning to be logged when enterprise hooks import fails" + + monkeypatch.undo() + sys.modules.pop("litellm.proxy.hooks", None) + importlib.import_module("litellm.proxy.hooks")