From 38ae83a537792a1135ad26afc47620ca1e24f82b Mon Sep 17 00:00:00 2001 From: apex-mochen <2756823972@qq.com> Date: Mon, 28 Sep 2026 00:31:34 +0800 Subject: [PATCH] fix(budget): honor zero-valued deployment budgets Signed-off-by: apex-mochen <2756823972@qq.com> --- litellm/router_strategy/budget_limiter.py | 2 +- .../router_strategy/test_budget_limiter.py | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/litellm/router_strategy/budget_limiter.py b/litellm/router_strategy/budget_limiter.py index cde39dbd48d..7904e2e2c70 100644 --- a/litellm/router_strategy/budget_limiter.py +++ b/litellm/router_strategy/budget_limiter.py @@ -841,7 +841,7 @@ class RouterBudgetLimiting(CustomLogger): for _model in model_list: _litellm_params = _model.get("litellm_params", {}) - if _litellm_params.get("max_budget") or _litellm_params.get("budget_duration") is not None: + if _litellm_params.get("max_budget") is not None or _litellm_params.get("budget_duration") is not None: return True return False diff --git a/tests/unit/router_strategy/test_budget_limiter.py b/tests/unit/router_strategy/test_budget_limiter.py index 6d2fce28ee2..ad759151799 100644 --- a/tests/unit/router_strategy/test_budget_limiter.py +++ b/tests/unit/router_strategy/test_budget_limiter.py @@ -217,3 +217,45 @@ async def test_provider_budget_boundary_is_unchanged( ) assert (kept != []) is expected_kept + + +def test_zero_max_budget_without_duration_initializes_router_budget_limiter() -> None: + """A configured zero budget must not be skipped by a truthiness check.""" + assert RouterBudgetLimiting.should_init_router_budget_limiter( + provider_budget_config=None, + model_list=[{"litellm_params": {"max_budget": 0}}], + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("scope", ["provider", "deployment", "tag"]) +async def test_none_max_budget_does_not_block_spend(disable_budget_sync, scope: str) -> None: + """An unset budget retains the existing non-blocking behaviour.""" + limiter = RouterBudgetLimiting(dual_cache=DualCache(), provider_budget_config=None) + deployment = _deployment() + provider_configs: dict[str, BudgetConfig] = {} + deployment_configs: dict[str, BudgetConfig] = {} + deployment_providers: list[str | None] = [] + request_tags: list[str] = [] + + if scope == "provider": + provider_configs = {"openai": _budget(limit=None)} + deployment_providers = ["openai"] + elif scope == "deployment": + deployment_configs = {"deployment-1": _budget(limit=None)} + else: + limiter.tag_budget_config = {"prod": _budget(limit=None)} + request_tags = ["prod"] + + kept, blocked_info = limiter._filter_out_deployments_above_budget( + potential_deployments=[], + healthy_deployments=[deployment], + provider_configs=provider_configs, + deployment_configs=deployment_configs, + deployment_providers=deployment_providers, + spend_map={}, + request_tags=request_tags, + ) + + assert kept == [deployment] + assert blocked_info == ""