mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
refactor(proxy): type the user window cache fallback and tighten carried window metadata types
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
b730b06691
commit
9024c2784d
4 changed files with 25 additions and 34 deletions
|
|
@ -40,6 +40,7 @@ from litellm.litellm_core_utils.dd_tracing import tracer
|
|||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
from litellm.litellm_core_utils.safe_json_loads import safe_json_loads
|
||||
from litellm.models.project import LiteLLM_ProjectTable
|
||||
from litellm.models.team import BudgetLimitEntry
|
||||
from litellm.proxy._types import (
|
||||
RBAC_ROLES,
|
||||
CallInfo,
|
||||
|
|
@ -5599,7 +5600,7 @@ async def _team_multi_budget_check(
|
|||
async def _user_multi_budget_check(
|
||||
valid_token: UserAPIKeyAuth | None,
|
||||
team_object: LiteLLM_TeamTable | None,
|
||||
general_settings: dict,
|
||||
general_settings: Mapping[str, object],
|
||||
):
|
||||
"""
|
||||
Raises BudgetExceededError if any budget window in valid_token.user_budget_limits is exceeded.
|
||||
|
|
@ -5617,30 +5618,26 @@ async def _user_multi_budget_check(
|
|||
|
||||
from litellm.proxy.proxy_server import get_current_spend
|
||||
|
||||
windows: Final[tuple[dict, ...]] = tuple(
|
||||
window if isinstance(window, dict) else window.model_dump() for window in valid_token.user_budget_limits
|
||||
)
|
||||
bind_spend_counter_keys(
|
||||
frozenset(f"spend:user:{valid_token.user_id}:window:{w['budget_duration']}" for w in windows)
|
||||
)
|
||||
windows: Final[tuple[BudgetLimitEntry, ...]] = tuple(valid_token.user_budget_limits)
|
||||
bind_spend_counter_keys(frozenset(f"spend:user:{valid_token.user_id}:window:{w.budget_duration}" for w in windows))
|
||||
for w in windows:
|
||||
counter_key = f"spend:user:{valid_token.user_id}:window:{w['budget_duration']}"
|
||||
counter_key = f"spend:user:{valid_token.user_id}:window:{w.budget_duration}"
|
||||
window_spend = await get_current_spend(
|
||||
counter_key=counter_key,
|
||||
fallback_spend=0.0,
|
||||
max_budget=w["max_budget"],
|
||||
max_budget=w.max_budget,
|
||||
window_entity_type="User",
|
||||
window_entity_id=valid_token.user_id,
|
||||
window_duration=str(w["budget_duration"]),
|
||||
window_duration=str(w.budget_duration),
|
||||
window_start=get_budget_window_start(w),
|
||||
)
|
||||
if math.isfinite(w["max_budget"]) and window_spend >= w["max_budget"]:
|
||||
if math.isfinite(w.max_budget) and window_spend >= w.max_budget:
|
||||
raise litellm.BudgetExceededError(
|
||||
current_cost=window_spend,
|
||||
max_budget=w["max_budget"],
|
||||
max_budget=w.max_budget,
|
||||
message=(
|
||||
f"ExceededBudget: User={valid_token.user_id} over {w['budget_duration']} budget. "
|
||||
f"Spend=${window_spend:.4f}, Limit=${w['max_budget']:.2f}"
|
||||
f"ExceededBudget: User={valid_token.user_id} over {w.budget_duration} budget. "
|
||||
f"Spend=${window_spend:.4f}, Limit=${w.max_budget:.2f}"
|
||||
),
|
||||
entity_type=Litellm_EntityType.USER.value,
|
||||
entity_id=valid_token.user_id,
|
||||
|
|
|
|||
|
|
@ -3061,15 +3061,11 @@ async def _increment_spend_counters_batched(
|
|||
)
|
||||
)
|
||||
|
||||
async def _user_window_increment(window: object) -> PendingSpendIncrement | None:
|
||||
duration = (
|
||||
window["budget_duration"] if isinstance(window, dict) else getattr(window, "budget_duration", None)
|
||||
)
|
||||
user_window_reset_at = (
|
||||
window.get("reset_at") if isinstance(window, dict) else getattr(window, "reset_at", None)
|
||||
)
|
||||
async def _user_window_increment(window: BudgetLimitEntry) -> PendingSpendIncrement | None:
|
||||
duration: Final = window.budget_duration
|
||||
user_window_reset_at: Final = window.reset_at
|
||||
user_window_counter: Final = f"spend:user:{scope_user_id}:window:{duration}"
|
||||
user_window_start = get_budget_window_start(window)
|
||||
user_window_start: Final = get_budget_window_start(window)
|
||||
pending_window: Final = (
|
||||
await _prepare_window_spend_counter_increment(
|
||||
counter_key=user_window_counter,
|
||||
|
|
@ -3380,16 +3376,11 @@ async def _enqueue_window_spend_row_update(
|
|||
)
|
||||
|
||||
|
||||
def _cached_user_budget_limits(cached_user: object) -> tuple[object, ...]:
|
||||
if cached_user is None:
|
||||
def _cached_user_budget_limits(cached_user: object) -> tuple[BudgetLimitEntry, ...]:
|
||||
user: Final = CacheCodec.deserialize(cached_user, LiteLLM_UserTable)
|
||||
if user is None or user.budget_limits is None:
|
||||
return ()
|
||||
raw: Final[object] = (
|
||||
cached_user.get("budget_limits")
|
||||
if isinstance(cached_user, dict)
|
||||
else getattr(cached_user, "budget_limits", None)
|
||||
)
|
||||
parsed: Final[object] = json.loads(raw) if isinstance(raw, str) else raw
|
||||
return tuple(parsed) if isinstance(parsed, list) else ()
|
||||
return tuple(user.budget_limits)
|
||||
|
||||
|
||||
async def _prepare_window_spend_counter_increment(
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ def carried_budget_metadata(valid_token: UserAPIKeyAuth) -> Mapping[str, object]
|
|||
)
|
||||
|
||||
|
||||
def carried_user_budget_limits_metadata(valid_token: UserAPIKeyAuth) -> tuple[dict[str, object], ...] | None:
|
||||
def carried_user_budget_limits_metadata(valid_token: UserAPIKeyAuth) -> tuple[Mapping[str, object], ...] | None:
|
||||
if valid_token.user_budget_limits is None:
|
||||
return None
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -13034,11 +13034,14 @@ async def test_team_window_spend_row_is_enqueued():
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_window_spend_row_is_enqueued():
|
||||
from litellm.models.user import LiteLLM_UserTable
|
||||
from litellm.proxy.proxy_server import increment_spend_counters
|
||||
|
||||
reset_at = datetime.now(timezone.utc) + timedelta(days=3)
|
||||
user_obj = MagicMock()
|
||||
user_obj.budget_limits = [{"budget_duration": "7d", "max_budget": 50.0, "reset_at": reset_at.isoformat()}]
|
||||
user_obj = LiteLLM_UserTable(
|
||||
user_id="user-1",
|
||||
budget_limits=[{"budget_duration": "7d", "max_budget": 50.0, "reset_at": reset_at.isoformat()}],
|
||||
)
|
||||
|
||||
with _window_spend_enqueue_env({"user-1": user_obj}) as queue:
|
||||
await increment_spend_counters(token=None, team_id=None, user_id="user-1", response_cost=1.5)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue