From 1198ef88eae3fa777c0627ef8c065c482a2f2c05 Mon Sep 17 00:00:00 2001 From: rajamanohar Date: Wed, 15 Jul 2026 08:49:26 -0700 Subject: [PATCH] fix(budget-reset): address review comments - Fix config section: docstring now says litellm_settings (not general_settings), matching how litellm.timezone is actually populated via the setattr loop in proxy_server.py - Log warning on invalid weekly_budget_reset_day value instead of silently falling back to Monday - Move WEEKDAYS constant to constants.py per project convention - Update invalid-day test to verify warning is emitted Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- litellm/constants.py | 12 +++++++++++ litellm/litellm_core_utils/duration_parser.py | 21 ++++++++++--------- litellm/proxy/common_utils/timezone_utils.py | 4 ++-- .../test_duration_parser.py | 16 ++++++++++---- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/litellm/constants.py b/litellm/constants.py index 715d57e594d..8b81d0abbd8 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1628,3 +1628,15 @@ ADVISOR_TOOL_DESCRIPTION: str = ( "want to verify your reasoning, or face a complex decision. " "Describe your question or challenge clearly in the 'question' field." ) + +# Mapping of lowercase weekday names to Python datetime.weekday() integers. +# Used by budget reset logic to determine which day weekly budgets reset on. +WEEKDAYS: dict[str, int] = { + "monday": 0, + "tuesday": 1, + "wednesday": 2, + "thursday": 3, + "friday": 4, + "saturday": 5, + "sunday": 6, +} diff --git a/litellm/litellm_core_utils/duration_parser.py b/litellm/litellm_core_utils/duration_parser.py index f5450e704ca..69820da8222 100644 --- a/litellm/litellm_core_utils/duration_parser.py +++ b/litellm/litellm_core_utils/duration_parser.py @@ -12,15 +12,8 @@ from datetime import datetime, timedelta, timezone, tzinfo from typing import Optional, Tuple from zoneinfo import ZoneInfo -WEEKDAYS: dict[str, int] = { - "monday": 0, - "tuesday": 1, - "wednesday": 2, - "thursday": 3, - "friday": 4, - "saturday": 5, - "sunday": 6, -} +from litellm._logging import verbose_logger +from litellm.constants import WEEKDAYS def _extract_from_regex(duration: str) -> Tuple[int, str]: @@ -207,7 +200,15 @@ def _handle_day_reset( if value == 1: # Daily reset at midnight return base_midnight + timedelta(days=1) elif value == 7: # Weekly reset on configured day at midnight - target_weekday = WEEKDAYS.get(weekly_reset_day.lower().strip(), 0) + normalized_day = weekly_reset_day.lower().strip() + target_weekday = WEEKDAYS.get(normalized_day) + if target_weekday is None: + verbose_logger.warning( + "Invalid weekly_budget_reset_day '%s'; falling back to 'monday'. Valid values: %s", + weekly_reset_day, + ", ".join(WEEKDAYS.keys()), + ) + target_weekday = 0 # Monday days_until_reset = (target_weekday - current_time.weekday()) % 7 if days_until_reset == 0: # If today is the reset day days_until_reset = 7 diff --git a/litellm/proxy/common_utils/timezone_utils.py b/litellm/proxy/common_utils/timezone_utils.py index fed12665c50..63d0593d5a2 100644 --- a/litellm/proxy/common_utils/timezone_utils.py +++ b/litellm/proxy/common_utils/timezone_utils.py @@ -20,8 +20,8 @@ def get_weekly_budget_reset_day() -> str: Get the configured weekly budget reset day from litellm_settings. Falls back to "monday" if not specified. - Set via general_settings in config.yaml: - general_settings: + Set via litellm_settings in config.yaml: + litellm_settings: weekly_budget_reset_day: "sunday" litellm_settings values are set as attributes on the litellm module diff --git a/tests/test_litellm/litellm_core_utils/test_duration_parser.py b/tests/test_litellm/litellm_core_utils/test_duration_parser.py index 5dc02ac420d..48527d9cdf6 100644 --- a/tests/test_litellm/litellm_core_utils/test_duration_parser.py +++ b/tests/test_litellm/litellm_core_utils/test_duration_parser.py @@ -260,15 +260,23 @@ class TestStandardizedResetTime(unittest.TestCase): self.assertEqual(sunday_result, datetime(2023, 5, 21, 0, 0, 0, tzinfo=timezone.utc)) def test_invalid_weekly_reset_day_falls_back_to_monday(self): - """Test that an invalid weekly_reset_day falls back to Monday.""" + """Test that an invalid weekly_reset_day falls back to Monday and logs a warning.""" # Wednesday May 17, 2023 wednesday = datetime(2023, 5, 17, 15, 45, 0, tzinfo=timezone.utc) # Invalid day - should fall back to Monday (next Monday = May 22) - result = get_next_standardized_reset_time( - "7d", wednesday, "UTC", weekly_reset_day="funday" - ) + import logging + + with self.assertLogs("LiteLLM", level="WARNING") as log_ctx: + result = get_next_standardized_reset_time( + "7d", wednesday, "UTC", weekly_reset_day="funday" + ) self.assertEqual(result, datetime(2023, 5, 22, 0, 0, 0, tzinfo=timezone.utc)) + # Verify a warning was logged about the invalid day + self.assertTrue( + any("funday" in msg for msg in log_ctx.output), + f"Expected warning about invalid day 'funday', got: {log_ctx.output}", + ) def test_weekly_reset_day_case_insensitive(self): """Test that weekly_reset_day is case-insensitive."""