diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 59bab43fbe6..f129af6d444 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5453,9 +5453,11 @@ class ProxyConfig: # Load config separately so a timeout here doesn't block model loading config_data: dict = {} + config_loaded = False search_tools = None try: config_data = await proxy_config.get_config() + config_loaded = True search_tools = self.parse_search_tools(config_data) except Exception as e: verbose_proxy_logger.warning( @@ -5509,7 +5511,8 @@ class ProxyConfig: llm_model_list = llm_router.get_model_list() # check if user set any callbacks in Config Table - self._add_callbacks_from_db_config(config_data) + if config_loaded: + self._add_callbacks_from_db_config(config_data) # router settings await self._add_router_settings_from_db_config( @@ -5576,8 +5579,9 @@ class ProxyConfig: existing_callbacks=litellm.failure_callback, ) - if callbacks is not None and isinstance(callbacks, list): - for callback in callbacks: + if callbacks is None or isinstance(callbacks, list): + configured_callbacks = callbacks or () + for callback in configured_callbacks: if isinstance(callback, str) and install_config_parameterized_callback( callback=callback, litellm_settings=litellm_settings, @@ -5589,7 +5593,7 @@ class ProxyConfig: event_types=["success", "failure"], existing_callbacks=litellm.callbacks, ) - uninstall_deconfigured_parameterized_callbacks(callbacks) + uninstall_deconfigured_parameterized_callbacks(configured_callbacks) def _encrypt_env_variables(self, environment_variables: dict, new_encryption_key: str | None = None) -> dict: """ diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index 4ffedda8323..20647b47071 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -2025,10 +2025,11 @@ def test_ProxyConfig__add_callbacks_from_db_config_uninstalls_logger_when_callba [ {}, {"litellm_settings": {}}, + {"litellm_settings": {"callbacks": []}}, {"litellm_settings": {"success_callback": ["langfuse"]}}, ], ) -def test_ProxyConfig__add_callbacks_from_db_config_keeps_logger_when_config_omits_callbacks( +def test_ProxyConfig__add_callbacks_from_db_config_uninstalls_logger_when_config_omits_callbacks( monkeypatch, config_without_callbacks ): from litellm.integrations.websearch_interception.handler import ( @@ -2039,10 +2040,37 @@ def test_ProxyConfig__add_callbacks_from_db_config_keeps_logger_when_config_omit pc = ProxyConfig() pc._add_callbacks_from_db_config(_websearch_db_config("tavily-search", ["bedrock"])) + installed_before = [cb for cb in litellm.callbacks if isinstance(cb, WebSearchInterceptionLogger)] + pc._add_callbacks_from_db_config(config_without_callbacks) - installed = [cb for cb in litellm.callbacks if isinstance(cb, WebSearchInterceptionLogger)] - assert len(installed) == 1 + remaining = [cb for cb in litellm.callbacks if isinstance(cb, WebSearchInterceptionLogger)] + snapshot = {"installed_before": len(installed_before), "remaining": len(remaining)} + assert snapshot == {"installed_before": 1, "remaining": 0} + + +@pytest.mark.asyncio +async def test_ProxyConfig__update_llm_router_keeps_logger_when_config_load_fails(monkeypatch): + from litellm.integrations.websearch_interception.handler import ( + WebSearchInterceptionLogger, + ) + + _reset_callback_lists(monkeypatch) + pc = ProxyConfig() + pc._add_callbacks_from_db_config(_websearch_db_config("tavily-search", ["bedrock"])) + installed_before = [cb for cb in litellm.callbacks if isinstance(cb, WebSearchInterceptionLogger)] + + class _FailingProxyConfig: + async def get_config(self) -> dict: + raise TimeoutError("transient DB timeout") + + monkeypatch.setattr(litellm.proxy.proxy_server, "proxy_config", _FailingProxyConfig()) + + await pc._update_llm_router(new_models=[], proxy_logging_obj=MagicMock()) + + remaining = [cb for cb in litellm.callbacks if isinstance(cb, WebSearchInterceptionLogger)] + snapshot = {"installed_before": len(installed_before), "remaining": len(remaining)} + assert snapshot == {"installed_before": 1, "remaining": 1} def test_ProxyConfig__add_callbacks_from_db_config_bad_config_raises():