From 4e7e2f53b98f5737e43a27c5137e9ad6567c71ac Mon Sep 17 00:00:00 2001 From: mateo Date: Tue, 11 Aug 2026 21:44:05 +0000 Subject: [PATCH] fix(proxy): schedule the deprecation loop when a config reload enables alerting Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/proxy/utils.py | 22 +++++++++++++------ .../utils/proxy_logging/test_lifecycle.py | 17 ++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 47eea9218f0..e1bc0642182 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -482,13 +482,20 @@ class ProxyLogging: ) # RUN HANGING REQUEST CHECK (if user wants to alert on hanging requests) self.hanging_requests_check_started = True - if ( - self.alerting is not None - and self.slack_alerting_instance is not None - and not self.deprecation_check_started - ): - asyncio.create_task(self.slack_alerting_instance._run_scheduled_deprecation_check()) - self.deprecation_check_started = True + self._ensure_deprecation_check_scheduled() + + def _ensure_deprecation_check_scheduled(self) -> None: + """Alerting can be configured at startup or by a later config reload, so schedule from either path""" + if self.alerting is None or self.slack_alerting_instance is None or self.deprecation_check_started: + return + + try: + asyncio.get_running_loop() + except RuntimeError: + return + + asyncio.create_task(self.slack_alerting_instance._run_scheduled_deprecation_check()) + self.deprecation_check_started = True def update_values( self, @@ -517,6 +524,7 @@ class ProxyLogging: updated_slack_alerting = True if updated_slack_alerting is True: + self._ensure_deprecation_check_scheduled() self.slack_alerting_instance.update_values( alerting=self.alerting, alerting_threshold=self.alerting_threshold, diff --git a/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py b/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py index b2aa16e88d9..e82ad41ecc2 100644 --- a/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py +++ b/tests/test_litellm/proxy/utils/proxy_logging/test_lifecycle.py @@ -144,6 +144,23 @@ async def test_startup_event_schedules_deprecation_check_before_its_alert_type_i proxy_logging.slack_alerting_instance._run_scheduled_deprecation_check.assert_called_once_with() +@pytest.mark.asyncio +async def test_update_values_schedules_deprecation_check_when_alerting_arrives_later(proxy_logging): + """A proxy that boots without alerting still needs the loop once a config reload turns it on""" + proxy_logging.slack_alerting_instance = MagicMock() + proxy_logging.slack_alerting_instance.alert_types = [] + proxy_logging.slack_alerting_instance._run_scheduled_deprecation_check = AsyncMock() + proxy_logging._init_litellm_callbacks = MagicMock() + + proxy_logging.startup_event(llm_router=None, redis_usage_cache=None) + assert proxy_logging.deprecation_check_started is False + + proxy_logging.update_values(alerting=["slack"]) + + assert proxy_logging.deprecation_check_started is True + proxy_logging.slack_alerting_instance._run_scheduled_deprecation_check.assert_called_once_with() + + def test_startup_event_propagates_init_callbacks_failure_raises(proxy_logging): proxy_logging.slack_alerting_instance = MagicMock() proxy_logging.slack_alerting_instance.alert_types = []