@acarmisc/backstage-plugin-litellm 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -2749,11 +2749,391 @@ var init_UsageStats = __esm({
2749
2749
  }
2750
2750
  });
2751
2751
 
2752
- // src/components/TeamUsage.tsx
2753
- import React7, { useState as useState5 } from "react";
2752
+ // src/budget.ts
2753
+ function limitPct(spend, budget) {
2754
+ if (budget <= 0) return 0;
2755
+ return spend / budget * 100;
2756
+ }
2757
+ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
2758
+ const budgeted = keys.filter((k) => (k.max_budget ?? 0) > 0).map((k) => {
2759
+ const spend = k.spend ?? 0;
2760
+ return {
2761
+ kind: "key",
2762
+ label: k.key_alias || k.key || "Untitled key",
2763
+ sublabel: k.key,
2764
+ budget: k.max_budget,
2765
+ spend,
2766
+ budgetDuration: k.budget_duration,
2767
+ pct: limitPct(spend, k.max_budget)
2768
+ };
2769
+ }).sort((a, b) => b.pct - a.pct);
2770
+ const userBudget = user && (user.max_budget ?? user.hard_limit ?? 0) > 0 ? (() => {
2771
+ const budget = user.max_budget ?? user.hard_limit ?? 0;
2772
+ const spend = user.current_spend ?? user.spend ?? 0;
2773
+ return {
2774
+ kind: "user",
2775
+ label: "Personal budget",
2776
+ sublabel: user.user_email || user.user_id,
2777
+ budget,
2778
+ spend,
2779
+ budgetDuration: user.budget_duration,
2780
+ softLimit: user.soft_limit,
2781
+ pct: limitPct(spend, budget)
2782
+ };
2783
+ })() : void 0;
2784
+ const teamLimits = teams.filter((t) => (t.max_budget ?? 0) > 0).map((t) => {
2785
+ const spend = t.spend ?? 0;
2786
+ return {
2787
+ kind: "team",
2788
+ label: t.team_alias || "Untitled team",
2789
+ sublabel: t.team_id,
2790
+ budget: t.max_budget,
2791
+ spend,
2792
+ budgetDuration: t.budget_duration,
2793
+ pct: limitPct(spend, t.max_budget)
2794
+ };
2795
+ }).sort((a, b) => b.pct - a.pct);
2796
+ return {
2797
+ keys: budgeted.slice(0, maxKeys),
2798
+ hiddenBudgetedKeys: Math.max(0, budgeted.length - maxKeys),
2799
+ totalKeyCount: keys.length,
2800
+ user: userBudget,
2801
+ teams: teamLimits
2802
+ };
2803
+ }
2804
+ function budgetHeadline(summary) {
2805
+ const pcts = [
2806
+ ...summary.keys.map((k) => k.pct),
2807
+ ...summary.user ? [summary.user.pct] : [],
2808
+ ...summary.teams.map((t) => t.pct)
2809
+ ];
2810
+ const count = summary.keys.length + summary.hiddenBudgetedKeys + (summary.user ? 1 : 0) + summary.teams.length;
2811
+ return {
2812
+ count,
2813
+ closestPct: pcts.length ? Math.max(...pcts) : null
2814
+ };
2815
+ }
2816
+ function fmtBudgetDuration(duration) {
2817
+ if (!duration) return null;
2818
+ const m = /^(\d+)(s|m|h|d)$/.exec(duration);
2819
+ if (!m) return duration;
2820
+ const n = Number(m[1]);
2821
+ const unit = m[2];
2822
+ if (unit === "d") return n === 1 ? "daily" : `every ${n} days`;
2823
+ if (unit === "h") return n === 1 ? "hourly" : `every ${n} hours`;
2824
+ if (unit === "m") return n === 1 ? "every minute" : `every ${n} minutes`;
2825
+ return n === 1 ? "every second" : `every ${n} seconds`;
2826
+ }
2827
+ function budgetTone2(pct) {
2828
+ if (pct >= 100) return "danger";
2829
+ if (pct >= 80) return "warning";
2830
+ return "accent";
2831
+ }
2832
+ var init_budget = __esm({
2833
+ "src/budget.ts"() {
2834
+ "use strict";
2835
+ }
2836
+ });
2837
+
2838
+ // src/components/LiteLLMBudgetWidget.tsx
2839
+ import React7, { useEffect as useEffect3, useMemo as useMemo6, useState as useState5 } from "react";
2754
2840
  import Paper5 from "@mui/material/Paper";
2755
2841
  import Box7 from "@mui/material/Box";
2756
2842
  import Typography7 from "@mui/material/Typography";
2843
+ import CircularProgress4 from "@mui/material/CircularProgress";
2844
+ import Alert3 from "@mui/material/Alert";
2845
+ import Collapse from "@mui/material/Collapse";
2846
+ import IconButton4 from "@mui/material/IconButton";
2847
+ import { ExpandMore as ExpandMore2 } from "@mui/icons-material";
2848
+ import { alpha as alpha4 } from "@mui/material/styles";
2849
+ import { useApi as useApi2 } from "@backstage/core-plugin-api";
2850
+ function levelToneColor(tone, theme) {
2851
+ switch (tone) {
2852
+ case "success":
2853
+ return theme.palette.success.main;
2854
+ case "warning":
2855
+ return theme.palette.warning.main;
2856
+ case "danger":
2857
+ return theme.palette.error.main;
2858
+ case "info":
2859
+ return theme.palette.info.main;
2860
+ default:
2861
+ return theme.palette.primary.main;
2862
+ }
2863
+ }
2864
+ var LevelFrame, EmptyLimitCard, LimitCard, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
2865
+ var init_LiteLLMBudgetWidget = __esm({
2866
+ "src/components/LiteLLMBudgetWidget.tsx"() {
2867
+ "use strict";
2868
+ init_api();
2869
+ init_format();
2870
+ init_ui();
2871
+ init_budget();
2872
+ LevelFrame = ({
2873
+ rank,
2874
+ name,
2875
+ tagline,
2876
+ note,
2877
+ tone,
2878
+ isLast,
2879
+ children
2880
+ }) => /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ React7.createElement(
2881
+ Box7,
2882
+ {
2883
+ sx: (theme) => ({
2884
+ width: 26,
2885
+ height: 26,
2886
+ borderRadius: "50%",
2887
+ display: "flex",
2888
+ alignItems: "center",
2889
+ justifyContent: "center",
2890
+ bgcolor: alpha4(levelToneColor(tone, theme), 0.14),
2891
+ color: levelToneColor(tone, theme),
2892
+ fontSize: 12,
2893
+ fontWeight: 700,
2894
+ flexShrink: 0
2895
+ })
2896
+ },
2897
+ rank
2898
+ ), /* @__PURE__ */ React7.createElement(
2899
+ Box7,
2900
+ {
2901
+ sx: (theme) => ({
2902
+ flex: 1,
2903
+ width: 2,
2904
+ minHeight: 14,
2905
+ bgcolor: isLast ? "transparent" : alpha4(theme.palette.text.primary, 0.12)
2906
+ })
2907
+ }
2908
+ )), /* @__PURE__ */ React7.createElement(Box7, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ React7.createElement(
2909
+ Typography7,
2910
+ {
2911
+ sx: (theme) => ({
2912
+ fontSize: 12.5,
2913
+ fontWeight: 700,
2914
+ letterSpacing: "0.08em",
2915
+ textTransform: "uppercase",
2916
+ color: levelToneColor(tone, theme)
2917
+ })
2918
+ },
2919
+ name
2920
+ ), /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ React7.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
2921
+ EmptyLimitCard = ({ children }) => /* @__PURE__ */ React7.createElement(
2922
+ Paper5,
2923
+ {
2924
+ variant: "outlined",
2925
+ sx: (theme) => ({
2926
+ px: 1.5,
2927
+ py: 1.25,
2928
+ borderRadius: 1.5,
2929
+ bgcolor: alpha4(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
2930
+ })
2931
+ },
2932
+ /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
2933
+ );
2934
+ LimitCard = ({ limit }) => {
2935
+ const tone = budgetTone2(limit.pct);
2936
+ const pct = Math.round(limit.pct);
2937
+ const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
2938
+ return /* @__PURE__ */ React7.createElement(Paper5, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React7.createElement(
2939
+ Typography7,
2940
+ {
2941
+ variant: "body2",
2942
+ sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
2943
+ title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
2944
+ },
2945
+ limit.label
2946
+ ), limit.sublabel && /* @__PURE__ */ React7.createElement(
2947
+ Typography7,
2948
+ {
2949
+ variant: "caption",
2950
+ color: "text.secondary",
2951
+ sx: {
2952
+ fontSize: 10.5,
2953
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
2954
+ display: "block",
2955
+ overflow: "hidden",
2956
+ textOverflow: "ellipsis",
2957
+ whiteSpace: "nowrap"
2958
+ }
2959
+ },
2960
+ limit.sublabel
2961
+ )), /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ React7.createElement(Box7, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ React7.createElement(Box7, { sx: { mt: 1 } }, /* @__PURE__ */ React7.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ React7.createElement(
2962
+ Box7,
2963
+ {
2964
+ sx: {
2965
+ display: "flex",
2966
+ justifyContent: "space-between",
2967
+ alignItems: "center",
2968
+ gap: 1,
2969
+ mt: 0.75
2970
+ }
2971
+ },
2972
+ /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`),
2973
+ /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
2974
+ ));
2975
+ };
2976
+ TaggedLimit = ({ limit }) => {
2977
+ const tone = budgetTone2(limit.pct);
2978
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(
2979
+ Typography7,
2980
+ {
2981
+ sx: (theme) => ({
2982
+ fontSize: 11,
2983
+ fontWeight: 700,
2984
+ letterSpacing: "0.08em",
2985
+ textTransform: "uppercase",
2986
+ color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
2987
+ mb: 0.5
2988
+ })
2989
+ },
2990
+ limit.kind
2991
+ ), /* @__PURE__ */ React7.createElement(LimitCard, { limit }));
2992
+ };
2993
+ FullBody = ({ summary }) => {
2994
+ const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
2995
+ return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(
2996
+ LevelFrame,
2997
+ {
2998
+ rank: 1,
2999
+ name: "Key",
3000
+ tagline: "per-key cap \xB7 highest priority",
3001
+ note: hasBudgetedKeys ? void 0 : "no key budgets set"
3002
+ },
3003
+ !hasBudgetedKeys && /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3004
+ summary.keys.map((k) => /* @__PURE__ */ React7.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3005
+ summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, "+", summary.hiddenBudgetedKeys, " more budgeted key", summary.hiddenBudgetedKeys > 1 ? "s" : "", " further from the cap")
3006
+ ), /* @__PURE__ */ React7.createElement(
3007
+ LevelFrame,
3008
+ {
3009
+ rank: 2,
3010
+ name: "User",
3011
+ tagline: "cap on everything you spend with your own keys",
3012
+ note: "skipped for team-bound keys"
3013
+ },
3014
+ summary.user ? /* @__PURE__ */ React7.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3015
+ ), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ React7.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ React7.createElement(
3016
+ Box7,
3017
+ {
3018
+ sx: (theme) => ({
3019
+ mt: 2,
3020
+ px: 1.5,
3021
+ py: 1.25,
3022
+ borderRadius: 1.5,
3023
+ border: "1px dashed",
3024
+ borderColor: alpha4(theme.palette.text.primary, 0.18)
3025
+ })
3026
+ },
3027
+ /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", display: "block" }, "The first cap a request runs into is the one that stops it. A limit with a reset window (e.g. daily or every 30 days) clears its spend to $0 when the window closes; without a window it never resets.")
3028
+ ));
3029
+ };
3030
+ CompactBody = ({ summary }) => {
3031
+ const { count } = budgetHeadline(summary);
3032
+ if (count === 0) {
3033
+ return /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3034
+ }
3035
+ return /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, summary.keys.map((k) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, "+", summary.hiddenBudgetedKeys, " more budgeted key", summary.hiddenBudgetedKeys > 1 ? "s" : "", " further from the cap"), summary.user && /* @__PURE__ */ React7.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3036
+ };
3037
+ LiteLLMBudgetWidget = ({
3038
+ title = "Budget Policy",
3039
+ maxKeys = 3,
3040
+ compact = false,
3041
+ collapsible = false,
3042
+ defaultExpanded = true,
3043
+ action
3044
+ }) => {
3045
+ const api = useApi2(liteLlmApiRef);
3046
+ const [loading, setLoading] = useState5(true);
3047
+ const [error, setError] = useState5(null);
3048
+ const [user, setUser] = useState5(null);
3049
+ const [teams, setTeams] = useState5([]);
3050
+ const [keys, setKeys] = useState5([]);
3051
+ const [expanded, setExpanded] = useState5(defaultExpanded);
3052
+ useEffect3(() => {
3053
+ let cancelled = false;
3054
+ setLoading(true);
3055
+ setError(null);
3056
+ Promise.allSettled([api.getUserInfo(), api.getTeams(), api.listKeys()]).then(
3057
+ ([userResult, teamsResult, keysResult]) => {
3058
+ if (cancelled) return;
3059
+ if (userResult.status === "fulfilled") {
3060
+ setUser(userResult.value);
3061
+ } else {
3062
+ setUser(null);
3063
+ setError(
3064
+ userResult.reason?.message ?? "Failed to load your LiteLLM profile"
3065
+ );
3066
+ }
3067
+ setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
3068
+ setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
3069
+ setLoading(false);
3070
+ }
3071
+ );
3072
+ return () => {
3073
+ cancelled = true;
3074
+ };
3075
+ }, [api]);
3076
+ const summary = useMemo6(
3077
+ () => buildBudgetSummary(user, teams, keys, maxKeys),
3078
+ [user, teams, keys, maxKeys]
3079
+ );
3080
+ const headline = useMemo6(() => budgetHeadline(summary), [summary]);
3081
+ const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
3082
+ const closestWord = headline.closestPct === null ? "" : ` \xB7 closest ${Math.round(headline.closestPct)}%`;
3083
+ const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
3084
+ const body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, loading && /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ React7.createElement(CircularProgress4, { size: 32 })), !loading && error && /* @__PURE__ */ React7.createElement(Alert3, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ React7.createElement(CompactBody, { summary }) : /* @__PURE__ */ React7.createElement(FullBody, { summary })));
3085
+ return /* @__PURE__ */ React7.createElement(Paper5, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ React7.createElement(
3086
+ Box7,
3087
+ {
3088
+ onClick: () => setExpanded((e) => !e),
3089
+ sx: {
3090
+ display: "flex",
3091
+ alignItems: "center",
3092
+ gap: 1,
3093
+ cursor: "pointer",
3094
+ userSelect: "none",
3095
+ mb: expanded ? 1.5 : 0
3096
+ }
3097
+ },
3098
+ /* @__PURE__ */ React7.createElement(Box7, { sx: { minWidth: 0, flex: 1, display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" } }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ React7.createElement(StatusPill, { label: summaryText, tone: budgetTone2(headline.closestPct ?? 0) })),
3099
+ /* @__PURE__ */ React7.createElement(
3100
+ IconButton4,
3101
+ {
3102
+ size: "small",
3103
+ "aria-label": expanded ? "Collapse budget policy" : "Expand budget policy",
3104
+ "aria-expanded": expanded,
3105
+ onClick: (e) => {
3106
+ e.stopPropagation();
3107
+ setExpanded((x) => !x);
3108
+ },
3109
+ sx: (theme) => ({
3110
+ color: theme.palette.text.secondary,
3111
+ transform: expanded ? "rotate(180deg)" : "none",
3112
+ transition: theme.transitions.create("transform")
3113
+ })
3114
+ },
3115
+ /* @__PURE__ */ React7.createElement(ExpandMore2, null)
3116
+ )
3117
+ ) : /* @__PURE__ */ React7.createElement(Box7, { sx: { mb: 1.5 } }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ React7.createElement(Collapse, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ React7.createElement(
3118
+ Box7,
3119
+ {
3120
+ sx: (theme) => ({
3121
+ mt: 2,
3122
+ pt: 2,
3123
+ borderTop: `1px solid ${theme.palette.divider}`
3124
+ })
3125
+ },
3126
+ action
3127
+ ));
3128
+ };
3129
+ }
3130
+ });
3131
+
3132
+ // src/components/TeamUsage.tsx
3133
+ import React8, { useState as useState6 } from "react";
3134
+ import Paper6 from "@mui/material/Paper";
3135
+ import Box8 from "@mui/material/Box";
3136
+ import Typography8 from "@mui/material/Typography";
2757
3137
  import Skeleton4 from "@mui/material/Skeleton";
2758
3138
  import Divider3 from "@mui/material/Divider";
2759
3139
  import Table4 from "@mui/material/Table";
@@ -2762,12 +3142,12 @@ import TableCell4 from "@mui/material/TableCell";
2762
3142
  import TableHead4 from "@mui/material/TableHead";
2763
3143
  import TableRow4 from "@mui/material/TableRow";
2764
3144
  import TableContainer4 from "@mui/material/TableContainer";
2765
- import Collapse from "@mui/material/Collapse";
2766
- import IconButton4 from "@mui/material/IconButton";
3145
+ import Collapse2 from "@mui/material/Collapse";
3146
+ import IconButton5 from "@mui/material/IconButton";
2767
3147
  import Button5 from "@mui/material/Button";
2768
- import CircularProgress4 from "@mui/material/CircularProgress";
2769
- import { alpha as alpha4 } from "@mui/material/styles";
2770
- import { ExpandMore as ExpandMore2, Group, Add as Add3 } from "@mui/icons-material";
3148
+ import CircularProgress5 from "@mui/material/CircularProgress";
3149
+ import { alpha as alpha5 } from "@mui/material/styles";
3150
+ import { ExpandMore as ExpandMore3, Group, Add as Add3 } from "@mui/icons-material";
2771
3151
  import {
2772
3152
  AreaChart as AreaChart2,
2773
3153
  Area as Area2,
@@ -2777,7 +3157,7 @@ import {
2777
3157
  Tooltip as Tooltip2,
2778
3158
  ResponsiveContainer as ResponsiveContainer2
2779
3159
  } from "recharts";
2780
- function budgetTone2(isOver, isNear) {
3160
+ function budgetTone3(isOver, isNear) {
2781
3161
  if (isOver) return "danger";
2782
3162
  if (isNear) return "warning";
2783
3163
  return "accent";
@@ -2789,7 +3169,7 @@ var init_TeamUsage = __esm({
2789
3169
  init_ui();
2790
3170
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
2791
3171
  TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
2792
- const [expanded, setExpanded] = useState5(false);
3172
+ const [expanded, setExpanded] = useState6(false);
2793
3173
  const chart = useChartTheme();
2794
3174
  const budget = team.max_budget ?? 0;
2795
3175
  const spend = team.spend ?? 0;
@@ -2799,24 +3179,24 @@ var init_TeamUsage = __esm({
2799
3179
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
2800
3180
  const renderDailySpendSection = () => {
2801
3181
  if (usageLoading) {
2802
- return /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React7.createElement(CircularProgress4, { size: 22 }));
3182
+ return /* @__PURE__ */ React8.createElement(Box8, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React8.createElement(CircularProgress5, { size: 22 }));
2803
3183
  }
2804
3184
  if (dailyData.length === 0) return null;
2805
- return /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, /* @__PURE__ */ React7.createElement(
2806
- Typography7,
3185
+ return /* @__PURE__ */ React8.createElement(Box8, { mb: 2.5 }, /* @__PURE__ */ React8.createElement(
3186
+ Typography8,
2807
3187
  {
2808
3188
  variant: "caption",
2809
3189
  color: "text.secondary",
2810
3190
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
2811
3191
  },
2812
3192
  "Daily spend"
2813
- ), /* @__PURE__ */ React7.createElement(Box7, { height: 150 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(AreaChart2, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid2, { ...chart.grid }), /* @__PURE__ */ React7.createElement(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ React7.createElement(
3193
+ ), /* @__PURE__ */ React8.createElement(Box8, { height: 150 }, /* @__PURE__ */ React8.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React8.createElement(AreaChart2, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React8.createElement(CartesianGrid2, { ...chart.grid }), /* @__PURE__ */ React8.createElement(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React8.createElement(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ React8.createElement(
2814
3194
  Tooltip2,
2815
3195
  {
2816
3196
  cursor: chart.cursor,
2817
- content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3197
+ content: /* @__PURE__ */ React8.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
2818
3198
  }
2819
- ), /* @__PURE__ */ React7.createElement(
3199
+ ), /* @__PURE__ */ React8.createElement(
2820
3200
  Area2,
2821
3201
  {
2822
3202
  type: "monotone",
@@ -2829,8 +3209,8 @@ var init_TeamUsage = __esm({
2829
3209
  }
2830
3210
  )))));
2831
3211
  };
2832
- const sectionLabel = (label) => /* @__PURE__ */ React7.createElement(
2833
- Typography7,
3212
+ const sectionLabel = (label) => /* @__PURE__ */ React8.createElement(
3213
+ Typography8,
2834
3214
  {
2835
3215
  variant: "caption",
2836
3216
  color: "text.secondary",
@@ -2838,8 +3218,8 @@ var init_TeamUsage = __esm({
2838
3218
  },
2839
3219
  label
2840
3220
  );
2841
- return /* @__PURE__ */ React7.createElement(Paper5, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React7.createElement(
2842
- Box7,
3221
+ return /* @__PURE__ */ React8.createElement(Paper6, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React8.createElement(Box8, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React8.createElement(
3222
+ Box8,
2843
3223
  {
2844
3224
  sx: (theme) => ({
2845
3225
  width: 36,
@@ -2849,20 +3229,20 @@ var init_TeamUsage = __esm({
2849
3229
  display: "flex",
2850
3230
  alignItems: "center",
2851
3231
  justifyContent: "center",
2852
- bgcolor: alpha4(theme.palette.primary.main, 0.1),
3232
+ bgcolor: alpha5(theme.palette.primary.main, 0.1),
2853
3233
  color: theme.palette.primary.main
2854
3234
  })
2855
3235
  },
2856
- /* @__PURE__ */ React7.createElement(Group, { fontSize: "small" })
2857
- ), /* @__PURE__ */ React7.createElement(Box7, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ React7.createElement(
2858
- Typography7,
3236
+ /* @__PURE__ */ React8.createElement(Group, { fontSize: "small" })
3237
+ ), /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ React8.createElement(
3238
+ Typography8,
2859
3239
  {
2860
3240
  variant: "caption",
2861
3241
  color: "text.secondary",
2862
3242
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2863
3243
  },
2864
3244
  team.team_id
2865
- )), isOver && /* @__PURE__ */ React7.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ React7.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ React7.createElement(
3245
+ )), isOver && /* @__PURE__ */ React8.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ React8.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ React8.createElement(
2866
3246
  Button5,
2867
3247
  {
2868
3248
  size: "small",
@@ -2871,8 +3251,8 @@ var init_TeamUsage = __esm({
2871
3251
  sx: { textTransform: "none" }
2872
3252
  },
2873
3253
  "Edit"
2874
- ), /* @__PURE__ */ React7.createElement(
2875
- IconButton4,
3254
+ ), /* @__PURE__ */ React8.createElement(
3255
+ IconButton5,
2876
3256
  {
2877
3257
  size: "small",
2878
3258
  onClick: () => setExpanded((e) => !e),
@@ -2884,9 +3264,9 @@ var init_TeamUsage = __esm({
2884
3264
  transition: theme.transitions.create("transform")
2885
3265
  })
2886
3266
  },
2887
- /* @__PURE__ */ React7.createElement(ExpandMore2, null)
2888
- )), /* @__PURE__ */ React7.createElement(
2889
- Box7,
3267
+ /* @__PURE__ */ React8.createElement(ExpandMore3, null)
3268
+ )), /* @__PURE__ */ React8.createElement(
3269
+ Box8,
2890
3270
  {
2891
3271
  sx: {
2892
3272
  display: "grid",
@@ -2896,35 +3276,35 @@ var init_TeamUsage = __esm({
2896
3276
  maxWidth: 640
2897
3277
  }
2898
3278
  },
2899
- /* @__PURE__ */ React7.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
2900
- /* @__PURE__ */ React7.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
2901
- /* @__PURE__ */ React7.createElement(Stat, { label: "Budget", value: budget > 0 ? fmtUsd2(budget) : "Unlimited" }),
2902
- /* @__PURE__ */ React7.createElement(Stat, { label: "Spend", value: fmtUsd2(spend) }),
2903
- /* @__PURE__ */ React7.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
2904
- /* @__PURE__ */ React7.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
2905
- ), budget > 0 && /* @__PURE__ */ React7.createElement(Box7, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React7.createElement(Meter, { value: budgetPct, tone: budgetTone2(isOver, isNear), height: 5 })), /* @__PURE__ */ React7.createElement(Collapse, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React7.createElement(Divider3, { sx: { my: 2.5 } }), /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React7.createElement(Box7, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React7.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React7.createElement(Box7, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React7.createElement(
3279
+ /* @__PURE__ */ React8.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3280
+ /* @__PURE__ */ React8.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3281
+ /* @__PURE__ */ React8.createElement(Stat, { label: "Budget", value: budget > 0 ? fmtUsd2(budget) : "Unlimited" }),
3282
+ /* @__PURE__ */ React8.createElement(Stat, { label: "Spend", value: fmtUsd2(spend) }),
3283
+ /* @__PURE__ */ React8.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3284
+ /* @__PURE__ */ React8.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3285
+ ), budget > 0 && /* @__PURE__ */ React8.createElement(Box8, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React8.createElement(Box8, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React8.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ React8.createElement(Collapse2, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React8.createElement(Divider3, { sx: { my: 2.5 } }), /* @__PURE__ */ React8.createElement(Box8, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React8.createElement(Box8, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React8.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React8.createElement(Box8, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React8.createElement(
2906
3286
  TableContainer4,
2907
3287
  {
2908
- component: Paper5,
3288
+ component: Paper6,
2909
3289
  variant: "outlined",
2910
3290
  sx: { borderRadius: 1.5 }
2911
3291
  },
2912
- /* @__PURE__ */ React7.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead4, null, /* @__PURE__ */ React7.createElement(TableRow4, null, /* @__PURE__ */ React7.createElement(TableCell4, null, "User"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Role"))), /* @__PURE__ */ React7.createElement(TableBody4, null, team.members_with_roles.map((m) => /* @__PURE__ */ React7.createElement(TableRow4, { key: m.user_id }, /* @__PURE__ */ React7.createElement(TableCell4, null, m.user_email ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2" }, m.user_email), /* @__PURE__ */ React7.createElement(
2913
- Typography7,
3292
+ /* @__PURE__ */ React8.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead4, null, /* @__PURE__ */ React8.createElement(TableRow4, null, /* @__PURE__ */ React8.createElement(TableCell4, null, "User"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Role"))), /* @__PURE__ */ React8.createElement(TableBody4, null, team.members_with_roles.map((m) => /* @__PURE__ */ React8.createElement(TableRow4, { key: m.user_id }, /* @__PURE__ */ React8.createElement(TableCell4, null, m.user_email ? /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2" }, m.user_email), /* @__PURE__ */ React8.createElement(
3293
+ Typography8,
2914
3294
  {
2915
3295
  variant: "caption",
2916
3296
  color: "text.secondary",
2917
3297
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2918
3298
  },
2919
3299
  m.user_id
2920
- )) : /* @__PURE__ */ React7.createElement(
2921
- Typography7,
3300
+ )) : /* @__PURE__ */ React8.createElement(
3301
+ Typography8,
2922
3302
  {
2923
3303
  variant: "body2",
2924
3304
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2925
3305
  },
2926
3306
  m.user_id
2927
- )), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(
3307
+ )), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(
2928
3308
  StatusPill,
2929
3309
  {
2930
3310
  label: m.role,
@@ -2932,7 +3312,7 @@ var init_TeamUsage = __esm({
2932
3312
  dot: false
2933
3313
  }
2934
3314
  ))))))
2935
- ) : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3315
+ ) : /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
2936
3316
  };
2937
3317
  TeamUsage = ({
2938
3318
  teams,
@@ -2944,22 +3324,22 @@ var init_TeamUsage = __esm({
2944
3324
  canCreate,
2945
3325
  onCreateTeam
2946
3326
  }) => {
2947
- const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React7.createElement(
3327
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React8.createElement(
2948
3328
  Button5,
2949
3329
  {
2950
3330
  variant: "contained",
2951
3331
  color: "primary",
2952
3332
  disableElevation: true,
2953
- startIcon: /* @__PURE__ */ React7.createElement(Add3, null),
3333
+ startIcon: /* @__PURE__ */ React8.createElement(Add3, null),
2954
3334
  onClick: onCreateTeam
2955
3335
  },
2956
3336
  "Create Team"
2957
3337
  ) : void 0;
2958
3338
  if (loading) {
2959
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120 }));
3339
+ return /* @__PURE__ */ React8.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React8.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React8.createElement(Skeleton4, { variant: "rounded", height: 120 }));
2960
3340
  }
2961
3341
  if (!teams.length) {
2962
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React7.createElement(
3342
+ return /* @__PURE__ */ React8.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React8.createElement(
2963
3343
  EmptyState,
2964
3344
  {
2965
3345
  message: "You're not a member of any LiteLLM team yet.",
@@ -2967,14 +3347,14 @@ var init_TeamUsage = __esm({
2967
3347
  }
2968
3348
  ));
2969
3349
  }
2970
- return /* @__PURE__ */ React7.createElement(
3350
+ return /* @__PURE__ */ React8.createElement(
2971
3351
  SectionCard,
2972
3352
  {
2973
3353
  title: "Teams",
2974
3354
  subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
2975
3355
  actions: createAction
2976
3356
  },
2977
- /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React7.createElement(
3357
+ /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React8.createElement(
2978
3358
  TeamCard,
2979
3359
  {
2980
3360
  key: team.team_id,
@@ -2991,15 +3371,15 @@ var init_TeamUsage = __esm({
2991
3371
  });
2992
3372
 
2993
3373
  // src/components/ModelsTable.tsx
2994
- import React8, { useState as useState6, useMemo as useMemo6, useCallback } from "react";
2995
- import Box8 from "@mui/material/Box";
3374
+ import React9, { useState as useState7, useMemo as useMemo7, useCallback } from "react";
3375
+ import Box9 from "@mui/material/Box";
2996
3376
  import Table5 from "@mui/material/Table";
2997
3377
  import TableBody5 from "@mui/material/TableBody";
2998
3378
  import TableCell5 from "@mui/material/TableCell";
2999
3379
  import TableContainer5 from "@mui/material/TableContainer";
3000
3380
  import TableHead5 from "@mui/material/TableHead";
3001
3381
  import TableRow5 from "@mui/material/TableRow";
3002
- import Typography8 from "@mui/material/Typography";
3382
+ import Typography9 from "@mui/material/Typography";
3003
3383
  import MenuItem4 from "@mui/material/MenuItem";
3004
3384
  import Select from "@mui/material/Select";
3005
3385
  import FormControl from "@mui/material/FormControl";
@@ -3010,8 +3390,8 @@ import { Search as Search2 } from "@mui/icons-material";
3010
3390
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
3011
3391
  import Tooltip3 from "@mui/material/Tooltip";
3012
3392
  import Snackbar from "@mui/material/Snackbar";
3013
- import Alert3 from "@mui/material/Alert";
3014
- import { alpha as alpha5, useTheme as useTheme2 } from "@mui/material/styles";
3393
+ import Alert4 from "@mui/material/Alert";
3394
+ import { alpha as alpha6, useTheme as useTheme2 } from "@mui/material/styles";
3015
3395
  function isModelAllowedByTeam2(model, teamModels) {
3016
3396
  if (!teamModels || teamModels.length === 0 || teamModels.includes(ALL_PROXY_MODELS2)) return true;
3017
3397
  return teamModels.includes(model.model_name) || !!model.access_groups?.some((group) => teamModels.includes(group));
@@ -3037,18 +3417,18 @@ var init_ModelsTable = __esm({
3037
3417
  ALL_PROXY_MODELS2 = "all-proxy-models";
3038
3418
  ModelsTable = ({ allModels, teams, loading }) => {
3039
3419
  const theme = useTheme2();
3040
- const [selectedTeamId, setSelectedTeamId] = useState6("");
3041
- const [filterText, setFilterText] = useState6("");
3042
- const [copiedSnackbar, setCopiedSnackbar] = useState6(false);
3043
- const selectedTeam = useMemo6(
3420
+ const [selectedTeamId, setSelectedTeamId] = useState7("");
3421
+ const [filterText, setFilterText] = useState7("");
3422
+ const [copiedSnackbar, setCopiedSnackbar] = useState7(false);
3423
+ const selectedTeam = useMemo7(
3044
3424
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
3045
3425
  [teams, selectedTeamId]
3046
3426
  );
3047
- const teamModels = useMemo6(() => {
3427
+ const teamModels = useMemo7(() => {
3048
3428
  if (!selectedTeam) return [];
3049
3429
  return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
3050
3430
  }, [allModels, selectedTeam]);
3051
- const filteredModels = useMemo6(() => {
3431
+ const filteredModels = useMemo7(() => {
3052
3432
  const q = filterText.trim().toLowerCase();
3053
3433
  if (!q) return teamModels;
3054
3434
  return teamModels.filter(
@@ -3072,13 +3452,13 @@ var init_ModelsTable = __esm({
3072
3452
  return theme.palette.info.main;
3073
3453
  }
3074
3454
  };
3075
- return /* @__PURE__ */ React8.createElement(
3455
+ return /* @__PURE__ */ React9.createElement(
3076
3456
  SectionCard,
3077
3457
  {
3078
3458
  title: "Available Models",
3079
3459
  subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
3080
3460
  },
3081
- /* @__PURE__ */ React8.createElement(Box8, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ React8.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React8.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React8.createElement(
3461
+ /* @__PURE__ */ React9.createElement(Box9, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ React9.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React9.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React9.createElement(
3082
3462
  Select,
3083
3463
  {
3084
3464
  labelId: "team-select-label",
@@ -3086,8 +3466,8 @@ var init_ModelsTable = __esm({
3086
3466
  label: "Team",
3087
3467
  onChange: (e) => setSelectedTeamId(e.target.value)
3088
3468
  },
3089
- teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3090
- )), /* @__PURE__ */ React8.createElement(
3469
+ teams.map((t) => /* @__PURE__ */ React9.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3470
+ )), /* @__PURE__ */ React9.createElement(
3091
3471
  TextField5,
3092
3472
  {
3093
3473
  label: "Filter Models",
@@ -3097,33 +3477,33 @@ var init_ModelsTable = __esm({
3097
3477
  disabled: !selectedTeamId,
3098
3478
  sx: { minWidth: 240, mb: 2 },
3099
3479
  InputProps: {
3100
- startAdornment: /* @__PURE__ */ React8.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React8.createElement(Search2, { fontSize: "small" }))
3480
+ startAdornment: /* @__PURE__ */ React9.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React9.createElement(Search2, { fontSize: "small" }))
3101
3481
  }
3102
3482
  }
3103
3483
  )),
3104
- loading && /* @__PURE__ */ React8.createElement(EmptyState, { message: "Loading models\u2026" }),
3105
- !loading && !selectedTeamId && /* @__PURE__ */ React8.createElement(
3484
+ loading && /* @__PURE__ */ React9.createElement(EmptyState, { message: "Loading models\u2026" }),
3485
+ !loading && !selectedTeamId && /* @__PURE__ */ React9.createElement(
3106
3486
  EmptyState,
3107
3487
  {
3108
3488
  message: "Select a team to see available models",
3109
3489
  hint: "Each team may have a different set of models assigned"
3110
3490
  }
3111
3491
  ),
3112
- !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React8.createElement(
3492
+ !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React9.createElement(
3113
3493
  EmptyState,
3114
3494
  {
3115
3495
  message: "No models available for this team",
3116
3496
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
3117
3497
  }
3118
3498
  ),
3119
- !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React8.createElement(
3499
+ !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React9.createElement(
3120
3500
  EmptyState,
3121
3501
  {
3122
3502
  message: "No models match your search",
3123
3503
  hint: "Try a different search term"
3124
3504
  }
3125
3505
  ),
3126
- !loading && filteredModels.length > 0 && /* @__PURE__ */ React8.createElement(TableContainer5, null, /* @__PURE__ */ React8.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead5, null, /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, null, "Model ID"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Mode"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Input Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Output Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Input"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Output"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React8.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React8.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React8.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React8.createElement(
3506
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ React9.createElement(TableContainer5, null, /* @__PURE__ */ React9.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead5, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, null, "Model ID"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Mode"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Input Cost"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Output Cost"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Max Input"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Max Output"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React9.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React9.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(Box9, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React9.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React9.createElement(
3127
3507
  "span",
3128
3508
  {
3129
3509
  role: "button",
@@ -3133,9 +3513,9 @@ var init_ModelsTable = __esm({
3133
3513
  onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
3134
3514
  style: { cursor: "pointer", display: "inline-flex" }
3135
3515
  },
3136
- /* @__PURE__ */ React8.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3137
- )), /* @__PURE__ */ React8.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React8.createElement(
3138
- Box8,
3516
+ /* @__PURE__ */ React9.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3517
+ )), /* @__PURE__ */ React9.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React9.createElement(
3518
+ Box9,
3139
3519
  {
3140
3520
  component: "span",
3141
3521
  role: "button",
@@ -3145,9 +3525,9 @@ var init_ModelsTable = __esm({
3145
3525
  sx: quietIconButtonSx("accent"),
3146
3526
  style: { cursor: "pointer", display: "inline-flex" }
3147
3527
  },
3148
- /* @__PURE__ */ React8.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
3149
- )))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(
3150
- Box8,
3528
+ /* @__PURE__ */ React9.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
3529
+ )))), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(
3530
+ Box9,
3151
3531
  {
3152
3532
  component: "span",
3153
3533
  sx: {
@@ -3158,13 +3538,13 @@ var init_ModelsTable = __esm({
3158
3538
  fontSize: 11,
3159
3539
  fontWeight: 600,
3160
3540
  textTransform: "capitalize",
3161
- bgcolor: alpha5(modeColor(m.mode), 0.1),
3541
+ bgcolor: alpha6(modeColor(m.mode), 0.1),
3162
3542
  color: modeColor(m.mode)
3163
3543
  }
3164
3544
  },
3165
3545
  m.mode
3166
- )), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React8.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React8.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3167
- /* @__PURE__ */ React8.createElement(
3546
+ )), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(Box9, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React9.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React9.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3547
+ /* @__PURE__ */ React9.createElement(
3168
3548
  Snackbar,
3169
3549
  {
3170
3550
  open: copiedSnackbar,
@@ -3172,7 +3552,7 @@ var init_ModelsTable = __esm({
3172
3552
  onClose: () => setCopiedSnackbar(false),
3173
3553
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
3174
3554
  },
3175
- /* @__PURE__ */ React8.createElement(Alert3, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3555
+ /* @__PURE__ */ React9.createElement(Alert4, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3176
3556
  )
3177
3557
  );
3178
3558
  };
@@ -3180,9 +3560,9 @@ var init_ModelsTable = __esm({
3180
3560
  });
3181
3561
 
3182
3562
  // src/components/AuditLog.tsx
3183
- import React9, { useState as useState7, useCallback as useCallback2 } from "react";
3184
- import Box9 from "@mui/material/Box";
3185
- import Typography9 from "@mui/material/Typography";
3563
+ import React10, { useState as useState8, useCallback as useCallback2 } from "react";
3564
+ import Box10 from "@mui/material/Box";
3565
+ import Typography10 from "@mui/material/Typography";
3186
3566
  import Table6 from "@mui/material/Table";
3187
3567
  import TableBody6 from "@mui/material/TableBody";
3188
3568
  import TableCell6 from "@mui/material/TableCell";
@@ -3193,10 +3573,10 @@ import TablePagination from "@mui/material/TablePagination";
3193
3573
  import TextField6 from "@mui/material/TextField";
3194
3574
  import MenuItem5 from "@mui/material/MenuItem";
3195
3575
  import Skeleton5 from "@mui/material/Skeleton";
3196
- import Collapse2 from "@mui/material/Collapse";
3197
- import IconButton5 from "@mui/material/IconButton";
3198
- import Alert4 from "@mui/material/Alert";
3199
- import { alpha as alpha6 } from "@mui/material/styles";
3576
+ import Collapse3 from "@mui/material/Collapse";
3577
+ import IconButton6 from "@mui/material/IconButton";
3578
+ import Alert5 from "@mui/material/Alert";
3579
+ import { alpha as alpha7 } from "@mui/material/styles";
3200
3580
  import { KeyboardArrowDown } from "@mui/icons-material";
3201
3581
  import { useAsync as useAsync2 } from "react-use";
3202
3582
  function actionTone(action) {
@@ -3219,10 +3599,10 @@ function formatDateTime(iso) {
3219
3599
  }
3220
3600
  function renderAuditLogBody(loading, entries) {
3221
3601
  if (loading) {
3222
- return /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React9.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3602
+ return /* @__PURE__ */ React10.createElement(TableRow6, null, /* @__PURE__ */ React10.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React10.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3223
3603
  }
3224
3604
  if (entries.length === 0) {
3225
- return /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React9.createElement(
3605
+ return /* @__PURE__ */ React10.createElement(TableRow6, null, /* @__PURE__ */ React10.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React10.createElement(
3226
3606
  EmptyState,
3227
3607
  {
3228
3608
  message: "No audit events found",
@@ -3230,7 +3610,7 @@ function renderAuditLogBody(loading, entries) {
3230
3610
  }
3231
3611
  )));
3232
3612
  }
3233
- return entries.map((entry) => /* @__PURE__ */ React9.createElement(DetailRow, { key: entry.id, entry }));
3613
+ return entries.map((entry) => /* @__PURE__ */ React10.createElement(DetailRow, { key: entry.id, entry }));
3234
3614
  }
3235
3615
  var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
3236
3616
  var init_AuditLog = __esm({
@@ -3252,10 +3632,10 @@ var init_AuditLog = __esm({
3252
3632
  LiteLLM_UserTable: "User"
3253
3633
  };
3254
3634
  DetailRow = ({ entry }) => {
3255
- const [open, setOpen] = useState7(false);
3635
+ const [open, setOpen] = useState8(false);
3256
3636
  const hasDetail = entry.before_value || entry.updated_values;
3257
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React9.createElement(
3258
- IconButton5,
3637
+ return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(TableRow6, null, /* @__PURE__ */ React10.createElement(TableCell6, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React10.createElement(
3638
+ IconButton6,
3259
3639
  {
3260
3640
  size: "small",
3261
3641
  onClick: () => setOpen((o) => !o),
@@ -3267,9 +3647,9 @@ var init_AuditLog = __esm({
3267
3647
  transition: theme.transitions.create("transform")
3268
3648
  })
3269
3649
  },
3270
- /* @__PURE__ */ React9.createElement(KeyboardArrowDown, { fontSize: "small" })
3271
- )), /* @__PURE__ */ React9.createElement(TableCell6, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React9.createElement(TableCell6, null, entry.action && /* @__PURE__ */ React9.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React9.createElement(TableCell6, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React9.createElement(TableCell6, null, /* @__PURE__ */ React9.createElement(
3272
- Typography9,
3650
+ /* @__PURE__ */ React10.createElement(KeyboardArrowDown, { fontSize: "small" })
3651
+ )), /* @__PURE__ */ React10.createElement(TableCell6, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React10.createElement(TableCell6, null, entry.action && /* @__PURE__ */ React10.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React10.createElement(TableCell6, null, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React10.createElement(TableCell6, null, /* @__PURE__ */ React10.createElement(
3652
+ Typography10,
3273
3653
  {
3274
3654
  variant: "body2",
3275
3655
  component: "code",
@@ -3278,8 +3658,8 @@ var init_AuditLog = __esm({
3278
3658
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3279
3659
  },
3280
3660
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
3281
- )), /* @__PURE__ */ React9.createElement(TableCell6, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React9.createElement(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React9.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(
3282
- Box9,
3661
+ )), /* @__PURE__ */ React10.createElement(TableCell6, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React10.createElement(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React10.createElement(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React10.createElement(Collapse3, { in: open, unmountOnExit: true }, /* @__PURE__ */ React10.createElement(
3662
+ Box10,
3283
3663
  {
3284
3664
  display: "flex",
3285
3665
  gap: 2,
@@ -3288,11 +3668,11 @@ var init_AuditLog = __esm({
3288
3668
  p: 2,
3289
3669
  mb: 1.5,
3290
3670
  borderRadius: 1.5,
3291
- bgcolor: alpha6(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3671
+ bgcolor: alpha7(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3292
3672
  })
3293
3673
  },
3294
- entry.before_value && /* @__PURE__ */ React9.createElement(Box9, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React9.createElement(
3295
- Typography9,
3674
+ entry.before_value && /* @__PURE__ */ React10.createElement(Box10, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React10.createElement(
3675
+ Typography10,
3296
3676
  {
3297
3677
  variant: "caption",
3298
3678
  color: "text.secondary",
@@ -3301,9 +3681,9 @@ var init_AuditLog = __esm({
3301
3681
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3302
3682
  },
3303
3683
  "Before"
3304
- ), /* @__PURE__ */ React9.createElement(Typography9, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
3305
- entry.updated_values && /* @__PURE__ */ React9.createElement(Box9, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React9.createElement(
3306
- Typography9,
3684
+ ), /* @__PURE__ */ React10.createElement(Typography10, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
3685
+ entry.updated_values && /* @__PURE__ */ React10.createElement(Box10, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React10.createElement(
3686
+ Typography10,
3307
3687
  {
3308
3688
  variant: "caption",
3309
3689
  color: "text.secondary",
@@ -3312,13 +3692,13 @@ var init_AuditLog = __esm({
3312
3692
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3313
3693
  },
3314
3694
  "After"
3315
- ), /* @__PURE__ */ React9.createElement(Typography9, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
3695
+ ), /* @__PURE__ */ React10.createElement(Typography10, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
3316
3696
  )))));
3317
3697
  };
3318
3698
  AuditLog = ({ api }) => {
3319
- const [page, setPage] = useState7(0);
3320
- const [pageSize, setPageSize] = useState7(25);
3321
- const [filters, setFilters] = useState7({});
3699
+ const [page, setPage] = useState8(0);
3700
+ const [pageSize, setPageSize] = useState8(25);
3701
+ const [filters, setFilters] = useState8({});
3322
3702
  const fetchParams = useCallback2(
3323
3703
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3324
3704
  [page, pageSize, filters]
@@ -3329,13 +3709,13 @@ var init_AuditLog = __esm({
3329
3709
  );
3330
3710
  const entries = value?.audit_logs ?? [];
3331
3711
  const total = value?.total ?? 0;
3332
- return /* @__PURE__ */ React9.createElement(
3712
+ return /* @__PURE__ */ React10.createElement(
3333
3713
  SectionCard,
3334
3714
  {
3335
3715
  title: "Audit Log",
3336
3716
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3337
3717
  flush: true,
3338
- actions: /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
3718
+ actions: /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
3339
3719
  TextField6,
3340
3720
  {
3341
3721
  size: "small",
@@ -3348,12 +3728,12 @@ var init_AuditLog = __esm({
3348
3728
  },
3349
3729
  sx: { minWidth: 150 }
3350
3730
  },
3351
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All actions"),
3352
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "created" }, "Created"),
3353
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "updated" }, "Updated"),
3354
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3355
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3356
- ), /* @__PURE__ */ React9.createElement(
3731
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "" }, "All actions"),
3732
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "created" }, "Created"),
3733
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "updated" }, "Updated"),
3734
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3735
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3736
+ ), /* @__PURE__ */ React10.createElement(
3357
3737
  TextField6,
3358
3738
  {
3359
3739
  size: "small",
@@ -3366,13 +3746,13 @@ var init_AuditLog = __esm({
3366
3746
  },
3367
3747
  sx: { minWidth: 150 }
3368
3748
  },
3369
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All tables"),
3370
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
3371
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
3372
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
3373
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3374
- /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3375
- ), /* @__PURE__ */ React9.createElement(
3749
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "" }, "All tables"),
3750
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
3751
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
3752
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
3753
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3754
+ /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3755
+ ), /* @__PURE__ */ React10.createElement(
3376
3756
  TextField6,
3377
3757
  {
3378
3758
  size: "small",
@@ -3386,9 +3766,9 @@ var init_AuditLog = __esm({
3386
3766
  }
3387
3767
  ))
3388
3768
  },
3389
- error && /* @__PURE__ */ React9.createElement(Box9, { px: 2.5, pb: 2 }, /* @__PURE__ */ React9.createElement(Alert4, { severity: "error" }, error.message)),
3390
- /* @__PURE__ */ React9.createElement(TableContainer6, null, /* @__PURE__ */ React9.createElement(Table6, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead6, null, /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { sx: { width: 40 } }), /* @__PURE__ */ React9.createElement(TableCell6, null, "Time"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Action"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Table"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Object ID"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Changed By"))), /* @__PURE__ */ React9.createElement(TableBody6, null, renderAuditLogBody(loading, entries)))),
3391
- /* @__PURE__ */ React9.createElement(
3769
+ error && /* @__PURE__ */ React10.createElement(Box10, { px: 2.5, pb: 2 }, /* @__PURE__ */ React10.createElement(Alert5, { severity: "error" }, error.message)),
3770
+ /* @__PURE__ */ React10.createElement(TableContainer6, null, /* @__PURE__ */ React10.createElement(Table6, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React10.createElement(TableHead6, null, /* @__PURE__ */ React10.createElement(TableRow6, null, /* @__PURE__ */ React10.createElement(TableCell6, { sx: { width: 40 } }), /* @__PURE__ */ React10.createElement(TableCell6, null, "Time"), /* @__PURE__ */ React10.createElement(TableCell6, null, "Action"), /* @__PURE__ */ React10.createElement(TableCell6, null, "Table"), /* @__PURE__ */ React10.createElement(TableCell6, null, "Object ID"), /* @__PURE__ */ React10.createElement(TableCell6, null, "Changed By"))), /* @__PURE__ */ React10.createElement(TableBody6, null, renderAuditLogBody(loading, entries)))),
3771
+ /* @__PURE__ */ React10.createElement(
3392
3772
  TablePagination,
3393
3773
  {
3394
3774
  component: "div",
@@ -3443,17 +3823,18 @@ var LiteLLMPage_exports = {};
3443
3823
  __export(LiteLLMPage_exports, {
3444
3824
  LiteLLMPage: () => LiteLLMPage
3445
3825
  });
3446
- import React10, { useState as useState8, useCallback as useCallback3, useMemo as useMemo7 } from "react";
3447
- import Box10 from "@mui/material/Box";
3826
+ import React11, { useState as useState9, useCallback as useCallback3, useMemo as useMemo8 } from "react";
3827
+ import Box11 from "@mui/material/Box";
3448
3828
  import Snackbar2 from "@mui/material/Snackbar";
3449
- import Alert5 from "@mui/material/Alert";
3450
- import CircularProgress5 from "@mui/material/CircularProgress";
3451
- import Typography10 from "@mui/material/Typography";
3452
- import Paper6 from "@mui/material/Paper";
3829
+ import Alert6 from "@mui/material/Alert";
3830
+ import CircularProgress6 from "@mui/material/CircularProgress";
3831
+ import Typography11 from "@mui/material/Typography";
3832
+ import Paper7 from "@mui/material/Paper";
3453
3833
  import Tabs2 from "@mui/material/Tabs";
3454
3834
  import Tab2 from "@mui/material/Tab";
3835
+ import Grid2 from "@mui/material/Grid";
3455
3836
  import { useAsync as useAsync3, useAsyncRetry } from "react-use";
3456
- import { useApi as useApi2 } from "@backstage/core-plugin-api";
3837
+ import { useApi as useApi3 } from "@backstage/core-plugin-api";
3457
3838
  import { usePermission } from "@backstage/plugin-permission-react";
3458
3839
  function initDateRange() {
3459
3840
  let preset = "7d";
@@ -3478,6 +3859,7 @@ var init_LiteLLMPage = __esm({
3478
3859
  init_GenerateKeyDialog();
3479
3860
  init_ManageTeamDialog();
3480
3861
  init_UsageStats();
3862
+ init_LiteLLMBudgetWidget();
3481
3863
  init_TeamUsage();
3482
3864
  init_ModelsTable();
3483
3865
  init_AuditLog();
@@ -3485,21 +3867,21 @@ var init_LiteLLMPage = __esm({
3485
3867
  init_permissions();
3486
3868
  PERIOD_LS_KEY2 = "litellm_usage_period";
3487
3869
  LiteLLMPage = () => {
3488
- const api = useApi2(liteLlmApiRef);
3489
- const [dateRange, setDateRange] = useState8(initDateRange);
3490
- const [currentPreset, setCurrentPreset] = useState8(() => {
3870
+ const api = useApi3(liteLlmApiRef);
3871
+ const [dateRange, setDateRange] = useState9(initDateRange);
3872
+ const [currentPreset, setCurrentPreset] = useState9(() => {
3491
3873
  try {
3492
3874
  return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
3493
3875
  } catch {
3494
3876
  return "7d";
3495
3877
  }
3496
3878
  });
3497
- const [activeTab, setActiveTab] = useState8("overview");
3498
- const [snackbar, setSnackbar] = useState8(null);
3499
- const [generateDialogOpen, setGenerateDialogOpen] = useState8(false);
3500
- const [manageTeam, setManageTeam] = useState8(null);
3501
- const [teamUsageCache, setTeamUsageCache] = useState8({});
3502
- const [teamUsageLoading, setTeamUsageLoading] = useState8({});
3879
+ const [activeTab, setActiveTab] = useState9("overview");
3880
+ const [snackbar, setSnackbar] = useState9(null);
3881
+ const [generateDialogOpen, setGenerateDialogOpen] = useState9(false);
3882
+ const [manageTeam, setManageTeam] = useState9(null);
3883
+ const [teamUsageCache, setTeamUsageCache] = useState9({});
3884
+ const [teamUsageLoading, setTeamUsageLoading] = useState9({});
3503
3885
  const { value: userInfo, loading: userLoading, error: userError } = useAsync3(
3504
3886
  () => api.getUserInfo(),
3505
3887
  [api]
@@ -3543,7 +3925,7 @@ var init_LiteLLMPage = __esm({
3543
3925
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
3544
3926
  [api, objectPermsEnabled]
3545
3927
  );
3546
- const teams = useMemo7(() => {
3928
+ const teams = useMemo8(() => {
3547
3929
  if (!allTeams?.length) return [];
3548
3930
  if (!userInfo) return allTeams;
3549
3931
  const userId = userInfo.user_id;
@@ -3582,7 +3964,7 @@ var init_LiteLLMPage = __esm({
3582
3964
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
3583
3965
  }
3584
3966
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
3585
- const allowedModels = useMemo7(() => {
3967
+ const allowedModels = useMemo8(() => {
3586
3968
  if (!allModels?.length) return [];
3587
3969
  const userModels = userInfo?.models;
3588
3970
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -3690,14 +4072,14 @@ var init_LiteLLMPage = __esm({
3690
4072
  );
3691
4073
  const isInitialLoading = userLoading && !userInfo;
3692
4074
  if (isInitialLoading) {
3693
- return /* @__PURE__ */ React10.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React10.createElement(CircularProgress5, null));
4075
+ return /* @__PURE__ */ React11.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React11.createElement(CircularProgress6, null));
3694
4076
  }
3695
4077
  if (userError || !userInfo) {
3696
4078
  const isProvisioningEnabled = userError?.body?.provisioning === true;
3697
4079
  const hint = userError?.body?.hint;
3698
- return /* @__PURE__ */ React10.createElement(Box10, { p: 3 }, /* @__PURE__ */ React10.createElement(Paper6, { sx: { p: 3 } }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React10.createElement(Typography10, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
4080
+ return /* @__PURE__ */ React11.createElement(Box11, { p: 3 }, /* @__PURE__ */ React11.createElement(Paper7, { sx: { p: 3 } }, /* @__PURE__ */ React11.createElement(Typography11, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React11.createElement(Typography11, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React11.createElement(Typography11, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React11.createElement(Typography11, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
3699
4081
  }
3700
- const pageTabs = /* @__PURE__ */ React10.createElement(
4082
+ const pageTabs = /* @__PURE__ */ React11.createElement(
3701
4083
  Tabs2,
3702
4084
  {
3703
4085
  value: activeTab,
@@ -3710,13 +4092,13 @@ var init_LiteLLMPage = __esm({
3710
4092
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
3711
4093
  }
3712
4094
  },
3713
- /* @__PURE__ */ React10.createElement(Tab2, { label: "Overview", value: "overview" }),
3714
- /* @__PURE__ */ React10.createElement(Tab2, { label: "Keys", value: "keys" }),
3715
- /* @__PURE__ */ React10.createElement(Tab2, { label: "Teams", value: "teams" }),
3716
- /* @__PURE__ */ React10.createElement(Tab2, { label: "Models", value: "models" }),
3717
- userInfo.can_view_audit && /* @__PURE__ */ React10.createElement(Tab2, { label: "Audit Log", value: "audit" })
4095
+ /* @__PURE__ */ React11.createElement(Tab2, { label: "Overview", value: "overview" }),
4096
+ /* @__PURE__ */ React11.createElement(Tab2, { label: "Keys", value: "keys" }),
4097
+ /* @__PURE__ */ React11.createElement(Tab2, { label: "Teams", value: "teams" }),
4098
+ /* @__PURE__ */ React11.createElement(Tab2, { label: "Models", value: "models" }),
4099
+ userInfo.can_view_audit && /* @__PURE__ */ React11.createElement(Tab2, { label: "Audit Log", value: "audit" })
3718
4100
  );
3719
- return /* @__PURE__ */ React10.createElement(Box10, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React10.createElement(
4101
+ return /* @__PURE__ */ React11.createElement(Box11, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React11.createElement(
3720
4102
  DashboardHeader,
3721
4103
  {
3722
4104
  userInfo,
@@ -3726,7 +4108,7 @@ var init_LiteLLMPage = __esm({
3726
4108
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
3727
4109
  tabs: pageTabs
3728
4110
  }
3729
- ), activeTab === "overview" && /* @__PURE__ */ React10.createElement(
4111
+ ), activeTab === "overview" && /* @__PURE__ */ React11.createElement(Grid2, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ React11.createElement(
3730
4112
  UsageStats,
3731
4113
  {
3732
4114
  usage: usage ?? null,
@@ -3737,7 +4119,7 @@ var init_LiteLLMPage = __esm({
3737
4119
  loading: usageLoading,
3738
4120
  userInfo
3739
4121
  }
3740
- ), activeTab === "keys" && /* @__PURE__ */ React10.createElement(
4122
+ )), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ React11.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ React11.createElement(
3741
4123
  KeysTable,
3742
4124
  {
3743
4125
  keys: keys ?? [],
@@ -3756,7 +4138,7 @@ var init_LiteLLMPage = __esm({
3756
4138
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
3757
4139
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
3758
4140
  const visibleTeams = Array.from(teamsById.values());
3759
- return /* @__PURE__ */ React10.createElement(
4141
+ return /* @__PURE__ */ React11.createElement(
3760
4142
  TeamUsage,
3761
4143
  {
3762
4144
  teams: visibleTeams,
@@ -3772,14 +4154,14 @@ var init_LiteLLMPage = __esm({
3772
4154
  onCreateTeam: () => setManageTeam({ mode: "create" })
3773
4155
  }
3774
4156
  );
3775
- })(), activeTab === "models" && /* @__PURE__ */ React10.createElement(
4157
+ })(), activeTab === "models" && /* @__PURE__ */ React11.createElement(
3776
4158
  ModelsTable,
3777
4159
  {
3778
4160
  allModels: allModels ?? [],
3779
4161
  teams: teams ?? [],
3780
4162
  loading: modelsLoading
3781
4163
  }
3782
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React10.createElement(AuditLog, { api }), /* @__PURE__ */ React10.createElement(
4164
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React11.createElement(AuditLog, { api }), /* @__PURE__ */ React11.createElement(
3783
4165
  GenerateKeyDialog,
3784
4166
  {
3785
4167
  open: generateDialogOpen,
@@ -3792,7 +4174,7 @@ var init_LiteLLMPage = __esm({
3792
4174
  onGenerateKey: handleGenerateKey,
3793
4175
  onGetConfig: () => api.getConfig()
3794
4176
  }
3795
- ), /* @__PURE__ */ React10.createElement(
4177
+ ), /* @__PURE__ */ React11.createElement(
3796
4178
  ManageTeamDialog,
3797
4179
  {
3798
4180
  open: !!manageTeam,
@@ -3854,7 +4236,7 @@ var init_LiteLLMPage = __esm({
3854
4236
  refreshTeams();
3855
4237
  }
3856
4238
  }
3857
- ), /* @__PURE__ */ React10.createElement(
4239
+ ), /* @__PURE__ */ React11.createElement(
3858
4240
  Snackbar2,
3859
4241
  {
3860
4242
  open: !!snackbar,
@@ -3862,7 +4244,7 @@ var init_LiteLLMPage = __esm({
3862
4244
  onClose: () => setSnackbar(null),
3863
4245
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
3864
4246
  },
3865
- snackbar ? /* @__PURE__ */ React10.createElement(Alert5, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4247
+ snackbar ? /* @__PURE__ */ React11.createElement(Alert6, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
3866
4248
  ));
3867
4249
  };
3868
4250
  }
@@ -3870,7 +4252,7 @@ var init_LiteLLMPage = __esm({
3870
4252
 
3871
4253
  // src/plugin.tsx
3872
4254
  init_api();
3873
- import React11 from "react";
4255
+ import React12 from "react";
3874
4256
  import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
3875
4257
  import {
3876
4258
  createFrontendPlugin,
@@ -3889,10 +4271,10 @@ var liteLlmPage = PageBlueprint.make({
3889
4271
  params: {
3890
4272
  path: "/litellm",
3891
4273
  title: "LiteLLM",
3892
- icon: /* @__PURE__ */ React11.createElement(TrendingUpIcon, null),
4274
+ icon: /* @__PURE__ */ React12.createElement(TrendingUpIcon, null),
3893
4275
  loader: async () => {
3894
4276
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
3895
- return /* @__PURE__ */ React11.createElement(LiteLLMPage2, null);
4277
+ return /* @__PURE__ */ React12.createElement(LiteLLMPage2, null);
3896
4278
  }
3897
4279
  }
3898
4280
  });
@@ -3911,18 +4293,18 @@ init_TeamUsage();
3911
4293
  // src/components/LiteLLMHomeWidget.tsx
3912
4294
  init_api();
3913
4295
  init_format();
3914
- import React12, { useState as useState9, useEffect as useEffect3 } from "react";
3915
- import Paper7 from "@mui/material/Paper";
3916
- import Box11 from "@mui/material/Box";
3917
- import Typography11 from "@mui/material/Typography";
4296
+ import React13, { useState as useState10, useEffect as useEffect4 } from "react";
4297
+ import Paper8 from "@mui/material/Paper";
4298
+ import Box12 from "@mui/material/Box";
4299
+ import Typography12 from "@mui/material/Typography";
3918
4300
  import FormControl2 from "@mui/material/FormControl";
3919
4301
  import Select2 from "@mui/material/Select";
3920
4302
  import MenuItem6 from "@mui/material/MenuItem";
3921
- import Grid2 from "@mui/material/Grid";
3922
- import CircularProgress6 from "@mui/material/CircularProgress";
3923
- import Alert6 from "@mui/material/Alert";
4303
+ import Grid3 from "@mui/material/Grid";
4304
+ import CircularProgress7 from "@mui/material/CircularProgress";
4305
+ import Alert7 from "@mui/material/Alert";
3924
4306
  import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
3925
- import { useApi as useApi3 } from "@backstage/core-plugin-api";
4307
+ import { useApi as useApi4 } from "@backstage/core-plugin-api";
3926
4308
  function presetToDateRange(preset) {
3927
4309
  const end = /* @__PURE__ */ new Date();
3928
4310
  const start = /* @__PURE__ */ new Date();
@@ -3935,19 +4317,19 @@ function presetToDateRange(preset) {
3935
4317
  }
3936
4318
  return { start, end };
3937
4319
  }
3938
- var Kpi = ({ label, value }) => /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Typography11, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React12.createElement(Typography11, { variant: "subtitle1", fontWeight: 600 }, value));
4320
+ var Kpi = ({ label, value }) => /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Typography12, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React13.createElement(Typography12, { variant: "subtitle1", fontWeight: 600 }, value));
3939
4321
  var LiteLLMHomeWidget = ({
3940
4322
  defaultPeriod = "7d",
3941
4323
  title = "LiteLLM Usage"
3942
4324
  }) => {
3943
- const api = useApi3(liteLlmApiRef);
3944
- const [period, setPeriod] = useState9(defaultPeriod);
3945
- const [loading, setLoading] = useState9(true);
3946
- const [usageError, setUsageError] = useState9(null);
3947
- const [keysError, setKeysError] = useState9(null);
3948
- const [usage, setUsage] = useState9(null);
3949
- const [keys, setKeys] = useState9([]);
3950
- useEffect3(() => {
4325
+ const api = useApi4(liteLlmApiRef);
4326
+ const [period, setPeriod] = useState10(defaultPeriod);
4327
+ const [loading, setLoading] = useState10(true);
4328
+ const [usageError, setUsageError] = useState10(null);
4329
+ const [keysError, setKeysError] = useState10(null);
4330
+ const [usage, setUsage] = useState10(null);
4331
+ const [keys, setKeys] = useState10([]);
4332
+ useEffect4(() => {
3951
4333
  let cancelled = false;
3952
4334
  setLoading(true);
3953
4335
  setUsageError(null);
@@ -3984,17 +4366,17 @@ var LiteLLMHomeWidget = ({
3984
4366
  spend: d.spend
3985
4367
  }));
3986
4368
  const hasSparkline = dailyData.length > 0;
3987
- return /* @__PURE__ */ React12.createElement(Paper7, { sx: { p: 2 } }, /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React12.createElement(Typography11, { variant: "h6" }, title), /* @__PURE__ */ React12.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React12.createElement(
4369
+ return /* @__PURE__ */ React13.createElement(Paper8, { sx: { p: 2 } }, /* @__PURE__ */ React13.createElement(Box12, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React13.createElement(Typography12, { variant: "h6" }, title), /* @__PURE__ */ React13.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React13.createElement(
3988
4370
  Select2,
3989
4371
  {
3990
4372
  value: period,
3991
4373
  onChange: (e) => setPeriod(e.target.value),
3992
4374
  displayEmpty: true
3993
4375
  },
3994
- /* @__PURE__ */ React12.createElement(MenuItem6, { value: "today" }, "Today"),
3995
- /* @__PURE__ */ React12.createElement(MenuItem6, { value: "7d" }, "7d"),
3996
- /* @__PURE__ */ React12.createElement(MenuItem6, { value: "30d" }, "30d")
3997
- ))), loading && /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React12.createElement(CircularProgress6, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React12.createElement(React12.Fragment, null, partialFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React12.createElement(Grid2, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React12.createElement(Box11, { height: 120 }, /* @__PURE__ */ React12.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React12.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React12.createElement(
4376
+ /* @__PURE__ */ React13.createElement(MenuItem6, { value: "today" }, "Today"),
4377
+ /* @__PURE__ */ React13.createElement(MenuItem6, { value: "7d" }, "7d"),
4378
+ /* @__PURE__ */ React13.createElement(MenuItem6, { value: "30d" }, "30d")
4379
+ ))), loading && /* @__PURE__ */ React13.createElement(Box12, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React13.createElement(CircularProgress7, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React13.createElement(Alert7, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React13.createElement(React13.Fragment, null, partialFailure && /* @__PURE__ */ React13.createElement(Alert7, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React13.createElement(Grid3, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React13.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React13.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React13.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React13.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React13.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React13.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React13.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React13.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React13.createElement(Box12, { height: 120 }, /* @__PURE__ */ React13.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React13.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React13.createElement(
3998
4380
  Area3,
3999
4381
  {
4000
4382
  type: "monotone",
@@ -4008,281 +4390,8 @@ var LiteLLMHomeWidget = ({
4008
4390
  ))))));
4009
4391
  };
4010
4392
 
4011
- // src/components/LiteLLMBudgetWidget.tsx
4012
- init_api();
4013
- init_format();
4014
- init_ui();
4015
- import React13, { useEffect as useEffect4, useMemo as useMemo8, useState as useState10 } from "react";
4016
- import Paper8 from "@mui/material/Paper";
4017
- import Box12 from "@mui/material/Box";
4018
- import Typography12 from "@mui/material/Typography";
4019
- import CircularProgress7 from "@mui/material/CircularProgress";
4020
- import Alert7 from "@mui/material/Alert";
4021
- import { alpha as alpha7 } from "@mui/material/styles";
4022
- import { useApi as useApi4 } from "@backstage/core-plugin-api";
4023
-
4024
- // src/budget.ts
4025
- function limitPct(spend, budget) {
4026
- if (budget <= 0) return 0;
4027
- return spend / budget * 100;
4028
- }
4029
- function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
4030
- const budgeted = keys.filter((k) => (k.max_budget ?? 0) > 0).map((k) => {
4031
- const spend = k.spend ?? 0;
4032
- return {
4033
- kind: "key",
4034
- label: k.key_alias || k.key || "Untitled key",
4035
- sublabel: k.key,
4036
- budget: k.max_budget,
4037
- spend,
4038
- budgetDuration: k.budget_duration,
4039
- pct: limitPct(spend, k.max_budget)
4040
- };
4041
- }).sort((a, b) => b.pct - a.pct);
4042
- const userBudget = user && (user.max_budget ?? user.hard_limit ?? 0) > 0 ? (() => {
4043
- const budget = user.max_budget ?? user.hard_limit ?? 0;
4044
- const spend = user.current_spend ?? user.spend ?? 0;
4045
- return {
4046
- kind: "user",
4047
- label: "Personal budget",
4048
- sublabel: user.user_email || user.user_id,
4049
- budget,
4050
- spend,
4051
- budgetDuration: user.budget_duration,
4052
- softLimit: user.soft_limit,
4053
- pct: limitPct(spend, budget)
4054
- };
4055
- })() : void 0;
4056
- const teamLimits = teams.filter((t) => (t.max_budget ?? 0) > 0).map((t) => {
4057
- const spend = t.spend ?? 0;
4058
- return {
4059
- kind: "team",
4060
- label: t.team_alias || "Untitled team",
4061
- sublabel: t.team_id,
4062
- budget: t.max_budget,
4063
- spend,
4064
- budgetDuration: t.budget_duration,
4065
- pct: limitPct(spend, t.max_budget)
4066
- };
4067
- }).sort((a, b) => b.pct - a.pct);
4068
- return {
4069
- keys: budgeted.slice(0, maxKeys),
4070
- hiddenBudgetedKeys: Math.max(0, budgeted.length - maxKeys),
4071
- totalKeyCount: keys.length,
4072
- user: userBudget,
4073
- teams: teamLimits
4074
- };
4075
- }
4076
- function fmtBudgetDuration(duration) {
4077
- if (!duration) return null;
4078
- const m = /^(\d+)(s|m|h|d)$/.exec(duration);
4079
- if (!m) return duration;
4080
- const n = Number(m[1]);
4081
- const unit = m[2];
4082
- if (unit === "d") return n === 1 ? "daily" : `every ${n} days`;
4083
- if (unit === "h") return n === 1 ? "hourly" : `every ${n} hours`;
4084
- if (unit === "m") return n === 1 ? "every minute" : `every ${n} minutes`;
4085
- return n === 1 ? "every second" : `every ${n} seconds`;
4086
- }
4087
- function budgetTone3(pct) {
4088
- if (pct >= 100) return "danger";
4089
- if (pct >= 80) return "warning";
4090
- return "accent";
4091
- }
4092
-
4093
- // src/components/LiteLLMBudgetWidget.tsx
4094
- function levelToneColor(tone, theme) {
4095
- switch (tone) {
4096
- case "success":
4097
- return theme.palette.success.main;
4098
- case "warning":
4099
- return theme.palette.warning.main;
4100
- case "danger":
4101
- return theme.palette.error.main;
4102
- case "info":
4103
- return theme.palette.info.main;
4104
- default:
4105
- return theme.palette.primary.main;
4106
- }
4107
- }
4108
- var LevelFrame = ({
4109
- rank,
4110
- name,
4111
- tagline,
4112
- note,
4113
- tone,
4114
- isLast,
4115
- children
4116
- }) => /* @__PURE__ */ React13.createElement(Box12, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ React13.createElement(Box12, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ React13.createElement(
4117
- Box12,
4118
- {
4119
- sx: (theme) => ({
4120
- width: 26,
4121
- height: 26,
4122
- borderRadius: "50%",
4123
- display: "flex",
4124
- alignItems: "center",
4125
- justifyContent: "center",
4126
- bgcolor: alpha7(levelToneColor(tone, theme), 0.14),
4127
- color: levelToneColor(tone, theme),
4128
- fontSize: 12,
4129
- fontWeight: 700,
4130
- flexShrink: 0
4131
- })
4132
- },
4133
- rank
4134
- ), /* @__PURE__ */ React13.createElement(
4135
- Box12,
4136
- {
4137
- sx: (theme) => ({
4138
- flex: 1,
4139
- width: 2,
4140
- minHeight: 14,
4141
- bgcolor: isLast ? "transparent" : alpha7(theme.palette.text.primary, 0.12)
4142
- })
4143
- }
4144
- )), /* @__PURE__ */ React13.createElement(Box12, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ React13.createElement(Box12, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ React13.createElement(
4145
- Typography12,
4146
- {
4147
- sx: (theme) => ({
4148
- fontSize: 12.5,
4149
- fontWeight: 700,
4150
- letterSpacing: "0.08em",
4151
- textTransform: "uppercase",
4152
- color: levelToneColor(tone, theme)
4153
- })
4154
- },
4155
- name
4156
- ), /* @__PURE__ */ React13.createElement(Typography12, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ React13.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ React13.createElement(Box12, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
4157
- var EmptyLimitCard = ({ children }) => /* @__PURE__ */ React13.createElement(
4158
- Paper8,
4159
- {
4160
- variant: "outlined",
4161
- sx: (theme) => ({
4162
- px: 1.5,
4163
- py: 1.25,
4164
- borderRadius: 1.5,
4165
- bgcolor: alpha7(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
4166
- })
4167
- },
4168
- /* @__PURE__ */ React13.createElement(Typography12, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
4169
- );
4170
- var LimitCard = ({ limit }) => {
4171
- const tone = budgetTone3(limit.pct);
4172
- const pct = Math.round(limit.pct);
4173
- const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
4174
- return /* @__PURE__ */ React13.createElement(Paper8, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ React13.createElement(Box12, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ React13.createElement(Box12, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React13.createElement(
4175
- Typography12,
4176
- {
4177
- variant: "body2",
4178
- sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
4179
- title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
4180
- },
4181
- limit.label
4182
- ), limit.sublabel && /* @__PURE__ */ React13.createElement(
4183
- Typography12,
4184
- {
4185
- variant: "caption",
4186
- color: "text.secondary",
4187
- sx: {
4188
- fontSize: 10.5,
4189
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
4190
- display: "block",
4191
- overflow: "hidden",
4192
- textOverflow: "ellipsis",
4193
- whiteSpace: "nowrap"
4194
- }
4195
- },
4196
- limit.sublabel
4197
- )), /* @__PURE__ */ React13.createElement(Typography12, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ React13.createElement(Box12, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ React13.createElement(Box12, { sx: { mt: 1 } }, /* @__PURE__ */ React13.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ React13.createElement(
4198
- Box12,
4199
- {
4200
- sx: {
4201
- display: "flex",
4202
- justifyContent: "space-between",
4203
- alignItems: "center",
4204
- gap: 1,
4205
- mt: 0.75
4206
- }
4207
- },
4208
- /* @__PURE__ */ React13.createElement(Typography12, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`),
4209
- /* @__PURE__ */ React13.createElement(Typography12, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
4210
- ));
4211
- };
4212
- var LiteLLMBudgetWidget = ({
4213
- title = "Budget Policy",
4214
- maxKeys = 3
4215
- }) => {
4216
- const api = useApi4(liteLlmApiRef);
4217
- const [loading, setLoading] = useState10(true);
4218
- const [error, setError] = useState10(null);
4219
- const [user, setUser] = useState10(null);
4220
- const [teams, setTeams] = useState10([]);
4221
- const [keys, setKeys] = useState10([]);
4222
- useEffect4(() => {
4223
- let cancelled = false;
4224
- setLoading(true);
4225
- setError(null);
4226
- Promise.allSettled([api.getUserInfo(), api.getTeams(), api.listKeys()]).then(
4227
- ([userResult, teamsResult, keysResult]) => {
4228
- if (cancelled) return;
4229
- if (userResult.status === "fulfilled") {
4230
- setUser(userResult.value);
4231
- } else {
4232
- setUser(null);
4233
- setError(
4234
- userResult.reason?.message ?? "Failed to load your LiteLLM profile"
4235
- );
4236
- }
4237
- setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
4238
- setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
4239
- setLoading(false);
4240
- }
4241
- );
4242
- return () => {
4243
- cancelled = true;
4244
- };
4245
- }, [api]);
4246
- const hasAnyLimit = !!user && (user.max_budget ?? user.hard_limit ?? 0) > 0;
4247
- const summary = useMemo8(() => buildBudgetSummary(user, teams, keys, maxKeys), [user, teams, keys, maxKeys]);
4248
- const hasBudgetedKeys = keys.some((k) => (k.max_budget ?? 0) > 0);
4249
- return /* @__PURE__ */ React13.createElement(Paper8, { sx: { p: 2 } }, /* @__PURE__ */ React13.createElement(Box12, { sx: { mb: 1.5 } }, /* @__PURE__ */ React13.createElement(Typography12, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ React13.createElement(Typography12, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), loading && /* @__PURE__ */ React13.createElement(Box12, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ React13.createElement(CircularProgress7, { size: 32 })), !loading && error && /* @__PURE__ */ React13.createElement(Alert7, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
4250
- LevelFrame,
4251
- {
4252
- rank: 1,
4253
- name: "Key",
4254
- tagline: "per-key cap \xB7 highest priority",
4255
- note: hasBudgetedKeys ? void 0 : "no key budgets set"
4256
- },
4257
- !hasBudgetedKeys && /* @__PURE__ */ React13.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
4258
- summary.keys.map((k) => /* @__PURE__ */ React13.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
4259
- summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React13.createElement(Typography12, { variant: "caption", color: "text.secondary" }, "+", summary.hiddenBudgetedKeys, " more budgeted key", summary.hiddenBudgetedKeys > 1 ? "s" : "", " further from the cap")
4260
- ), /* @__PURE__ */ React13.createElement(
4261
- LevelFrame,
4262
- {
4263
- rank: 2,
4264
- name: "User",
4265
- tagline: "cap on everything you spend with your own keys",
4266
- note: "skipped for team-bound keys"
4267
- },
4268
- hasAnyLimit && summary.user ? /* @__PURE__ */ React13.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React13.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
4269
- ), /* @__PURE__ */ React13.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ React13.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React13.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React13.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React13.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ React13.createElement(
4270
- Box12,
4271
- {
4272
- sx: (theme) => ({
4273
- mt: 2,
4274
- px: 1.5,
4275
- py: 1.25,
4276
- borderRadius: 1.5,
4277
- border: "1px dashed",
4278
- borderColor: alpha7(theme.palette.text.primary, 0.18)
4279
- })
4280
- },
4281
- /* @__PURE__ */ React13.createElement(Typography12, { variant: "caption", color: "text.secondary", display: "block" }, "The first cap a request runs into is the one that stops it. A limit with a reset window (e.g. daily or every 30 days) clears its spend to $0 when the window closes; without a window it never resets.")
4282
- )));
4283
- };
4284
-
4285
4393
  // src/index.ts
4394
+ init_LiteLLMBudgetWidget();
4286
4395
  init_GenerateKeyDialog();
4287
4396
  init_ManageTeamDialog();
4288
4397
  init_api();