fix(proxy): reject throttled exhausted budgets in JEV previews
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Moe Khalil 2026-09-19 18:11:50 +00:00
parent 503ab0b373
commit 3b0d32ec6f
2 changed files with 57 additions and 0 deletions

View file

@ -345,6 +345,14 @@ async def _authorize_models_this_test_can_call(
code=status.HTTP_400_BAD_REQUEST,
) from e
if config.classifier_type == "jev" and user_api_key_dict.budget_throttle_pct is not None:
raise ProxyException(
message="Budget has been exceeded! JEV Test Routing requires available budget.",
type=ProxyErrorTypes.budget_exceeded,
param=None,
code=status.HTTP_400_BAD_REQUEST,
)
@router.post(
"/auto_router/validate_complexity_router_config",

View file

@ -12,6 +12,7 @@ import pytest
from fastapi import HTTPException, Request
from pydantic import ValidationError
import litellm
from litellm.proxy import proxy_server
from litellm.proxy._types import (
LitellmUserRoles,
@ -488,6 +489,54 @@ async def test_jev_test_routing_enforces_key_budget_before_provider_invocation(
client.evaluate.assert_awaited_once()
@pytest.mark.parametrize(
"max_budget, spend, denied",
((0.0, 0.0, True), (1.0, 2.0, True), (1.0, 0.5, False), (None, 2.0, False)),
)
@pytest.mark.asyncio
async def test_jev_test_routing_hard_blocks_exhausted_throttle_enabled_keys(
monkeypatch: pytest.MonkeyPatch, max_budget: float | None, spend: float, denied: bool
) -> None:
client: Final = AsyncMock(spec=JevClassifierClient)
client.evaluate.return_value = JevSystemOneResponse(
model="jev-test",
answers={
"tier": JevChoiceAnswer(type="choice", choice="SIMPLE", probabilities={"SIMPLE": 1.0}, confidence=1.0)
},
)
monkeypatch.setattr(litellm, "budget_exceeded_throttle_percentage", 0.1)
monkeypatch.setattr(proxy_server, "llm_router", _router())
monkeypatch.setattr(auto_router_endpoints, "ComplexityRouter", partial(ComplexityRouter, jev_client=client))
actor: Final = UserAPIKeyAuth(
user_role=LitellmUserRoles.PROXY_ADMIN,
api_key="sk-jev-throttle-test",
user_id="admin",
models=["cheap-model", "typesafe/jev-test"],
max_budget=max_budget,
spend=spend,
rpm_limit=100,
metadata={"throttle_on_budget_exceeded": True},
)
request: Final = _request(
"what is 2+2",
classifier_type="jev",
jev_classifier_config={"model": "jev-test"},
)
if denied:
with pytest.raises(ProxyException) as exc_info:
await preview_auto_router_routing(http_request=ROUTING_HTTP_REQUEST, data=request, user_api_key_dict=actor)
assert exc_info.value.type == ProxyErrorTypes.budget_exceeded
assert exc_info.value.code == "400"
client.evaluate.assert_not_called()
return
response: Final = await preview_auto_router_routing(
http_request=ROUTING_HTTP_REQUEST, data=request, user_api_key_dict=actor
)
assert response.routing_decision["cause"] == "jev_classifier"
client.evaluate.assert_awaited_once()
@pytest.mark.parametrize("max_budget, spend", ((0.0, 0.0), (1.0, 2.0)))
@pytest.mark.asyncio
async def test_a_heuristic_config_does_not_need_a_budget(