mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
Surface budget_reset_at on team info and members tab
Adds a formatBudgetReset helper (dayjs-based, with validity guard) that renders the next reset as "today" / "in N days" / "on MMM D, YYYY". The team budget card now shows the team's reset timestamp and the member- default reset (when a shared team_member_budget is configured), and the Members tab gains a Budget Reset column per member.
This commit is contained in:
parent
331e3f2250
commit
5e5a94ac8d
3 changed files with 48 additions and 4 deletions
|
|
@ -15,6 +15,7 @@ import {
|
|||
teamUpdateCall,
|
||||
} from "@/components/networking";
|
||||
import { useGuardrails } from "@/app/(dashboard)/hooks/guardrails/useGuardrails";
|
||||
import { formatBudgetReset } from "@/utils/budgetUtils";
|
||||
import { formatNumberWithCommas } from "@/utils/dataUtils";
|
||||
import { mapEmptyStringToNull } from "@/utils/keyUpdateUtils";
|
||||
import { isProxyAdminRole } from "@/utils/roles";
|
||||
|
|
@ -70,6 +71,7 @@ export interface TeamMembership {
|
|||
rpm_limit: number | null;
|
||||
model_max_budget: Record<string, number> | null;
|
||||
budget_duration: string | null;
|
||||
budget_reset_at: string | null;
|
||||
allowed_models?: string[] | null;
|
||||
};
|
||||
}
|
||||
|
|
@ -120,6 +122,7 @@ export interface TeamData {
|
|||
team_member_budget_table: {
|
||||
max_budget: number;
|
||||
budget_duration: string;
|
||||
budget_reset_at: string | null;
|
||||
tpm_limit: number | null;
|
||||
rpm_limit: number | null;
|
||||
} | null;
|
||||
|
|
@ -732,12 +735,21 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
|||
<Text>
|
||||
of {info.max_budget === null ? "Unlimited" : `$${formatNumberWithCommas(info.max_budget, 4)}`}
|
||||
</Text>
|
||||
{info.budget_duration && <Text className="text-gray-500">Reset: {info.budget_duration}</Text>}
|
||||
{formatBudgetReset(info.budget_reset_at) && (
|
||||
<Text className="text-gray-500">Resets {formatBudgetReset(info.budget_reset_at)}</Text>
|
||||
)}
|
||||
<br />
|
||||
{info.team_member_budget_table && (
|
||||
<Text className="text-gray-500">
|
||||
Team Member Budget: ${formatNumberWithCommas(info.team_member_budget_table.max_budget, 4)}
|
||||
</Text>
|
||||
<>
|
||||
<Text className="text-gray-500">
|
||||
Team Member Budget: ${formatNumberWithCommas(info.team_member_budget_table.max_budget, 4)}
|
||||
</Text>
|
||||
{formatBudgetReset(info.team_member_budget_table.budget_reset_at) && (
|
||||
<Text className="text-gray-500">
|
||||
Member budgets reset {formatBudgetReset(info.team_member_budget_table.budget_reset_at)}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { Member } from "@/components/networking";
|
||||
import { formatBudgetReset } from "@/utils/budgetUtils";
|
||||
import { formatNumberWithCommas } from "@/utils/dataUtils";
|
||||
import { isProxyAdminRole, isUserTeamAdminForSingleTeam } from "@/utils/roles";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
|
|
@ -88,6 +89,12 @@ export default function TeamMemberTab({
|
|||
return models && models.length > 0 ? models : null;
|
||||
};
|
||||
|
||||
const getUserBudgetReset = (userId: string | null): string | null => {
|
||||
if (!userId) return null;
|
||||
const membership = teamData.team_memberships.find((tm) => tm.user_id === userId);
|
||||
return formatBudgetReset(membership?.litellm_budget_table?.budget_reset_at);
|
||||
};
|
||||
|
||||
const extraColumns: ColumnsType<Member> = [
|
||||
{
|
||||
title: (
|
||||
|
|
@ -146,6 +153,18 @@ export default function TeamMemberTab({
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Budget Reset",
|
||||
key: "budget_reset",
|
||||
render: (_: unknown, record: Member) => {
|
||||
const reset = getUserBudgetReset(record.user_id);
|
||||
return reset ? (
|
||||
<Typography.Text>{reset}</Typography.Text>
|
||||
) : (
|
||||
<Typography.Text type="secondary">—</Typography.Text>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<Space direction="horizontal">
|
||||
|
|
|
|||
13
ui/litellm-dashboard/src/utils/budgetUtils.ts
Normal file
13
ui/litellm-dashboard/src/utils/budgetUtils.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import dayjs from "dayjs";
|
||||
|
||||
export function formatBudgetReset(iso: string | null | undefined): string | null {
|
||||
if (!iso) return null;
|
||||
const resetDate = dayjs(iso);
|
||||
if (!resetDate.isValid()) return null;
|
||||
|
||||
const days = resetDate.diff(dayjs(), "day");
|
||||
if (days < 0) return `on ${resetDate.format("MMM D, YYYY")}`;
|
||||
if (days === 0) return "today";
|
||||
if (days < 7) return `in ${days} day${days === 1 ? "" : "s"}`;
|
||||
return `on ${resetDate.format("MMM D, YYYY")}`;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue