@acarmisc/backstage-plugin-litellm 0.24.0 → 0.25.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
@@ -2924,6 +2924,13 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
2924
2924
  teams: teamLimits
2925
2925
  };
2926
2926
  }
2927
+ function allBudgetLimits(summary) {
2928
+ return [
2929
+ ...summary.keys,
2930
+ ...summary.user ? [summary.user] : [],
2931
+ ...summary.teams
2932
+ ];
2933
+ }
2927
2934
  function buildBudgetGauges(summary) {
2928
2935
  const byLevel = {
2929
2936
  key: summary.keys,
@@ -2974,18 +2981,12 @@ var init_budget = __esm({
2974
2981
  }
2975
2982
  });
2976
2983
 
2977
- // src/components/LiteLLMBudgetWidget.tsx
2978
- import React7, { useEffect as useEffect3, useMemo as useMemo6, useState as useState5 } from "react";
2979
- import Paper5 from "@mui/material/Paper";
2984
+ // src/components/BudgetLimitList.tsx
2985
+ import React7 from "react";
2980
2986
  import Box7 from "@mui/material/Box";
2987
+ import Paper5 from "@mui/material/Paper";
2981
2988
  import Typography7 from "@mui/material/Typography";
2982
- import CircularProgress4 from "@mui/material/CircularProgress";
2983
- import Alert3 from "@mui/material/Alert";
2984
- import Collapse from "@mui/material/Collapse";
2985
- import IconButton4 from "@mui/material/IconButton";
2986
- import { ExpandMore as ExpandMore2 } from "@mui/icons-material";
2987
- import { alpha as alpha4 } from "@mui/material/styles";
2988
- import { useApi as useApi2 } from "@backstage/core-plugin-api";
2989
+ import { alpha as alpha4, useTheme as useTheme2 } from "@mui/material/styles";
2989
2990
  import { Link } from "@backstage/core-components";
2990
2991
  function levelToneColor(tone, theme) {
2991
2992
  switch (tone) {
@@ -3001,13 +3002,119 @@ function levelToneColor(tone, theme) {
3001
3002
  return theme.palette.primary.main;
3002
3003
  }
3003
3004
  }
3004
- var LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, MoreKeysNote, HierarchyNote, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
3005
+ var LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
3006
+ var init_BudgetLimitList = __esm({
3007
+ "src/components/BudgetLimitList.tsx"() {
3008
+ "use strict";
3009
+ init_budget();
3010
+ init_format();
3011
+ init_ui();
3012
+ LimitCard = ({ limit }) => {
3013
+ const tone = budgetTone2(limit.pct);
3014
+ const pct = Math.round(limit.pct);
3015
+ const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
3016
+ 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(
3017
+ Typography7,
3018
+ {
3019
+ variant: "body2",
3020
+ sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
3021
+ title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
3022
+ },
3023
+ limit.label
3024
+ ), limit.sublabel && /* @__PURE__ */ React7.createElement(
3025
+ Typography7,
3026
+ {
3027
+ variant: "caption",
3028
+ color: "text.secondary",
3029
+ sx: {
3030
+ fontSize: 10.5,
3031
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3032
+ display: "block",
3033
+ overflow: "hidden",
3034
+ textOverflow: "ellipsis",
3035
+ whiteSpace: "nowrap"
3036
+ }
3037
+ },
3038
+ limit.sublabel
3039
+ )), limit.hidden ? /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__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(
3040
+ Box7,
3041
+ {
3042
+ sx: {
3043
+ display: "flex",
3044
+ justifyContent: "space-between",
3045
+ alignItems: "center",
3046
+ gap: 1,
3047
+ mt: 0.75
3048
+ }
3049
+ },
3050
+ /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, "Hidden by admin") : /* @__PURE__ */ React7.createElement(React7.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
3051
+ /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3052
+ ));
3053
+ };
3054
+ TaggedLimit = ({ limit }) => {
3055
+ const theme = useTheme2();
3056
+ const tone = budgetTone2(limit.pct);
3057
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(
3058
+ Typography7,
3059
+ {
3060
+ sx: {
3061
+ fontSize: 11,
3062
+ fontWeight: 700,
3063
+ letterSpacing: "0.08em",
3064
+ textTransform: "uppercase",
3065
+ color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
3066
+ mb: 0.5
3067
+ }
3068
+ },
3069
+ limit.kind
3070
+ ), /* @__PURE__ */ React7.createElement(LimitCard, { limit }));
3071
+ };
3072
+ MoreKeysNote = ({
3073
+ count,
3074
+ href,
3075
+ label
3076
+ }) => {
3077
+ const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
3078
+ if (!href) {
3079
+ return /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, text);
3080
+ }
3081
+ return /* @__PURE__ */ React7.createElement(Link, { to: href, variant: "caption" }, text, " \u2192");
3082
+ };
3083
+ BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, limits.map((limit) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: `${limit.kind}:${limit.sublabel ?? limit.label}`, limit })), hiddenKeyCount > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: hiddenKeyCount, href: keysHref }));
3084
+ LimitListPanel = ({ children }) => /* @__PURE__ */ React7.createElement(
3085
+ Box7,
3086
+ {
3087
+ sx: (theme) => ({
3088
+ px: 1.5,
3089
+ py: 1.5,
3090
+ borderRadius: 1.5,
3091
+ bgcolor: alpha4(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.02)
3092
+ })
3093
+ },
3094
+ children
3095
+ );
3096
+ }
3097
+ });
3098
+
3099
+ // src/components/LiteLLMBudgetWidget.tsx
3100
+ import React8, { useEffect as useEffect3, useMemo as useMemo6, useState as useState5 } from "react";
3101
+ import Paper6 from "@mui/material/Paper";
3102
+ import Box8 from "@mui/material/Box";
3103
+ import Typography8 from "@mui/material/Typography";
3104
+ import CircularProgress4 from "@mui/material/CircularProgress";
3105
+ import Alert3 from "@mui/material/Alert";
3106
+ import Collapse from "@mui/material/Collapse";
3107
+ import IconButton4 from "@mui/material/IconButton";
3108
+ import { ExpandMore as ExpandMore2 } from "@mui/icons-material";
3109
+ import { alpha as alpha5 } from "@mui/material/styles";
3110
+ import { useApi as useApi2 } from "@backstage/core-plugin-api";
3111
+ var LevelFrame, EmptyLimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, HierarchyNote, FullBody, CompactBody, LiteLLMBudgetWidget;
3005
3112
  var init_LiteLLMBudgetWidget = __esm({
3006
3113
  "src/components/LiteLLMBudgetWidget.tsx"() {
3007
3114
  "use strict";
3008
3115
  init_api();
3009
- init_format();
3010
3116
  init_ui();
3117
+ init_BudgetLimitList();
3011
3118
  init_budget();
3012
3119
  LevelFrame = ({
3013
3120
  rank,
@@ -3017,8 +3124,8 @@ var init_LiteLLMBudgetWidget = __esm({
3017
3124
  tone,
3018
3125
  isLast,
3019
3126
  children
3020
- }) => /* @__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(
3021
- Box7,
3127
+ }) => /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ React8.createElement(
3128
+ Box8,
3022
3129
  {
3023
3130
  sx: (theme) => ({
3024
3131
  width: 26,
@@ -3027,7 +3134,7 @@ var init_LiteLLMBudgetWidget = __esm({
3027
3134
  display: "flex",
3028
3135
  alignItems: "center",
3029
3136
  justifyContent: "center",
3030
- bgcolor: alpha4(levelToneColor(tone, theme), 0.14),
3137
+ bgcolor: alpha5(levelToneColor(tone, theme), 0.14),
3031
3138
  color: levelToneColor(tone, theme),
3032
3139
  fontSize: 12,
3033
3140
  fontWeight: 700,
@@ -3035,18 +3142,18 @@ var init_LiteLLMBudgetWidget = __esm({
3035
3142
  })
3036
3143
  },
3037
3144
  rank
3038
- ), /* @__PURE__ */ React7.createElement(
3039
- Box7,
3145
+ ), /* @__PURE__ */ React8.createElement(
3146
+ Box8,
3040
3147
  {
3041
3148
  sx: (theme) => ({
3042
3149
  flex: 1,
3043
3150
  width: 2,
3044
3151
  minHeight: 14,
3045
- bgcolor: isLast ? "transparent" : alpha4(theme.palette.text.primary, 0.12)
3152
+ bgcolor: isLast ? "transparent" : alpha5(theme.palette.text.primary, 0.12)
3046
3153
  })
3047
3154
  }
3048
- )), /* @__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(
3049
- Typography7,
3155
+ )), /* @__PURE__ */ React8.createElement(Box8, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ React8.createElement(
3156
+ Typography8,
3050
3157
  {
3051
3158
  sx: (theme) => ({
3052
3159
  fontSize: 12.5,
@@ -3057,102 +3164,42 @@ var init_LiteLLMBudgetWidget = __esm({
3057
3164
  })
3058
3165
  },
3059
3166
  name
3060
- ), /* @__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)));
3061
- EmptyLimitCard = ({ children }) => /* @__PURE__ */ React7.createElement(
3062
- Paper5,
3167
+ ), /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ React8.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
3168
+ EmptyLimitCard = ({ children }) => /* @__PURE__ */ React8.createElement(
3169
+ Paper6,
3063
3170
  {
3064
3171
  variant: "outlined",
3065
3172
  sx: (theme) => ({
3066
3173
  px: 1.5,
3067
3174
  py: 1.25,
3068
3175
  borderRadius: 1.5,
3069
- bgcolor: alpha4(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
3176
+ bgcolor: alpha5(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
3070
3177
  })
3071
3178
  },
3072
- /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3179
+ /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3073
3180
  );
3074
- LimitCard = ({ limit }) => {
3075
- const tone = budgetTone2(limit.pct);
3076
- const pct = Math.round(limit.pct);
3077
- const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
3078
- 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(
3079
- Typography7,
3080
- {
3081
- variant: "body2",
3082
- sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
3083
- title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
3084
- },
3085
- limit.label
3086
- ), limit.sublabel && /* @__PURE__ */ React7.createElement(
3087
- Typography7,
3088
- {
3089
- variant: "caption",
3090
- color: "text.secondary",
3091
- sx: {
3092
- fontSize: 10.5,
3093
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3094
- display: "block",
3095
- overflow: "hidden",
3096
- textOverflow: "ellipsis",
3097
- whiteSpace: "nowrap"
3098
- }
3099
- },
3100
- limit.sublabel
3101
- )), limit.hidden ? /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__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(
3102
- Box7,
3103
- {
3104
- sx: {
3105
- display: "flex",
3106
- justifyContent: "space-between",
3107
- alignItems: "center",
3108
- gap: 1,
3109
- mt: 0.75
3110
- }
3111
- },
3112
- /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, "Hidden by admin") : /* @__PURE__ */ React7.createElement(React7.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
3113
- /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3114
- ));
3115
- };
3116
3181
  CLOSEST_LEVEL_LABEL = {
3117
3182
  key: "key",
3118
3183
  user: "personal",
3119
3184
  team: "team"
3120
3185
  };
3121
3186
  KEYS_PATH = "/litellm?tab=keys";
3122
- MoreKeysNote = ({ count }) => /* @__PURE__ */ React7.createElement(Link, { to: KEYS_PATH, variant: "caption" }, "+", count, " more budgeted key", count > 1 ? "s" : "", " further from the cap \u2192");
3123
- HierarchyNote = ({ withReset }) => /* @__PURE__ */ React7.createElement(
3124
- Box7,
3187
+ HierarchyNote = ({ withReset }) => /* @__PURE__ */ React8.createElement(
3188
+ Box8,
3125
3189
  {
3126
3190
  sx: (theme) => ({
3127
3191
  px: 1.5,
3128
3192
  py: 1.25,
3129
3193
  borderRadius: 1.5,
3130
3194
  border: "1px dashed",
3131
- borderColor: alpha4(theme.palette.text.primary, 0.18)
3195
+ borderColor: alpha5(theme.palette.text.primary, 0.18)
3132
3196
  })
3133
3197
  },
3134
- /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ React7.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
3198
+ /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ React8.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
3135
3199
  );
3136
- TaggedLimit = ({ limit }) => {
3137
- const tone = budgetTone2(limit.pct);
3138
- return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(
3139
- Typography7,
3140
- {
3141
- sx: (theme) => ({
3142
- fontSize: 11,
3143
- fontWeight: 700,
3144
- letterSpacing: "0.08em",
3145
- textTransform: "uppercase",
3146
- color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
3147
- mb: 0.5
3148
- })
3149
- },
3150
- limit.kind
3151
- ), /* @__PURE__ */ React7.createElement(LimitCard, { limit }));
3152
- };
3153
3200
  FullBody = ({ summary }) => {
3154
3201
  const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
3155
- return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(
3202
+ return /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
3156
3203
  LevelFrame,
3157
3204
  {
3158
3205
  rank: 1,
@@ -3160,10 +3207,10 @@ var init_LiteLLMBudgetWidget = __esm({
3160
3207
  tagline: "per-key cap \xB7 highest priority",
3161
3208
  note: hasBudgetedKeys ? void 0 : "no key budgets set"
3162
3209
  },
3163
- !hasBudgetedKeys && /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3164
- summary.keys.map((k) => /* @__PURE__ */ React7.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3165
- summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys })
3166
- ), /* @__PURE__ */ React7.createElement(
3210
+ !hasBudgetedKeys && /* @__PURE__ */ React8.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3211
+ summary.keys.map((k) => /* @__PURE__ */ React8.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3212
+ summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React8.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
3213
+ ), /* @__PURE__ */ React8.createElement(
3167
3214
  LevelFrame,
3168
3215
  {
3169
3216
  rank: 2,
@@ -3171,15 +3218,15 @@ var init_LiteLLMBudgetWidget = __esm({
3171
3218
  tagline: "cap on everything you spend with your own keys",
3172
3219
  note: "skipped for team-bound keys"
3173
3220
  },
3174
- summary.user ? /* @__PURE__ */ React7.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3175
- ), /* @__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(Box7, { sx: { mt: 2 } }, /* @__PURE__ */ React7.createElement(HierarchyNote, { withReset: true })));
3221
+ summary.user ? /* @__PURE__ */ React8.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React8.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3222
+ ), /* @__PURE__ */ React8.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__ */ React8.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React8.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React8.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React8.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__ */ React8.createElement(Box8, { sx: { mt: 2 } }, /* @__PURE__ */ React8.createElement(HierarchyNote, { withReset: true })));
3176
3223
  };
3177
3224
  CompactBody = ({ summary }) => {
3178
3225
  const { count } = budgetHeadline(summary);
3179
3226
  if (count === 0) {
3180
- return /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3227
+ return /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3181
3228
  }
3182
- return /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ React7.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys }), summary.user && /* @__PURE__ */ React7.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3229
+ return /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ React8.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ React8.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React8.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ React8.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React8.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3183
3230
  };
3184
3231
  LiteLLMBudgetWidget = ({
3185
3232
  title = "Budget Policy",
@@ -3230,9 +3277,9 @@ var init_LiteLLMBudgetWidget = __esm({
3230
3277
  headline.closest.pct
3231
3278
  )}%`;
3232
3279
  const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
3233
- 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 })));
3234
- return /* @__PURE__ */ React7.createElement(Paper5, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ React7.createElement(
3235
- Box7,
3280
+ const body = /* @__PURE__ */ React8.createElement(React8.Fragment, null, loading && /* @__PURE__ */ React8.createElement(Box8, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ React8.createElement(CircularProgress4, { size: 32 })), !loading && error && /* @__PURE__ */ React8.createElement(Alert3, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ React8.createElement(CompactBody, { summary }) : /* @__PURE__ */ React8.createElement(FullBody, { summary })));
3281
+ return /* @__PURE__ */ React8.createElement(Paper6, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ React8.createElement(
3282
+ Box8,
3236
3283
  {
3237
3284
  onClick: () => setExpanded((e) => !e),
3238
3285
  sx: {
@@ -3244,14 +3291,14 @@ var init_LiteLLMBudgetWidget = __esm({
3244
3291
  mb: expanded ? 1.5 : 0
3245
3292
  }
3246
3293
  },
3247
- /* @__PURE__ */ React7.createElement(Box7, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ React7.createElement(Box7, { sx: { mt: 0.5 } }, /* @__PURE__ */ React7.createElement(
3294
+ /* @__PURE__ */ React8.createElement(Box8, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ React8.createElement(Box8, { sx: { mt: 0.5 } }, /* @__PURE__ */ React8.createElement(
3248
3295
  StatusPill,
3249
3296
  {
3250
3297
  label: summaryText,
3251
3298
  tone: budgetTone2(headline.closest?.pct ?? 0)
3252
3299
  }
3253
3300
  ))),
3254
- /* @__PURE__ */ React7.createElement(
3301
+ /* @__PURE__ */ React8.createElement(
3255
3302
  IconButton4,
3256
3303
  {
3257
3304
  size: "small",
@@ -3267,10 +3314,10 @@ var init_LiteLLMBudgetWidget = __esm({
3267
3314
  transition: theme.transitions.create("transform")
3268
3315
  })
3269
3316
  },
3270
- /* @__PURE__ */ React7.createElement(ExpandMore2, null)
3317
+ /* @__PURE__ */ React8.createElement(ExpandMore2, null)
3271
3318
  )
3272
- ) : /* @__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(
3273
- Box7,
3319
+ ) : /* @__PURE__ */ React8.createElement(Box8, { sx: { mb: 1.5 } }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ React8.createElement(Collapse, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ React8.createElement(
3320
+ Box8,
3274
3321
  {
3275
3322
  sx: (theme) => ({
3276
3323
  mt: 2,
@@ -3285,10 +3332,10 @@ var init_LiteLLMBudgetWidget = __esm({
3285
3332
  });
3286
3333
 
3287
3334
  // src/components/TeamUsage.tsx
3288
- import React8, { useState as useState6 } from "react";
3289
- import Paper6 from "@mui/material/Paper";
3290
- import Box8 from "@mui/material/Box";
3291
- import Typography8 from "@mui/material/Typography";
3335
+ import React9, { useState as useState6 } from "react";
3336
+ import Paper7 from "@mui/material/Paper";
3337
+ import Box9 from "@mui/material/Box";
3338
+ import Typography9 from "@mui/material/Typography";
3292
3339
  import Skeleton4 from "@mui/material/Skeleton";
3293
3340
  import Divider3 from "@mui/material/Divider";
3294
3341
  import Table4 from "@mui/material/Table";
@@ -3301,7 +3348,7 @@ import Collapse2 from "@mui/material/Collapse";
3301
3348
  import IconButton5 from "@mui/material/IconButton";
3302
3349
  import Button5 from "@mui/material/Button";
3303
3350
  import CircularProgress5 from "@mui/material/CircularProgress";
3304
- import { alpha as alpha5 } from "@mui/material/styles";
3351
+ import { alpha as alpha6 } from "@mui/material/styles";
3305
3352
  import { ExpandMore as ExpandMore3, Group, Add as Add3 } from "@mui/icons-material";
3306
3353
  import {
3307
3354
  AreaChart as AreaChart2,
@@ -3345,24 +3392,24 @@ var init_TeamUsage = __esm({
3345
3392
  const renderDailySpendSection = () => {
3346
3393
  if (hidden) return null;
3347
3394
  if (usageLoading) {
3348
- return /* @__PURE__ */ React8.createElement(Box8, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React8.createElement(CircularProgress5, { size: 22 }));
3395
+ return /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React9.createElement(CircularProgress5, { size: 22 }));
3349
3396
  }
3350
3397
  if (dailyData.length === 0) return null;
3351
- return /* @__PURE__ */ React8.createElement(Box8, { mb: 2.5 }, /* @__PURE__ */ React8.createElement(
3352
- Typography8,
3398
+ return /* @__PURE__ */ React9.createElement(Box9, { mb: 2.5 }, /* @__PURE__ */ React9.createElement(
3399
+ Typography9,
3353
3400
  {
3354
3401
  variant: "caption",
3355
3402
  color: "text.secondary",
3356
3403
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
3357
3404
  },
3358
3405
  "Daily spend"
3359
- ), /* @__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(
3406
+ ), /* @__PURE__ */ React9.createElement(Box9, { height: 150 }, /* @__PURE__ */ React9.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React9.createElement(AreaChart2, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React9.createElement(CartesianGrid2, { ...chart.grid }), /* @__PURE__ */ React9.createElement(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React9.createElement(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ React9.createElement(
3360
3407
  Tooltip2,
3361
3408
  {
3362
3409
  cursor: chart.cursor,
3363
- content: /* @__PURE__ */ React8.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3410
+ content: /* @__PURE__ */ React9.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3364
3411
  }
3365
- ), /* @__PURE__ */ React8.createElement(
3412
+ ), /* @__PURE__ */ React9.createElement(
3366
3413
  Area2,
3367
3414
  {
3368
3415
  type: "monotone",
@@ -3375,8 +3422,8 @@ var init_TeamUsage = __esm({
3375
3422
  }
3376
3423
  )))));
3377
3424
  };
3378
- const sectionLabel = (label) => /* @__PURE__ */ React8.createElement(
3379
- Typography8,
3425
+ const sectionLabel = (label) => /* @__PURE__ */ React9.createElement(
3426
+ Typography9,
3380
3427
  {
3381
3428
  variant: "caption",
3382
3429
  color: "text.secondary",
@@ -3384,8 +3431,8 @@ var init_TeamUsage = __esm({
3384
3431
  },
3385
3432
  label
3386
3433
  );
3387
- 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(
3388
- Box8,
3434
+ return /* @__PURE__ */ React9.createElement(Paper7, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React9.createElement(Box9, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React9.createElement(
3435
+ Box9,
3389
3436
  {
3390
3437
  sx: (theme) => ({
3391
3438
  width: 36,
@@ -3395,20 +3442,20 @@ var init_TeamUsage = __esm({
3395
3442
  display: "flex",
3396
3443
  alignItems: "center",
3397
3444
  justifyContent: "center",
3398
- bgcolor: alpha5(theme.palette.primary.main, 0.1),
3445
+ bgcolor: alpha6(theme.palette.primary.main, 0.1),
3399
3446
  color: theme.palette.primary.main
3400
3447
  })
3401
3448
  },
3402
- /* @__PURE__ */ React8.createElement(Group, { fontSize: "small" })
3403
- ), /* @__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(
3404
- Typography8,
3449
+ /* @__PURE__ */ React9.createElement(Group, { fontSize: "small" })
3450
+ ), /* @__PURE__ */ React9.createElement(Box9, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ React9.createElement(
3451
+ Typography9,
3405
3452
  {
3406
3453
  variant: "caption",
3407
3454
  color: "text.secondary",
3408
3455
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3409
3456
  },
3410
3457
  team.team_id
3411
- )), 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(
3458
+ )), isOver && /* @__PURE__ */ React9.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ React9.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ React9.createElement(
3412
3459
  Button5,
3413
3460
  {
3414
3461
  size: "small",
@@ -3417,7 +3464,7 @@ var init_TeamUsage = __esm({
3417
3464
  sx: { textTransform: "none" }
3418
3465
  },
3419
3466
  "Edit"
3420
- ), /* @__PURE__ */ React8.createElement(
3467
+ ), /* @__PURE__ */ React9.createElement(
3421
3468
  IconButton5,
3422
3469
  {
3423
3470
  size: "small",
@@ -3430,9 +3477,9 @@ var init_TeamUsage = __esm({
3430
3477
  transition: theme.transitions.create("transform")
3431
3478
  })
3432
3479
  },
3433
- /* @__PURE__ */ React8.createElement(ExpandMore3, null)
3434
- )), /* @__PURE__ */ React8.createElement(
3435
- Box8,
3480
+ /* @__PURE__ */ React9.createElement(ExpandMore3, null)
3481
+ )), /* @__PURE__ */ React9.createElement(
3482
+ Box9,
3436
3483
  {
3437
3484
  sx: {
3438
3485
  display: "grid",
@@ -3442,35 +3489,35 @@ var init_TeamUsage = __esm({
3442
3489
  maxWidth: 640
3443
3490
  }
3444
3491
  },
3445
- /* @__PURE__ */ React8.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3446
- /* @__PURE__ */ React8.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3447
- /* @__PURE__ */ React8.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3448
- /* @__PURE__ */ React8.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3449
- /* @__PURE__ */ React8.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3450
- /* @__PURE__ */ React8.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3451
- ), hidden && /* @__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" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ React8.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.75 } }, "Team budget is hidden \u2014 showing usage level only", team.budget_duration ? ` \xB7 resets every ${team.budget_duration}` : "", ".")), !hidden && 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(
3492
+ /* @__PURE__ */ React9.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3493
+ /* @__PURE__ */ React9.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3494
+ /* @__PURE__ */ React9.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3495
+ /* @__PURE__ */ React9.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3496
+ /* @__PURE__ */ React9.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3497
+ /* @__PURE__ */ React9.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3498
+ ), hidden && /* @__PURE__ */ React9.createElement(Box9, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ React9.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.75 } }, "Team budget is hidden \u2014 showing usage level only", team.budget_duration ? ` \xB7 resets every ${team.budget_duration}` : "", ".")), !hidden && budget > 0 && /* @__PURE__ */ React9.createElement(Box9, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React9.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ React9.createElement(Collapse2, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(Divider3, { sx: { my: 2.5 } }), /* @__PURE__ */ React9.createElement(Box9, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React9.createElement(Box9, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React9.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React9.createElement(Box9, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React9.createElement(
3452
3499
  TableContainer4,
3453
3500
  {
3454
- component: Paper6,
3501
+ component: Paper7,
3455
3502
  variant: "outlined",
3456
3503
  sx: { borderRadius: 1.5 }
3457
3504
  },
3458
- /* @__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(
3459
- Typography8,
3505
+ /* @__PURE__ */ React9.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead4, null, /* @__PURE__ */ React9.createElement(TableRow4, null, /* @__PURE__ */ React9.createElement(TableCell4, null, "User"), /* @__PURE__ */ React9.createElement(TableCell4, { align: "right" }, "Role"))), /* @__PURE__ */ React9.createElement(TableBody4, null, team.members_with_roles.map((m) => /* @__PURE__ */ React9.createElement(TableRow4, { key: m.user_id }, /* @__PURE__ */ React9.createElement(TableCell4, null, m.user_email ? /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, m.user_email), /* @__PURE__ */ React9.createElement(
3506
+ Typography9,
3460
3507
  {
3461
3508
  variant: "caption",
3462
3509
  color: "text.secondary",
3463
3510
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3464
3511
  },
3465
3512
  m.user_id
3466
- )) : /* @__PURE__ */ React8.createElement(
3467
- Typography8,
3513
+ )) : /* @__PURE__ */ React9.createElement(
3514
+ Typography9,
3468
3515
  {
3469
3516
  variant: "body2",
3470
3517
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
3471
3518
  },
3472
3519
  m.user_id
3473
- )), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(
3520
+ )), /* @__PURE__ */ React9.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React9.createElement(
3474
3521
  StatusPill,
3475
3522
  {
3476
3523
  label: m.role,
@@ -3478,7 +3525,7 @@ var init_TeamUsage = __esm({
3478
3525
  dot: false
3479
3526
  }
3480
3527
  ))))))
3481
- ) : /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3528
+ ) : /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3482
3529
  };
3483
3530
  TeamUsage = ({
3484
3531
  teams,
@@ -3490,22 +3537,22 @@ var init_TeamUsage = __esm({
3490
3537
  canCreate,
3491
3538
  onCreateTeam
3492
3539
  }) => {
3493
- const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React8.createElement(
3540
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React9.createElement(
3494
3541
  Button5,
3495
3542
  {
3496
3543
  variant: "contained",
3497
3544
  color: "primary",
3498
3545
  disableElevation: true,
3499
- startIcon: /* @__PURE__ */ React8.createElement(Add3, null),
3546
+ startIcon: /* @__PURE__ */ React9.createElement(Add3, null),
3500
3547
  onClick: onCreateTeam
3501
3548
  },
3502
3549
  "Create Team"
3503
3550
  ) : void 0;
3504
3551
  if (loading) {
3505
- 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 }));
3552
+ return /* @__PURE__ */ React9.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React9.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React9.createElement(Skeleton4, { variant: "rounded", height: 120 }));
3506
3553
  }
3507
3554
  if (!teams.length) {
3508
- return /* @__PURE__ */ React8.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React8.createElement(
3555
+ return /* @__PURE__ */ React9.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React9.createElement(
3509
3556
  EmptyState,
3510
3557
  {
3511
3558
  message: "You're not a member of any LiteLLM team yet.",
@@ -3513,14 +3560,14 @@ var init_TeamUsage = __esm({
3513
3560
  }
3514
3561
  ));
3515
3562
  }
3516
- return /* @__PURE__ */ React8.createElement(
3563
+ return /* @__PURE__ */ React9.createElement(
3517
3564
  SectionCard,
3518
3565
  {
3519
3566
  title: "Teams",
3520
3567
  subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
3521
3568
  actions: createAction
3522
3569
  },
3523
- /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React8.createElement(
3570
+ /* @__PURE__ */ React9.createElement(Box9, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React9.createElement(
3524
3571
  TeamCard,
3525
3572
  {
3526
3573
  key: team.team_id,
@@ -3537,8 +3584,8 @@ var init_TeamUsage = __esm({
3537
3584
  });
3538
3585
 
3539
3586
  // src/components/ModelsTable.tsx
3540
- import React9, { useState as useState7, useMemo as useMemo7, useCallback } from "react";
3541
- import Box9 from "@mui/material/Box";
3587
+ import React10, { useState as useState7, useMemo as useMemo7, useCallback } from "react";
3588
+ import Box10 from "@mui/material/Box";
3542
3589
  import Table5 from "@mui/material/Table";
3543
3590
  import TableBody5 from "@mui/material/TableBody";
3544
3591
  import TableCell5 from "@mui/material/TableCell";
@@ -3546,7 +3593,7 @@ import TableContainer5 from "@mui/material/TableContainer";
3546
3593
  import TableHead5 from "@mui/material/TableHead";
3547
3594
  import TableRow5 from "@mui/material/TableRow";
3548
3595
  import TableSortLabel2 from "@mui/material/TableSortLabel";
3549
- import Typography9 from "@mui/material/Typography";
3596
+ import Typography10 from "@mui/material/Typography";
3550
3597
  import MenuItem4 from "@mui/material/MenuItem";
3551
3598
  import Select from "@mui/material/Select";
3552
3599
  import FormControl from "@mui/material/FormControl";
@@ -3558,7 +3605,7 @@ import ContentCopyIcon from "@mui/icons-material/ContentCopy";
3558
3605
  import Tooltip3 from "@mui/material/Tooltip";
3559
3606
  import Snackbar from "@mui/material/Snackbar";
3560
3607
  import Alert4 from "@mui/material/Alert";
3561
- import { alpha as alpha6, useTheme as useTheme2 } from "@mui/material/styles";
3608
+ import { alpha as alpha7, useTheme as useTheme3 } from "@mui/material/styles";
3562
3609
  function isModelAllowedByTeam2(model, teamModels) {
3563
3610
  if (!teamModels || teamModels.length === 0 || teamModels.includes(ALL_PROXY_MODELS2)) return true;
3564
3611
  return teamModels.includes(model.model_name) || !!model.access_groups?.some((group) => teamModels.includes(group));
@@ -3583,7 +3630,7 @@ var init_ModelsTable = __esm({
3583
3630
  init_ui();
3584
3631
  ALL_PROXY_MODELS2 = "all-proxy-models";
3585
3632
  ModelsTable = ({ allModels, teams, loading }) => {
3586
- const theme = useTheme2();
3633
+ const theme = useTheme3();
3587
3634
  const [selectedTeamId, setSelectedTeamId] = useState7("");
3588
3635
  const [filterText, setFilterText] = useState7("");
3589
3636
  const [copiedSnackbar, setCopiedSnackbar] = useState7(false);
@@ -3669,13 +3716,13 @@ var init_ModelsTable = __esm({
3669
3716
  return theme.palette.info.main;
3670
3717
  }
3671
3718
  };
3672
- return /* @__PURE__ */ React9.createElement(
3719
+ return /* @__PURE__ */ React10.createElement(
3673
3720
  SectionCard,
3674
3721
  {
3675
3722
  title: "Available Models",
3676
3723
  subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
3677
3724
  },
3678
- /* @__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(
3725
+ /* @__PURE__ */ React10.createElement(Box10, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ React10.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React10.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React10.createElement(
3679
3726
  Select,
3680
3727
  {
3681
3728
  labelId: "team-select-label",
@@ -3683,8 +3730,8 @@ var init_ModelsTable = __esm({
3683
3730
  label: "Team",
3684
3731
  onChange: (e) => setSelectedTeamId(e.target.value)
3685
3732
  },
3686
- teams.map((t) => /* @__PURE__ */ React9.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3687
- )), /* @__PURE__ */ React9.createElement(
3733
+ teams.map((t) => /* @__PURE__ */ React10.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3734
+ )), /* @__PURE__ */ React10.createElement(
3688
3735
  TextField5,
3689
3736
  {
3690
3737
  label: "Filter Models",
@@ -3694,33 +3741,33 @@ var init_ModelsTable = __esm({
3694
3741
  disabled: !selectedTeamId,
3695
3742
  sx: { minWidth: 240, mb: 2 },
3696
3743
  InputProps: {
3697
- startAdornment: /* @__PURE__ */ React9.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React9.createElement(Search2, { fontSize: "small" }))
3744
+ startAdornment: /* @__PURE__ */ React10.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React10.createElement(Search2, { fontSize: "small" }))
3698
3745
  }
3699
3746
  }
3700
3747
  )),
3701
- loading && /* @__PURE__ */ React9.createElement(EmptyState, { message: "Loading models\u2026" }),
3702
- !loading && !selectedTeamId && /* @__PURE__ */ React9.createElement(
3748
+ loading && /* @__PURE__ */ React10.createElement(EmptyState, { message: "Loading models\u2026" }),
3749
+ !loading && !selectedTeamId && /* @__PURE__ */ React10.createElement(
3703
3750
  EmptyState,
3704
3751
  {
3705
3752
  message: "Select a team to see available models",
3706
3753
  hint: "Each team may have a different set of models assigned"
3707
3754
  }
3708
3755
  ),
3709
- !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React9.createElement(
3756
+ !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React10.createElement(
3710
3757
  EmptyState,
3711
3758
  {
3712
3759
  message: "No models available for this team",
3713
3760
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
3714
3761
  }
3715
3762
  ),
3716
- !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React9.createElement(
3763
+ !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React10.createElement(
3717
3764
  EmptyState,
3718
3765
  {
3719
3766
  message: "No models match your search",
3720
3767
  hint: "Try a different search term"
3721
3768
  }
3722
3769
  ),
3723
- !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, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ React9.createElement(TableCell5, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "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(
3770
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ React10.createElement(TableContainer5, null, /* @__PURE__ */ React10.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React10.createElement(TableHead5, null, /* @__PURE__ */ React10.createElement(TableRow5, null, /* @__PURE__ */ React10.createElement(TableCell5, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ React10.createElement(TableCell5, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ React10.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React10.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React10.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React10.createElement(TableCell5, null, /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React10.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React10.createElement(
3724
3771
  "span",
3725
3772
  {
3726
3773
  role: "button",
@@ -3730,9 +3777,9 @@ var init_ModelsTable = __esm({
3730
3777
  onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
3731
3778
  style: { cursor: "pointer", display: "inline-flex" }
3732
3779
  },
3733
- /* @__PURE__ */ React9.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3734
- )), /* @__PURE__ */ React9.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React9.createElement(
3735
- Box9,
3780
+ /* @__PURE__ */ React10.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3781
+ )), /* @__PURE__ */ React10.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React10.createElement(
3782
+ Box10,
3736
3783
  {
3737
3784
  component: "span",
3738
3785
  role: "button",
@@ -3742,9 +3789,9 @@ var init_ModelsTable = __esm({
3742
3789
  sx: quietIconButtonSx("accent"),
3743
3790
  style: { cursor: "pointer", display: "inline-flex" }
3744
3791
  },
3745
- /* @__PURE__ */ React9.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
3746
- )))), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(
3747
- Box9,
3792
+ /* @__PURE__ */ React10.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
3793
+ )))), /* @__PURE__ */ React10.createElement(TableCell5, null, /* @__PURE__ */ React10.createElement(
3794
+ Box10,
3748
3795
  {
3749
3796
  component: "span",
3750
3797
  sx: {
@@ -3755,13 +3802,13 @@ var init_ModelsTable = __esm({
3755
3802
  fontSize: 11,
3756
3803
  fontWeight: 600,
3757
3804
  textTransform: "capitalize",
3758
- bgcolor: alpha6(modeColor(m.mode), 0.1),
3805
+ bgcolor: alpha7(modeColor(m.mode), 0.1),
3759
3806
  color: modeColor(m.mode)
3760
3807
  }
3761
3808
  },
3762
3809
  m.mode
3763
- )), /* @__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" })))))))),
3764
- /* @__PURE__ */ React9.createElement(
3810
+ )), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React10.createElement(TableCell5, null, /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React10.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React10.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3811
+ /* @__PURE__ */ React10.createElement(
3765
3812
  Snackbar,
3766
3813
  {
3767
3814
  open: copiedSnackbar,
@@ -3769,7 +3816,7 @@ var init_ModelsTable = __esm({
3769
3816
  onClose: () => setCopiedSnackbar(false),
3770
3817
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
3771
3818
  },
3772
- /* @__PURE__ */ React9.createElement(Alert4, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3819
+ /* @__PURE__ */ React10.createElement(Alert4, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3773
3820
  )
3774
3821
  );
3775
3822
  };
@@ -3777,9 +3824,9 @@ var init_ModelsTable = __esm({
3777
3824
  });
3778
3825
 
3779
3826
  // src/components/AuditLog.tsx
3780
- import React10, { useState as useState8, useMemo as useMemo8 } from "react";
3781
- import Box10 from "@mui/material/Box";
3782
- import Typography10 from "@mui/material/Typography";
3827
+ import React11, { useState as useState8, useMemo as useMemo8 } from "react";
3828
+ import Box11 from "@mui/material/Box";
3829
+ import Typography11 from "@mui/material/Typography";
3783
3830
  import Table6 from "@mui/material/Table";
3784
3831
  import TableBody6 from "@mui/material/TableBody";
3785
3832
  import TableCell6 from "@mui/material/TableCell";
@@ -3793,7 +3840,7 @@ import Skeleton5 from "@mui/material/Skeleton";
3793
3840
  import Collapse3 from "@mui/material/Collapse";
3794
3841
  import IconButton6 from "@mui/material/IconButton";
3795
3842
  import Alert5 from "@mui/material/Alert";
3796
- import { alpha as alpha7 } from "@mui/material/styles";
3843
+ import { alpha as alpha8 } from "@mui/material/styles";
3797
3844
  import { KeyboardArrowDown } from "@mui/icons-material";
3798
3845
  import { useAsync as useAsync2 } from "react-use";
3799
3846
  function actionTone(action) {
@@ -3816,10 +3863,10 @@ function formatDateTime(iso) {
3816
3863
  }
3817
3864
  function renderAuditLogBody(loading, entries) {
3818
3865
  if (loading) {
3819
- return /* @__PURE__ */ React10.createElement(TableRow6, null, /* @__PURE__ */ React10.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React10.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3866
+ return /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React11.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3820
3867
  }
3821
3868
  if (entries.length === 0) {
3822
- return /* @__PURE__ */ React10.createElement(TableRow6, null, /* @__PURE__ */ React10.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React10.createElement(
3869
+ return /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React11.createElement(
3823
3870
  EmptyState,
3824
3871
  {
3825
3872
  message: "No audit events found",
@@ -3827,7 +3874,7 @@ function renderAuditLogBody(loading, entries) {
3827
3874
  }
3828
3875
  )));
3829
3876
  }
3830
- return entries.map((entry) => /* @__PURE__ */ React10.createElement(DetailRow, { key: entry.id, entry }));
3877
+ return entries.map((entry) => /* @__PURE__ */ React11.createElement(DetailRow, { key: entry.id, entry }));
3831
3878
  }
3832
3879
  var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
3833
3880
  var init_AuditLog = __esm({
@@ -3851,7 +3898,7 @@ var init_AuditLog = __esm({
3851
3898
  DetailRow = ({ entry }) => {
3852
3899
  const [open, setOpen] = useState8(false);
3853
3900
  const hasDetail = entry.before_value || entry.updated_values;
3854
- 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(
3901
+ return /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React11.createElement(
3855
3902
  IconButton6,
3856
3903
  {
3857
3904
  size: "small",
@@ -3864,9 +3911,9 @@ var init_AuditLog = __esm({
3864
3911
  transition: theme.transitions.create("transform")
3865
3912
  })
3866
3913
  },
3867
- /* @__PURE__ */ React10.createElement(KeyboardArrowDown, { fontSize: "small" })
3868
- )), /* @__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(
3869
- Typography10,
3914
+ /* @__PURE__ */ React11.createElement(KeyboardArrowDown, { fontSize: "small" })
3915
+ )), /* @__PURE__ */ React11.createElement(TableCell6, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React11.createElement(Typography11, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React11.createElement(TableCell6, null, entry.action && /* @__PURE__ */ React11.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React11.createElement(TableCell6, null, /* @__PURE__ */ React11.createElement(Typography11, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React11.createElement(TableCell6, null, /* @__PURE__ */ React11.createElement(
3916
+ Typography11,
3870
3917
  {
3871
3918
  variant: "body2",
3872
3919
  component: "code",
@@ -3875,8 +3922,8 @@ var init_AuditLog = __esm({
3875
3922
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3876
3923
  },
3877
3924
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
3878
- )), /* @__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(
3879
- Box10,
3925
+ )), /* @__PURE__ */ React11.createElement(TableCell6, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React11.createElement(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React11.createElement(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React11.createElement(Collapse3, { in: open, unmountOnExit: true }, /* @__PURE__ */ React11.createElement(
3926
+ Box11,
3880
3927
  {
3881
3928
  display: "flex",
3882
3929
  gap: 2,
@@ -3885,11 +3932,11 @@ var init_AuditLog = __esm({
3885
3932
  p: 2,
3886
3933
  mb: 1.5,
3887
3934
  borderRadius: 1.5,
3888
- bgcolor: alpha7(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3935
+ bgcolor: alpha8(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3889
3936
  })
3890
3937
  },
3891
- entry.before_value && /* @__PURE__ */ React10.createElement(Box10, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React10.createElement(
3892
- Typography10,
3938
+ entry.before_value && /* @__PURE__ */ React11.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React11.createElement(
3939
+ Typography11,
3893
3940
  {
3894
3941
  variant: "caption",
3895
3942
  color: "text.secondary",
@@ -3898,9 +3945,9 @@ var init_AuditLog = __esm({
3898
3945
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3899
3946
  },
3900
3947
  "Before"
3901
- ), /* @__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))),
3902
- entry.updated_values && /* @__PURE__ */ React10.createElement(Box10, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React10.createElement(
3903
- Typography10,
3948
+ ), /* @__PURE__ */ React11.createElement(Typography11, { 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))),
3949
+ entry.updated_values && /* @__PURE__ */ React11.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React11.createElement(
3950
+ Typography11,
3904
3951
  {
3905
3952
  variant: "caption",
3906
3953
  color: "text.secondary",
@@ -3909,7 +3956,7 @@ var init_AuditLog = __esm({
3909
3956
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3910
3957
  },
3911
3958
  "After"
3912
- ), /* @__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)))
3959
+ ), /* @__PURE__ */ React11.createElement(Typography11, { 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)))
3913
3960
  )))));
3914
3961
  };
3915
3962
  AuditLog = ({ api }) => {
@@ -3926,13 +3973,13 @@ var init_AuditLog = __esm({
3926
3973
  );
3927
3974
  const entries = value?.audit_logs ?? [];
3928
3975
  const total = value?.total ?? 0;
3929
- return /* @__PURE__ */ React10.createElement(
3976
+ return /* @__PURE__ */ React11.createElement(
3930
3977
  SectionCard,
3931
3978
  {
3932
3979
  title: "Audit Log",
3933
3980
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3934
3981
  flush: true,
3935
- actions: /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
3982
+ actions: /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(
3936
3983
  TextField6,
3937
3984
  {
3938
3985
  size: "small",
@@ -3945,12 +3992,12 @@ var init_AuditLog = __esm({
3945
3992
  },
3946
3993
  sx: { minWidth: 150 }
3947
3994
  },
3948
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "" }, "All actions"),
3949
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "created" }, "Created"),
3950
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "updated" }, "Updated"),
3951
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3952
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3953
- ), /* @__PURE__ */ React10.createElement(
3995
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "" }, "All actions"),
3996
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "created" }, "Created"),
3997
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "updated" }, "Updated"),
3998
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3999
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "blocked" }, "Blocked")
4000
+ ), /* @__PURE__ */ React11.createElement(
3954
4001
  TextField6,
3955
4002
  {
3956
4003
  size: "small",
@@ -3963,13 +4010,13 @@ var init_AuditLog = __esm({
3963
4010
  },
3964
4011
  sx: { minWidth: 150 }
3965
4012
  },
3966
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "" }, "All tables"),
3967
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
3968
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
3969
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
3970
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3971
- /* @__PURE__ */ React10.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3972
- ), /* @__PURE__ */ React10.createElement(
4013
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "" }, "All tables"),
4014
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
4015
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
4016
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
4017
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
4018
+ /* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
4019
+ ), /* @__PURE__ */ React11.createElement(
3973
4020
  TextField6,
3974
4021
  {
3975
4022
  size: "small",
@@ -3983,9 +4030,9 @@ var init_AuditLog = __esm({
3983
4030
  }
3984
4031
  ))
3985
4032
  },
3986
- error && /* @__PURE__ */ React10.createElement(Box10, { px: 2.5, pb: 2 }, /* @__PURE__ */ React10.createElement(Alert5, { severity: "error" }, error.message)),
3987
- /* @__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)))),
3988
- /* @__PURE__ */ React10.createElement(
4033
+ error && /* @__PURE__ */ React11.createElement(Box11, { px: 2.5, pb: 2 }, /* @__PURE__ */ React11.createElement(Alert5, { severity: "error" }, error.message)),
4034
+ /* @__PURE__ */ React11.createElement(TableContainer6, null, /* @__PURE__ */ React11.createElement(Table6, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React11.createElement(TableHead6, null, /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { sx: { width: 40 } }), /* @__PURE__ */ React11.createElement(TableCell6, null, "Time"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Action"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Table"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Object ID"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Changed By"))), /* @__PURE__ */ React11.createElement(TableBody6, null, renderAuditLogBody(loading, entries)))),
4035
+ /* @__PURE__ */ React11.createElement(
3989
4036
  TablePagination,
3990
4037
  {
3991
4038
  component: "div",
@@ -4040,13 +4087,13 @@ var LiteLLMPage_exports = {};
4040
4087
  __export(LiteLLMPage_exports, {
4041
4088
  LiteLLMPage: () => LiteLLMPage
4042
4089
  });
4043
- import React11, { useState as useState9, useCallback as useCallback2, useMemo as useMemo9 } from "react";
4044
- import Box11 from "@mui/material/Box";
4090
+ import React12, { useState as useState9, useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo9 } from "react";
4091
+ import Box12 from "@mui/material/Box";
4045
4092
  import Snackbar2 from "@mui/material/Snackbar";
4046
4093
  import Alert6 from "@mui/material/Alert";
4047
4094
  import CircularProgress6 from "@mui/material/CircularProgress";
4048
- import Typography11 from "@mui/material/Typography";
4049
- import Paper7 from "@mui/material/Paper";
4095
+ import Typography12 from "@mui/material/Typography";
4096
+ import Paper8 from "@mui/material/Paper";
4050
4097
  import Tabs2 from "@mui/material/Tabs";
4051
4098
  import Tab2 from "@mui/material/Tab";
4052
4099
  import Grid2 from "@mui/material/Grid";
@@ -4101,6 +4148,21 @@ var init_LiteLLMPage = __esm({
4101
4148
  const t = searchParams.get("tab");
4102
4149
  return isPageTab(t) ? t : "overview";
4103
4150
  });
4151
+ const [generateDialogOpen, setGenerateDialogOpen] = useState9(
4152
+ () => searchParams.get("generate") === "1"
4153
+ );
4154
+ useEffect4(() => {
4155
+ if (searchParams.get("generate") !== "1") return;
4156
+ setGenerateDialogOpen(true);
4157
+ setSearchParams(
4158
+ (prev) => {
4159
+ const next = new URLSearchParams(prev);
4160
+ next.delete("generate");
4161
+ return next;
4162
+ },
4163
+ { replace: true }
4164
+ );
4165
+ }, [searchParams, setSearchParams]);
4104
4166
  const selectTab = useCallback2(
4105
4167
  (tab) => {
4106
4168
  setActiveTab(tab);
@@ -4116,7 +4178,6 @@ var init_LiteLLMPage = __esm({
4116
4178
  [setSearchParams]
4117
4179
  );
4118
4180
  const [snackbar, setSnackbar] = useState9(null);
4119
- const [generateDialogOpen, setGenerateDialogOpen] = useState9(false);
4120
4181
  const [manageTeam, setManageTeam] = useState9(null);
4121
4182
  const [teamUsageCache, setTeamUsageCache] = useState9({});
4122
4183
  const [teamUsageLoading, setTeamUsageLoading] = useState9({});
@@ -4310,14 +4371,14 @@ var init_LiteLLMPage = __esm({
4310
4371
  );
4311
4372
  const isInitialLoading = userLoading && !userInfo;
4312
4373
  if (isInitialLoading) {
4313
- return /* @__PURE__ */ React11.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React11.createElement(CircularProgress6, null));
4374
+ return /* @__PURE__ */ React12.createElement(Box12, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React12.createElement(CircularProgress6, null));
4314
4375
  }
4315
4376
  if (userError || !userInfo) {
4316
4377
  const isProvisioningEnabled = userError?.body?.provisioning === true;
4317
4378
  const hint = userError?.body?.hint;
4318
- 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.")));
4379
+ return /* @__PURE__ */ React12.createElement(Box12, { p: 3 }, /* @__PURE__ */ React12.createElement(Paper8, { sx: { p: 3 } }, /* @__PURE__ */ React12.createElement(Typography12, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React12.createElement(Typography12, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React12.createElement(Typography12, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React12.createElement(Typography12, { 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.")));
4319
4380
  }
4320
- const pageTabs = /* @__PURE__ */ React11.createElement(
4381
+ const pageTabs = /* @__PURE__ */ React12.createElement(
4321
4382
  Tabs2,
4322
4383
  {
4323
4384
  value: activeTab,
@@ -4330,13 +4391,13 @@ var init_LiteLLMPage = __esm({
4330
4391
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
4331
4392
  }
4332
4393
  },
4333
- /* @__PURE__ */ React11.createElement(Tab2, { label: "Overview", value: "overview" }),
4334
- /* @__PURE__ */ React11.createElement(Tab2, { label: "Keys", value: "keys" }),
4335
- /* @__PURE__ */ React11.createElement(Tab2, { label: "Teams", value: "teams" }),
4336
- /* @__PURE__ */ React11.createElement(Tab2, { label: "Models", value: "models" }),
4337
- userInfo.can_view_audit && /* @__PURE__ */ React11.createElement(Tab2, { label: "Audit Log", value: "audit" })
4394
+ /* @__PURE__ */ React12.createElement(Tab2, { label: "Overview", value: "overview" }),
4395
+ /* @__PURE__ */ React12.createElement(Tab2, { label: "Keys", value: "keys" }),
4396
+ /* @__PURE__ */ React12.createElement(Tab2, { label: "Teams", value: "teams" }),
4397
+ /* @__PURE__ */ React12.createElement(Tab2, { label: "Models", value: "models" }),
4398
+ userInfo.can_view_audit && /* @__PURE__ */ React12.createElement(Tab2, { label: "Audit Log", value: "audit" })
4338
4399
  );
4339
- return /* @__PURE__ */ React11.createElement(Box11, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React11.createElement(
4400
+ return /* @__PURE__ */ React12.createElement(Box12, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React12.createElement(
4340
4401
  DashboardHeader,
4341
4402
  {
4342
4403
  userInfo,
@@ -4346,7 +4407,7 @@ var init_LiteLLMPage = __esm({
4346
4407
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
4347
4408
  tabs: pageTabs
4348
4409
  }
4349
- ), 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(
4410
+ ), activeTab === "overview" && /* @__PURE__ */ React12.createElement(Grid2, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ React12.createElement(
4350
4411
  UsageStats,
4351
4412
  {
4352
4413
  usage: usage ?? null,
@@ -4357,7 +4418,7 @@ var init_LiteLLMPage = __esm({
4357
4418
  loading: usageLoading,
4358
4419
  userInfo
4359
4420
  }
4360
- )), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ React11.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ React11.createElement(
4421
+ )), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ React12.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ React12.createElement(
4361
4422
  KeysTable,
4362
4423
  {
4363
4424
  keys: keys ?? [],
@@ -4376,7 +4437,7 @@ var init_LiteLLMPage = __esm({
4376
4437
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
4377
4438
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
4378
4439
  const visibleTeams = Array.from(teamsById.values());
4379
- return /* @__PURE__ */ React11.createElement(
4440
+ return /* @__PURE__ */ React12.createElement(
4380
4441
  TeamUsage,
4381
4442
  {
4382
4443
  teams: visibleTeams,
@@ -4392,14 +4453,14 @@ var init_LiteLLMPage = __esm({
4392
4453
  onCreateTeam: () => setManageTeam({ mode: "create" })
4393
4454
  }
4394
4455
  );
4395
- })(), activeTab === "models" && /* @__PURE__ */ React11.createElement(
4456
+ })(), activeTab === "models" && /* @__PURE__ */ React12.createElement(
4396
4457
  ModelsTable,
4397
4458
  {
4398
4459
  allModels: allModels ?? [],
4399
4460
  teams: teams ?? [],
4400
4461
  loading: modelsLoading
4401
4462
  }
4402
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React11.createElement(AuditLog, { api }), /* @__PURE__ */ React11.createElement(
4463
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React12.createElement(AuditLog, { api }), /* @__PURE__ */ React12.createElement(
4403
4464
  GenerateKeyDialog,
4404
4465
  {
4405
4466
  open: generateDialogOpen,
@@ -4412,7 +4473,7 @@ var init_LiteLLMPage = __esm({
4412
4473
  onGenerateKey: handleGenerateKey,
4413
4474
  onGetConfig: () => api.getConfig()
4414
4475
  }
4415
- ), /* @__PURE__ */ React11.createElement(
4476
+ ), /* @__PURE__ */ React12.createElement(
4416
4477
  ManageTeamDialog,
4417
4478
  {
4418
4479
  open: !!manageTeam,
@@ -4474,7 +4535,7 @@ var init_LiteLLMPage = __esm({
4474
4535
  refreshTeams();
4475
4536
  }
4476
4537
  }
4477
- ), /* @__PURE__ */ React11.createElement(
4538
+ ), /* @__PURE__ */ React12.createElement(
4478
4539
  Snackbar2,
4479
4540
  {
4480
4541
  open: !!snackbar,
@@ -4482,7 +4543,7 @@ var init_LiteLLMPage = __esm({
4482
4543
  onClose: () => setSnackbar(null),
4483
4544
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
4484
4545
  },
4485
- snackbar ? /* @__PURE__ */ React11.createElement(Alert6, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4546
+ snackbar ? /* @__PURE__ */ React12.createElement(Alert6, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4486
4547
  ));
4487
4548
  };
4488
4549
  }
@@ -4490,7 +4551,7 @@ var init_LiteLLMPage = __esm({
4490
4551
 
4491
4552
  // src/plugin.tsx
4492
4553
  init_api();
4493
- import React12 from "react";
4554
+ import React13 from "react";
4494
4555
  import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
4495
4556
  import {
4496
4557
  createFrontendPlugin,
@@ -4509,10 +4570,10 @@ var liteLlmPage = PageBlueprint.make({
4509
4570
  params: {
4510
4571
  path: "/litellm",
4511
4572
  title: "LiteLLM",
4512
- icon: /* @__PURE__ */ React12.createElement(TrendingUpIcon, null),
4573
+ icon: /* @__PURE__ */ React13.createElement(TrendingUpIcon, null),
4513
4574
  loader: async () => {
4514
4575
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
4515
- return /* @__PURE__ */ React12.createElement(LiteLLMPage2, null);
4576
+ return /* @__PURE__ */ React13.createElement(LiteLLMPage2, null);
4516
4577
  }
4517
4578
  }
4518
4579
  });
@@ -4531,10 +4592,10 @@ init_TeamUsage();
4531
4592
  // src/components/LiteLLMHomeWidget.tsx
4532
4593
  init_api();
4533
4594
  init_format();
4534
- import React13, { useState as useState10, useEffect as useEffect4 } from "react";
4535
- import Paper8 from "@mui/material/Paper";
4536
- import Box12 from "@mui/material/Box";
4537
- import Typography12 from "@mui/material/Typography";
4595
+ import React14, { useState as useState10, useEffect as useEffect5 } from "react";
4596
+ import Paper9 from "@mui/material/Paper";
4597
+ import Box13 from "@mui/material/Box";
4598
+ import Typography13 from "@mui/material/Typography";
4538
4599
  import FormControl2 from "@mui/material/FormControl";
4539
4600
  import Select2 from "@mui/material/Select";
4540
4601
  import MenuItem6 from "@mui/material/MenuItem";
@@ -4555,7 +4616,7 @@ function presetToDateRange(preset) {
4555
4616
  }
4556
4617
  return { start, end };
4557
4618
  }
4558
- 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));
4619
+ var Kpi = ({ label, value }) => /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Typography13, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React14.createElement(Typography13, { variant: "subtitle1", fontWeight: 600 }, value));
4559
4620
  var LiteLLMHomeWidget = ({
4560
4621
  defaultPeriod = "7d",
4561
4622
  title = "LiteLLM Usage"
@@ -4567,7 +4628,7 @@ var LiteLLMHomeWidget = ({
4567
4628
  const [keysError, setKeysError] = useState10(null);
4568
4629
  const [usage, setUsage] = useState10(null);
4569
4630
  const [keys, setKeys] = useState10([]);
4570
- useEffect4(() => {
4631
+ useEffect5(() => {
4571
4632
  let cancelled = false;
4572
4633
  setLoading(true);
4573
4634
  setUsageError(null);
@@ -4604,17 +4665,17 @@ var LiteLLMHomeWidget = ({
4604
4665
  spend: d.spend
4605
4666
  }));
4606
4667
  const hasSparkline = dailyData.length > 0;
4607
- 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(
4668
+ return /* @__PURE__ */ React14.createElement(Paper9, { sx: { p: 2 } }, /* @__PURE__ */ React14.createElement(Box13, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React14.createElement(Typography13, { variant: "h6" }, title), /* @__PURE__ */ React14.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React14.createElement(
4608
4669
  Select2,
4609
4670
  {
4610
4671
  value: period,
4611
4672
  onChange: (e) => setPeriod(e.target.value),
4612
4673
  displayEmpty: true
4613
4674
  },
4614
- /* @__PURE__ */ React13.createElement(MenuItem6, { value: "today" }, "Today"),
4615
- /* @__PURE__ */ React13.createElement(MenuItem6, { value: "7d" }, "7d"),
4616
- /* @__PURE__ */ React13.createElement(MenuItem6, { value: "30d" }, "30d")
4617
- ))), 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(
4675
+ /* @__PURE__ */ React14.createElement(MenuItem6, { value: "today" }, "Today"),
4676
+ /* @__PURE__ */ React14.createElement(MenuItem6, { value: "7d" }, "7d"),
4677
+ /* @__PURE__ */ React14.createElement(MenuItem6, { value: "30d" }, "30d")
4678
+ ))), loading && /* @__PURE__ */ React14.createElement(Box13, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React14.createElement(CircularProgress7, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React14.createElement(Alert7, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React14.createElement(React14.Fragment, null, partialFailure && /* @__PURE__ */ React14.createElement(Alert7, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React14.createElement(Grid3, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React14.createElement(Box13, { height: 120 }, /* @__PURE__ */ React14.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React14.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React14.createElement(
4618
4679
  Area3,
4619
4680
  {
4620
4681
  type: "monotone",
@@ -4635,21 +4696,31 @@ init_LiteLLMBudgetWidget();
4635
4696
  init_api();
4636
4697
  init_format();
4637
4698
  init_ui();
4699
+ init_BudgetLimitList();
4638
4700
  init_budget();
4639
- import React14, { useEffect as useEffect5, useMemo as useMemo10, useState as useState11 } from "react";
4640
- import Paper9 from "@mui/material/Paper";
4641
- import Box13 from "@mui/material/Box";
4642
- import Typography13 from "@mui/material/Typography";
4701
+ import React15, { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo10, useState as useState11 } from "react";
4702
+ import Paper10 from "@mui/material/Paper";
4703
+ import Box14 from "@mui/material/Box";
4704
+ import Button6 from "@mui/material/Button";
4705
+ import Typography14 from "@mui/material/Typography";
4643
4706
  import CircularProgress8 from "@mui/material/CircularProgress";
4644
4707
  import Alert8 from "@mui/material/Alert";
4708
+ import Collapse4 from "@mui/material/Collapse";
4709
+ import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
4645
4710
  import { useApi as useApi5 } from "@backstage/core-plugin-api";
4646
4711
  import { Link as Link2 } from "@backstage/core-components";
4647
- var DEFAULT_KEYS_HREF = "/litellm?tab=keys";
4712
+ var MODULE_PATH = "/litellm";
4648
4713
  var LEVEL = {
4649
4714
  key: { name: "Key", none: "No key budget" },
4650
4715
  user: { name: "User", none: "No personal budget" },
4651
4716
  team: { name: "Team", none: "No team budget" }
4652
4717
  };
4718
+ var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
4719
+ var CTA_LABELS = {
4720
+ "new-key": "New key",
4721
+ module: "Open module",
4722
+ "all-limits": "All limits"
4723
+ };
4653
4724
  function fmtUsdShort(n) {
4654
4725
  const v = n ?? 0;
4655
4726
  if (v >= 100 && Number.isInteger(v)) return `$${v}`;
@@ -4672,8 +4743,8 @@ var LevelGauge = ({
4672
4743
  const limit = gauge.limit;
4673
4744
  const tone = limit ? budgetTone2(limit.pct) : "neutral";
4674
4745
  const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
4675
- return /* @__PURE__ */ React14.createElement(
4676
- Box13,
4746
+ return /* @__PURE__ */ React15.createElement(
4747
+ Box14,
4677
4748
  {
4678
4749
  sx: {
4679
4750
  display: "flex",
@@ -4683,7 +4754,7 @@ var LevelGauge = ({
4683
4754
  flex: 1
4684
4755
  }
4685
4756
  },
4686
- /* @__PURE__ */ React14.createElement(
4757
+ /* @__PURE__ */ React15.createElement(
4687
4758
  Gauge,
4688
4759
  {
4689
4760
  value: limit?.pct ?? 0,
@@ -4692,8 +4763,8 @@ var LevelGauge = ({
4692
4763
  label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
4693
4764
  }
4694
4765
  ),
4695
- /* @__PURE__ */ React14.createElement(
4696
- Typography13,
4766
+ /* @__PURE__ */ React15.createElement(
4767
+ Typography14,
4697
4768
  {
4698
4769
  sx: (theme) => ({
4699
4770
  mt: 0.75,
@@ -4706,8 +4777,8 @@ var LevelGauge = ({
4706
4777
  },
4707
4778
  name
4708
4779
  ),
4709
- limit ? /* @__PURE__ */ React14.createElement(React14.Fragment, null, /* @__PURE__ */ React14.createElement(
4710
- Typography13,
4780
+ limit ? /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
4781
+ Typography14,
4711
4782
  {
4712
4783
  variant: "caption",
4713
4784
  title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
@@ -4722,8 +4793,8 @@ var LevelGauge = ({
4722
4793
  }
4723
4794
  },
4724
4795
  limit.label
4725
- ), /* @__PURE__ */ React14.createElement(
4726
- Typography13,
4796
+ ), /* @__PURE__ */ React15.createElement(
4797
+ Typography14,
4727
4798
  {
4728
4799
  variant: "caption",
4729
4800
  color: "text.secondary",
@@ -4734,8 +4805,8 @@ var LevelGauge = ({
4734
4805
  }
4735
4806
  },
4736
4807
  spendCaption(limit)
4737
- ), extra > 0 ? /* @__PURE__ */ React14.createElement(
4738
- Typography13,
4808
+ ), extra > 0 ? /* @__PURE__ */ React15.createElement(
4809
+ Typography14,
4739
4810
  {
4740
4811
  component: Link2,
4741
4812
  to: keysHref,
@@ -4750,8 +4821,8 @@ var LevelGauge = ({
4750
4821
  "+",
4751
4822
  extra,
4752
4823
  " more"
4753
- ) : /* @__PURE__ */ React14.createElement(Typography13, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ React14.createElement(
4754
- Typography13,
4824
+ ) : /* @__PURE__ */ React15.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ React15.createElement(
4825
+ Typography14,
4755
4826
  {
4756
4827
  variant: "caption",
4757
4828
  color: "text.secondary",
@@ -4764,7 +4835,14 @@ var LevelGauge = ({
4764
4835
  var LiteLLMBudgetGauges = ({
4765
4836
  title = "Budget",
4766
4837
  size = 72,
4767
- keysHref = DEFAULT_KEYS_HREF,
4838
+ keysHref,
4839
+ moduleHref = MODULE_PATH,
4840
+ onCreateKey,
4841
+ ctas,
4842
+ maxExpandedKeys = 8,
4843
+ expanded,
4844
+ defaultExpanded = false,
4845
+ onExpandedChange,
4768
4846
  action
4769
4847
  }) => {
4770
4848
  const api = useApi5(liteLlmApiRef);
@@ -4773,7 +4851,16 @@ var LiteLLMBudgetGauges = ({
4773
4851
  const [user, setUser] = useState11(null);
4774
4852
  const [teams, setTeams] = useState11([]);
4775
4853
  const [keys, setKeys] = useState11([]);
4776
- useEffect5(() => {
4854
+ const isControlled = expanded !== void 0;
4855
+ const [uncontrolledExpanded, setUncontrolledExpanded] = useState11(defaultExpanded);
4856
+ const isExpanded = isControlled ? expanded : uncontrolledExpanded;
4857
+ const toggleExpanded = useCallback3(() => {
4858
+ const next = !isExpanded;
4859
+ if (!isControlled) setUncontrolledExpanded(next);
4860
+ onExpandedChange?.(next);
4861
+ }, [isControlled, isExpanded, onExpandedChange]);
4862
+ const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
4863
+ useEffect6(() => {
4777
4864
  let cancelled = false;
4778
4865
  setLoading(true);
4779
4866
  setError(null);
@@ -4803,23 +4890,103 @@ var LiteLLMBudgetGauges = ({
4803
4890
  );
4804
4891
  const gauges = useMemo10(() => buildBudgetGauges(summary), [summary]);
4805
4892
  const headline = useMemo10(() => budgetHeadline(summary), [summary]);
4893
+ const expandedSummary = useMemo10(
4894
+ () => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
4895
+ [user, teams, keys, maxExpandedKeys]
4896
+ );
4897
+ const expandedLimits = useMemo10(
4898
+ () => allBudgetLimits(expandedSummary),
4899
+ [expandedSummary]
4900
+ );
4806
4901
  const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
4807
- return /* @__PURE__ */ React14.createElement(Paper9, { sx: { p: 2 } }, /* @__PURE__ */ React14.createElement(Box13, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ React14.createElement(Typography13, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ React14.createElement(
4902
+ const resolvedCtas = useMemo10(() => {
4903
+ const list = ctas ?? DEFAULT_CTAS;
4904
+ return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
4905
+ }, [ctas, headline.count]);
4906
+ const renderCta = (cta, index) => {
4907
+ const label = cta.label ?? CTA_LABELS[cta.kind];
4908
+ const key = `${cta.kind}-${index}`;
4909
+ switch (cta.kind) {
4910
+ case "new-key":
4911
+ return onCreateKey ? /* @__PURE__ */ React15.createElement(Button6, { key, size: "small", variant: "contained", onClick: onCreateKey }, label) : /* @__PURE__ */ React15.createElement(
4912
+ Button6,
4913
+ {
4914
+ key,
4915
+ size: "small",
4916
+ variant: "contained",
4917
+ component: Link2,
4918
+ to: `${moduleHref}?generate=1`
4919
+ },
4920
+ label
4921
+ );
4922
+ case "module":
4923
+ return /* @__PURE__ */ React15.createElement(Button6, { key, size: "small", variant: "outlined", component: Link2, to: moduleHref }, label);
4924
+ case "all-limits":
4925
+ return /* @__PURE__ */ React15.createElement(
4926
+ Button6,
4927
+ {
4928
+ key,
4929
+ size: "small",
4930
+ variant: "text",
4931
+ onClick: toggleExpanded,
4932
+ endIcon: /* @__PURE__ */ React15.createElement(
4933
+ ExpandMore4,
4934
+ {
4935
+ sx: {
4936
+ transform: isExpanded ? "rotate(180deg)" : "none",
4937
+ transition: (theme) => theme.transitions.create("transform")
4938
+ }
4939
+ }
4940
+ )
4941
+ },
4942
+ label
4943
+ );
4944
+ /* istanbul ignore next — exhaustiveness */
4945
+ default:
4946
+ return null;
4947
+ }
4948
+ };
4949
+ const hasFooter = resolvedCtas.length > 0 || !!action;
4950
+ return /* @__PURE__ */ React15.createElement(Paper10, { sx: { p: 2 } }, /* @__PURE__ */ React15.createElement(Box14, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ React15.createElement(Typography14, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ React15.createElement(
4808
4951
  StatusPill,
4809
4952
  {
4810
4953
  label: summaryText,
4811
4954
  tone: budgetTone2(headline.closest?.pct ?? 0),
4812
4955
  dot: headline.count > 0
4813
4956
  }
4814
- )), loading && /* @__PURE__ */ React14.createElement(Box13, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ React14.createElement(CircularProgress8, { size: 28 })), !loading && error && /* @__PURE__ */ React14.createElement(Alert8, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ React14.createElement(Box13, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ React14.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref }))), action && /* @__PURE__ */ React14.createElement(
4815
- Box13,
4957
+ )), loading && /* @__PURE__ */ React15.createElement(Box14, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ React15.createElement(CircularProgress8, { size: 28 })), !loading && error && /* @__PURE__ */ React15.createElement(Alert8, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Box14, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ React15.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref: keysTabHref }))), /* @__PURE__ */ React15.createElement(Collapse4, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ React15.createElement(
4958
+ Box14,
4959
+ {
4960
+ sx: {
4961
+ mt: 1.5,
4962
+ // Bound the expanded list so a user with many limits still
4963
+ // keeps the card compact and the toggle within reach.
4964
+ maxHeight: 300,
4965
+ overflowY: "auto"
4966
+ }
4967
+ },
4968
+ /* @__PURE__ */ React15.createElement(LimitListPanel, null, /* @__PURE__ */ React15.createElement(
4969
+ BudgetLimitList,
4970
+ {
4971
+ limits: expandedLimits,
4972
+ hiddenKeyCount: expandedSummary.hiddenBudgetedKeys,
4973
+ keysHref: keysTabHref
4974
+ }
4975
+ ))
4976
+ ))), hasFooter && /* @__PURE__ */ React15.createElement(
4977
+ Box14,
4816
4978
  {
4817
4979
  sx: (theme) => ({
4818
4980
  mt: 2,
4819
4981
  pt: 2,
4820
- borderTop: `1px solid ${theme.palette.divider}`
4982
+ borderTop: `1px solid ${theme.palette.divider}`,
4983
+ display: "flex",
4984
+ alignItems: "center",
4985
+ gap: 1,
4986
+ flexWrap: "wrap"
4821
4987
  })
4822
4988
  },
4989
+ resolvedCtas.map(renderCta),
4823
4990
  action
4824
4991
  ));
4825
4992
  };