diff --git a/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CreateCustomerDialog.tsx b/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CreateCustomerDialog.tsx index 3cbdd5a79cc..3fc37523154 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CreateCustomerDialog.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CreateCustomerDialog.tsx @@ -12,6 +12,8 @@ export default function CreateCustomerDialog({ open, onOpenChange, onCreated }: const [userId, setUserId] = useState(""); const [alias, setAlias] = useState(""); const [budget, setBudget] = useState(""); + const [tpm, setTpm] = useState(""); + const [rpm, setRpm] = useState(""); const create = $api.useMutation("post", "/customer/new", { onSuccess: () => { toast.success(`Created ${alias || userId}`); @@ -30,6 +32,8 @@ export default function CreateCustomerDialog({ open, onOpenChange, onCreated }: blocked: false, max_budget: budget ? Number(budget) : null, budget_duration: budget ? "1mo" : null, + tpm_limit: tpm ? Number(tpm) : null, + rpm_limit: rpm ? Number(rpm) : null, }, }); }; @@ -52,10 +56,23 @@ export default function CreateCustomerDialog({ open, onOpenChange, onCreated }: setAlias(e.target.value)} placeholder="Acme Corp" /> -
- - setBudget(e.target.value.replace(/[^0-9.]/g, ""))} placeholder="Leave empty for no budget" /> +
+
+ + setBudget(e.target.value.replace(/[^0-9.]/g, ""))} placeholder="Leave empty for no budget" /> +
+
+
+ + setTpm(e.target.value.replace(/[^0-9]/g, ""))} placeholder="No limit" /> +
+
+ + setRpm(e.target.value.replace(/[^0-9]/g, ""))} placeholder="No limit" /> +
+
+

TPM/RPM apply to this customer across every key it uses; the proxy returns 429 above them.

diff --git a/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CustomerDetail.tsx b/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CustomerDetail.tsx index ddcdc75ccd6..e043145bd65 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CustomerDetail.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/CustomerDetail.tsx @@ -43,6 +43,9 @@ export default function CustomerDetail({ customerId, onBack }: { customerId: str const label = c?.alias || customerId; const status = c ? customerStatus(c, spend) : "active"; const max = c?.litellm_budget_table?.max_budget ?? null; + const tpmLimit = c?.litellm_budget_table?.tpm_limit ?? null; + const rpmLimit = c?.litellm_budget_table?.rpm_limit ?? null; + const limitsText = [tpmLimit !== null ? `${tpmLimit.toLocaleString()} TPM` : null, rpmLimit !== null ? `${rpmLimit.toLocaleString()} RPM` : null].filter(Boolean).join(" / "); const refresh = () => { keysQuery.refetch(); trafficQuery.refetch(); }; const removeKey = async (k: PortalKey) => { if (!accessToken) return; @@ -67,6 +70,7 @@ export default function CustomerDetail({ customerId, onBack }: { customerId: str

Spend this month {formatUSD(spend)} {max !== null && max !== undefined ? <> of {formatUSD(max)} budget : ", no budget"} + {limitsText && <> · {limitsText}}

diff --git a/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/EditCustomerDialog.tsx b/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/EditCustomerDialog.tsx index 1a2d21bdcf2..5257a2a4c67 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/EditCustomerDialog.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/customers/_components/EditCustomerDialog.tsx @@ -20,8 +20,16 @@ function EditCustomerForm({ onOpenChange, customer, onSaved }: Omit (n === null || n === undefined ? "" : String(n)); + const [tpm, setTpm] = useState(asText(customer.litellm_budget_table?.tpm_limit)); + const [rpm, setRpm] = useState(asText(customer.litellm_budget_table?.rpm_limit)); + const updateLimits = $api.useMutation("post", "/budget/update"); const update = $api.useMutation("post", "/customer/update", { - onSuccess: () => { + onSuccess: async () => { + if (budgetId) { + await updateLimits.mutateAsync({ body: { budget_id: budgetId, tpm_limit: tpm ? Number(tpm) : null, rpm_limit: rpm ? Number(rpm) : null } }); + } toast.success("Customer updated"); onSaved(); onOpenChange(false); @@ -54,9 +62,20 @@ function EditCustomerForm({ onOpenChange, customer, onSaved }: OmitMax Budget (USD) setBudget(e.target.value.replace(/[^0-9.]/g, ""))} placeholder="Leave empty for no budget" />
+
+
+ + setTpm(e.target.value.replace(/[^0-9]/g, ""))} placeholder="No limit" disabled={!budgetId} /> +
+
+ + setRpm(e.target.value.replace(/[^0-9]/g, ""))} placeholder="No limit" disabled={!budgetId} /> +
+
+

{budgetId ? "Limits apply to this customer across every key it uses." : "Set a budget or limits when creating the customer to enable TPM/RPM here."}

- +