mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-15 09:12:18 +00:00
fix(proxy): never block a soft-budget-only user under fail-closed budget enforcement
Stop passing soft_budget to get_current_spend as its repair threshold, since in fail_closed_budget_enforcement mode an unverifiable read below that threshold raised a 503 for a user who only has a warning budget. Move formatBudgetValue into dataUtils so user_info_view.tsx stays under the 800-line lint cap.
This commit is contained in:
parent
7b3c2982a2
commit
bc164385b3
4 changed files with 33 additions and 5 deletions
|
|
@ -1473,7 +1473,7 @@ async def common_checks(
|
|||
user_spend: Final = await get_current_spend(
|
||||
counter_key=user_counter(user_object.user_id),
|
||||
fallback_spend=user_object.spend or 0.0,
|
||||
max_budget=user_budget if user_budget is not None else user_object.soft_budget,
|
||||
max_budget=user_budget,
|
||||
)
|
||||
_user_soft_budget_check(user_object=user_object, user_spend=user_spend, proxy_logging_obj=proxy_logging_obj)
|
||||
if user_budget is None:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import { teamDetailHref } from "@/utils/entityLinks";
|
|||
import { BadgeLink } from "@/components/shared/BadgeLink";
|
||||
import { UserEditView } from "../user_edit_view";
|
||||
import OnboardingModal, { InvitationLink } from "@/components/onboarding_link";
|
||||
import { formatNumberWithCommas, copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils";
|
||||
import { formatBudgetValue, formatNumberWithCommas, copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils";
|
||||
import { ArrowLeft, CheckIcon, CopyIcon, Plus, RefreshCw, Trash2 } from "lucide-react";
|
||||
import { toast } from "@/lib/toast";
|
||||
import { getBudgetDurationLabel } from "@/components/common_components/budget_duration_dropdown";
|
||||
|
|
@ -51,9 +51,6 @@ import { AutoRouterUsageView } from "@/app/(dashboard)/cost-optimization/_compon
|
|||
import { useActivityDateRange } from "@/app/(dashboard)/cost-optimization/_components/useDailyActivityRange";
|
||||
import { BudgetPeriodTooltip } from "@/components/shared/BudgetPeriodTooltip";
|
||||
|
||||
const formatBudgetValue = (value: number | null | undefined, fallback: string, precision = 4): string =>
|
||||
value !== null && value !== undefined ? `$${formatNumberWithCommas(value, precision)}` : fallback;
|
||||
|
||||
interface UserInfoViewProps {
|
||||
userId: string;
|
||||
onClose: () => void;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ export const formatNumberWithCommas = (
|
|||
return `${sign}${scaled.toLocaleString("en-US", opts)}${suffix}`;
|
||||
};
|
||||
|
||||
export const formatBudgetValue = (value: number | null | undefined, fallback: string, precision = 4): string =>
|
||||
value !== null && value !== undefined ? `$${formatNumberWithCommas(value, precision)}` : fallback;
|
||||
|
||||
export const getSpendString = (value: number | null | undefined, decimals: number = 6): string => {
|
||||
if (value === null || value === undefined || !Number.isFinite(value) || value === 0) {
|
||||
return "-";
|
||||
|
|
|
|||
|
|
@ -2571,3 +2571,31 @@ async def test_common_checks_alerts_user_over_soft_budget_without_blocking(max_b
|
|||
assert soft_alerts[0].user_id == user_id
|
||||
assert soft_alerts[0].spend == 7.0
|
||||
assert soft_alerts[0].soft_budget == 5.0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_soft_budget_only_user_is_not_blocked_when_fail_closed_spend_is_unverifiable(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
import litellm.proxy.proxy_server as proxy_server
|
||||
from litellm.models.user import LiteLLM_UserTable
|
||||
from litellm.proxy.auth.auth_checks import common_checks
|
||||
|
||||
monkeypatch.setitem(proxy_server.general_settings, "fail_closed_budget_enforcement", True)
|
||||
user_id: Final = f"soft-only-{uuid.uuid4().hex}"
|
||||
|
||||
result: Final = await common_checks(
|
||||
request_body={"model": "gpt-4o-mini"},
|
||||
team_object=None,
|
||||
user_object=LiteLLM_UserTable(user_id=user_id, soft_budget=5.0, spend=1.0),
|
||||
end_user_object=None,
|
||||
global_proxy_spend=None,
|
||||
general_settings={},
|
||||
route="/chat/completions",
|
||||
llm_router=None,
|
||||
proxy_logging_obj=MagicMock(spec=ProxyLogging),
|
||||
valid_token=UserAPIKeyAuth(token=f"token-{user_id}", user_id=user_id),
|
||||
request=MagicMock(),
|
||||
)
|
||||
|
||||
assert result is True
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue