@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.cjs.js CHANGED
@@ -2931,6 +2931,13 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
2931
2931
  teams: teamLimits
2932
2932
  };
2933
2933
  }
2934
+ function allBudgetLimits(summary) {
2935
+ return [
2936
+ ...summary.keys,
2937
+ ...summary.user ? [summary.user] : [],
2938
+ ...summary.teams
2939
+ ];
2940
+ }
2934
2941
  function buildBudgetGauges(summary) {
2935
2942
  const byLevel = {
2936
2943
  key: summary.keys,
@@ -2981,7 +2988,7 @@ var init_budget = __esm({
2981
2988
  }
2982
2989
  });
2983
2990
 
2984
- // src/components/LiteLLMBudgetWidget.tsx
2991
+ // src/components/BudgetLimitList.tsx
2985
2992
  function levelToneColor(tone, theme) {
2986
2993
  switch (tone) {
2987
2994
  case "success":
@@ -2996,25 +3003,125 @@ function levelToneColor(tone, theme) {
2996
3003
  return theme.palette.primary.main;
2997
3004
  }
2998
3005
  }
2999
- var import_react7, import_Paper5, import_Box7, import_Typography7, import_CircularProgress4, import_Alert3, import_Collapse, import_IconButton4, import_icons_material5, import_styles4, import_core_plugin_api3, import_core_components, LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, MoreKeysNote, HierarchyNote, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
3000
- var init_LiteLLMBudgetWidget = __esm({
3001
- "src/components/LiteLLMBudgetWidget.tsx"() {
3006
+ var import_react7, import_Box7, import_Paper5, import_Typography7, import_styles4, import_core_components, LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
3007
+ var init_BudgetLimitList = __esm({
3008
+ "src/components/BudgetLimitList.tsx"() {
3002
3009
  "use strict";
3003
3010
  import_react7 = __toESM(require("react"));
3004
- import_Paper5 = __toESM(require("@mui/material/Paper"));
3005
3011
  import_Box7 = __toESM(require("@mui/material/Box"));
3012
+ import_Paper5 = __toESM(require("@mui/material/Paper"));
3006
3013
  import_Typography7 = __toESM(require("@mui/material/Typography"));
3014
+ import_styles4 = require("@mui/material/styles");
3015
+ import_core_components = require("@backstage/core-components");
3016
+ init_budget();
3017
+ init_format();
3018
+ init_ui();
3019
+ LimitCard = ({ limit }) => {
3020
+ const tone = budgetTone2(limit.pct);
3021
+ const pct = Math.round(limit.pct);
3022
+ const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
3023
+ return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react7.default.createElement(
3024
+ import_Typography7.default,
3025
+ {
3026
+ variant: "body2",
3027
+ sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
3028
+ title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
3029
+ },
3030
+ limit.label
3031
+ ), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
3032
+ import_Typography7.default,
3033
+ {
3034
+ variant: "caption",
3035
+ color: "text.secondary",
3036
+ sx: {
3037
+ fontSize: 10.5,
3038
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3039
+ display: "block",
3040
+ overflow: "hidden",
3041
+ textOverflow: "ellipsis",
3042
+ whiteSpace: "nowrap"
3043
+ }
3044
+ },
3045
+ limit.sublabel
3046
+ )), limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 1 } }, /* @__PURE__ */ import_react7.default.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ import_react7.default.createElement(
3047
+ import_Box7.default,
3048
+ {
3049
+ sx: {
3050
+ display: "flex",
3051
+ justifyContent: "space-between",
3052
+ alignItems: "center",
3053
+ gap: 1,
3054
+ mt: 0.75
3055
+ }
3056
+ },
3057
+ /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, "Hidden by admin") : /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
3058
+ /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3059
+ ));
3060
+ };
3061
+ TaggedLimit = ({ limit }) => {
3062
+ const theme = (0, import_styles4.useTheme)();
3063
+ const tone = budgetTone2(limit.pct);
3064
+ return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
3065
+ import_Typography7.default,
3066
+ {
3067
+ sx: {
3068
+ fontSize: 11,
3069
+ fontWeight: 700,
3070
+ letterSpacing: "0.08em",
3071
+ textTransform: "uppercase",
3072
+ color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
3073
+ mb: 0.5
3074
+ }
3075
+ },
3076
+ limit.kind
3077
+ ), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
3078
+ };
3079
+ MoreKeysNote = ({
3080
+ count,
3081
+ href,
3082
+ label
3083
+ }) => {
3084
+ const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
3085
+ if (!href) {
3086
+ return /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, text);
3087
+ }
3088
+ return /* @__PURE__ */ import_react7.default.createElement(import_core_components.Link, { to: href, variant: "caption" }, text, " \u2192");
3089
+ };
3090
+ BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, limits.map((limit) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: `${limit.kind}:${limit.sublabel ?? limit.label}`, limit })), hiddenKeyCount > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: hiddenKeyCount, href: keysHref }));
3091
+ LimitListPanel = ({ children }) => /* @__PURE__ */ import_react7.default.createElement(
3092
+ import_Box7.default,
3093
+ {
3094
+ sx: (theme) => ({
3095
+ px: 1.5,
3096
+ py: 1.5,
3097
+ borderRadius: 1.5,
3098
+ bgcolor: (0, import_styles4.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.02)
3099
+ })
3100
+ },
3101
+ children
3102
+ );
3103
+ }
3104
+ });
3105
+
3106
+ // src/components/LiteLLMBudgetWidget.tsx
3107
+ var import_react8, import_Paper6, import_Box8, import_Typography8, import_CircularProgress4, import_Alert3, import_Collapse, import_IconButton4, import_icons_material5, import_styles5, import_core_plugin_api3, LevelFrame, EmptyLimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, HierarchyNote, FullBody, CompactBody, LiteLLMBudgetWidget;
3108
+ var init_LiteLLMBudgetWidget = __esm({
3109
+ "src/components/LiteLLMBudgetWidget.tsx"() {
3110
+ "use strict";
3111
+ import_react8 = __toESM(require("react"));
3112
+ import_Paper6 = __toESM(require("@mui/material/Paper"));
3113
+ import_Box8 = __toESM(require("@mui/material/Box"));
3114
+ import_Typography8 = __toESM(require("@mui/material/Typography"));
3007
3115
  import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
3008
3116
  import_Alert3 = __toESM(require("@mui/material/Alert"));
3009
3117
  import_Collapse = __toESM(require("@mui/material/Collapse"));
3010
3118
  import_IconButton4 = __toESM(require("@mui/material/IconButton"));
3011
3119
  import_icons_material5 = require("@mui/icons-material");
3012
- import_styles4 = require("@mui/material/styles");
3120
+ import_styles5 = require("@mui/material/styles");
3013
3121
  import_core_plugin_api3 = require("@backstage/core-plugin-api");
3014
- import_core_components = require("@backstage/core-components");
3015
3122
  init_api();
3016
- init_format();
3017
3123
  init_ui();
3124
+ init_BudgetLimitList();
3018
3125
  init_budget();
3019
3126
  LevelFrame = ({
3020
3127
  rank,
@@ -3024,8 +3131,8 @@ var init_LiteLLMBudgetWidget = __esm({
3024
3131
  tone,
3025
3132
  isLast,
3026
3133
  children
3027
- }) => /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ import_react7.default.createElement(
3028
- import_Box7.default,
3134
+ }) => /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ import_react8.default.createElement(
3135
+ import_Box8.default,
3029
3136
  {
3030
3137
  sx: (theme) => ({
3031
3138
  width: 26,
@@ -3034,7 +3141,7 @@ var init_LiteLLMBudgetWidget = __esm({
3034
3141
  display: "flex",
3035
3142
  alignItems: "center",
3036
3143
  justifyContent: "center",
3037
- bgcolor: (0, import_styles4.alpha)(levelToneColor(tone, theme), 0.14),
3144
+ bgcolor: (0, import_styles5.alpha)(levelToneColor(tone, theme), 0.14),
3038
3145
  color: levelToneColor(tone, theme),
3039
3146
  fontSize: 12,
3040
3147
  fontWeight: 700,
@@ -3042,18 +3149,18 @@ var init_LiteLLMBudgetWidget = __esm({
3042
3149
  })
3043
3150
  },
3044
3151
  rank
3045
- ), /* @__PURE__ */ import_react7.default.createElement(
3046
- import_Box7.default,
3152
+ ), /* @__PURE__ */ import_react8.default.createElement(
3153
+ import_Box8.default,
3047
3154
  {
3048
3155
  sx: (theme) => ({
3049
3156
  flex: 1,
3050
3157
  width: 2,
3051
3158
  minHeight: 14,
3052
- bgcolor: isLast ? "transparent" : (0, import_styles4.alpha)(theme.palette.text.primary, 0.12)
3159
+ bgcolor: isLast ? "transparent" : (0, import_styles5.alpha)(theme.palette.text.primary, 0.12)
3053
3160
  })
3054
3161
  }
3055
- )), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ import_react7.default.createElement(
3056
- import_Typography7.default,
3162
+ )), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ import_react8.default.createElement(
3163
+ import_Typography8.default,
3057
3164
  {
3058
3165
  sx: (theme) => ({
3059
3166
  fontSize: 12.5,
@@ -3064,102 +3171,42 @@ var init_LiteLLMBudgetWidget = __esm({
3064
3171
  })
3065
3172
  },
3066
3173
  name
3067
- ), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ import_react7.default.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
3068
- EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react7.default.createElement(
3069
- import_Paper5.default,
3174
+ ), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ import_react8.default.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
3175
+ EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react8.default.createElement(
3176
+ import_Paper6.default,
3070
3177
  {
3071
3178
  variant: "outlined",
3072
3179
  sx: (theme) => ({
3073
3180
  px: 1.5,
3074
3181
  py: 1.25,
3075
3182
  borderRadius: 1.5,
3076
- bgcolor: (0, import_styles4.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
3183
+ bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
3077
3184
  })
3078
3185
  },
3079
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3186
+ /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3080
3187
  );
3081
- LimitCard = ({ limit }) => {
3082
- const tone = budgetTone2(limit.pct);
3083
- const pct = Math.round(limit.pct);
3084
- const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
3085
- return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react7.default.createElement(
3086
- import_Typography7.default,
3087
- {
3088
- variant: "body2",
3089
- sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
3090
- title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
3091
- },
3092
- limit.label
3093
- ), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
3094
- import_Typography7.default,
3095
- {
3096
- variant: "caption",
3097
- color: "text.secondary",
3098
- sx: {
3099
- fontSize: 10.5,
3100
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3101
- display: "block",
3102
- overflow: "hidden",
3103
- textOverflow: "ellipsis",
3104
- whiteSpace: "nowrap"
3105
- }
3106
- },
3107
- limit.sublabel
3108
- )), limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 1 } }, /* @__PURE__ */ import_react7.default.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ import_react7.default.createElement(
3109
- import_Box7.default,
3110
- {
3111
- sx: {
3112
- display: "flex",
3113
- justifyContent: "space-between",
3114
- alignItems: "center",
3115
- gap: 1,
3116
- mt: 0.75
3117
- }
3118
- },
3119
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, "Hidden by admin") : /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
3120
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3121
- ));
3122
- };
3123
3188
  CLOSEST_LEVEL_LABEL = {
3124
3189
  key: "key",
3125
3190
  user: "personal",
3126
3191
  team: "team"
3127
3192
  };
3128
3193
  KEYS_PATH = "/litellm?tab=keys";
3129
- MoreKeysNote = ({ count }) => /* @__PURE__ */ import_react7.default.createElement(import_core_components.Link, { to: KEYS_PATH, variant: "caption" }, "+", count, " more budgeted key", count > 1 ? "s" : "", " further from the cap \u2192");
3130
- HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react7.default.createElement(
3131
- import_Box7.default,
3194
+ HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react8.default.createElement(
3195
+ import_Box8.default,
3132
3196
  {
3133
3197
  sx: (theme) => ({
3134
3198
  px: 1.5,
3135
3199
  py: 1.25,
3136
3200
  borderRadius: 1.5,
3137
3201
  border: "1px dashed",
3138
- borderColor: (0, import_styles4.alpha)(theme.palette.text.primary, 0.18)
3202
+ borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
3139
3203
  })
3140
3204
  },
3141
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react7.default.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.")
3205
+ /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react8.default.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.")
3142
3206
  );
3143
- TaggedLimit = ({ limit }) => {
3144
- const tone = budgetTone2(limit.pct);
3145
- return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
3146
- import_Typography7.default,
3147
- {
3148
- sx: (theme) => ({
3149
- fontSize: 11,
3150
- fontWeight: 700,
3151
- letterSpacing: "0.08em",
3152
- textTransform: "uppercase",
3153
- color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
3154
- mb: 0.5
3155
- })
3156
- },
3157
- limit.kind
3158
- ), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
3159
- };
3160
3207
  FullBody = ({ summary }) => {
3161
3208
  const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
3162
- return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(
3209
+ return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(
3163
3210
  LevelFrame,
3164
3211
  {
3165
3212
  rank: 1,
@@ -3167,10 +3214,10 @@ var init_LiteLLMBudgetWidget = __esm({
3167
3214
  tagline: "per-key cap \xB7 highest priority",
3168
3215
  note: hasBudgetedKeys ? void 0 : "no key budgets set"
3169
3216
  },
3170
- !hasBudgetedKeys && /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3171
- summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3172
- summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys })
3173
- ), /* @__PURE__ */ import_react7.default.createElement(
3217
+ !hasBudgetedKeys && /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3218
+ summary.keys.map((k) => /* @__PURE__ */ import_react8.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3219
+ summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
3220
+ ), /* @__PURE__ */ import_react8.default.createElement(
3174
3221
  LevelFrame,
3175
3222
  {
3176
3223
  rank: 2,
@@ -3178,15 +3225,15 @@ var init_LiteLLMBudgetWidget = __esm({
3178
3225
  tagline: "cap on everything you spend with your own keys",
3179
3226
  note: "skipped for team-bound keys"
3180
3227
  },
3181
- summary.user ? /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3182
- ), /* @__PURE__ */ import_react7.default.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__ */ import_react7.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react7.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react7.default.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__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react7.default.createElement(HierarchyNote, { withReset: true })));
3228
+ summary.user ? /* @__PURE__ */ import_react8.default.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3229
+ ), /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react8.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement(import_Box8.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react8.default.createElement(HierarchyNote, { withReset: true })));
3183
3230
  };
3184
3231
  CompactBody = ({ summary }) => {
3185
3232
  const { count } = budgetHeadline(summary);
3186
3233
  if (count === 0) {
3187
- return /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3234
+ return /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3188
3235
  }
3189
- return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react7.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys }), summary.user && /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3236
+ return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react8.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3190
3237
  };
3191
3238
  LiteLLMBudgetWidget = ({
3192
3239
  title = "Budget Policy",
@@ -3197,13 +3244,13 @@ var init_LiteLLMBudgetWidget = __esm({
3197
3244
  action
3198
3245
  }) => {
3199
3246
  const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
3200
- const [loading, setLoading] = (0, import_react7.useState)(true);
3201
- const [error, setError] = (0, import_react7.useState)(null);
3202
- const [user, setUser] = (0, import_react7.useState)(null);
3203
- const [teams, setTeams] = (0, import_react7.useState)([]);
3204
- const [keys, setKeys] = (0, import_react7.useState)([]);
3205
- const [expanded, setExpanded] = (0, import_react7.useState)(defaultExpanded);
3206
- (0, import_react7.useEffect)(() => {
3247
+ const [loading, setLoading] = (0, import_react8.useState)(true);
3248
+ const [error, setError] = (0, import_react8.useState)(null);
3249
+ const [user, setUser] = (0, import_react8.useState)(null);
3250
+ const [teams, setTeams] = (0, import_react8.useState)([]);
3251
+ const [keys, setKeys] = (0, import_react8.useState)([]);
3252
+ const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
3253
+ (0, import_react8.useEffect)(() => {
3207
3254
  let cancelled = false;
3208
3255
  setLoading(true);
3209
3256
  setError(null);
@@ -3227,19 +3274,19 @@ var init_LiteLLMBudgetWidget = __esm({
3227
3274
  cancelled = true;
3228
3275
  };
3229
3276
  }, [api]);
3230
- const summary = (0, import_react7.useMemo)(
3277
+ const summary = (0, import_react8.useMemo)(
3231
3278
  () => buildBudgetSummary(user, teams, keys, maxKeys),
3232
3279
  [user, teams, keys, maxKeys]
3233
3280
  );
3234
- const headline = (0, import_react7.useMemo)(() => budgetHeadline(summary), [summary]);
3281
+ const headline = (0, import_react8.useMemo)(() => budgetHeadline(summary), [summary]);
3235
3282
  const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
3236
3283
  const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
3237
3284
  headline.closest.pct
3238
3285
  )}%`;
3239
3286
  const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
3240
- const body = /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, loading && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ import_react7.default.createElement(import_CircularProgress4.default, { size: 32 })), !loading && error && /* @__PURE__ */ import_react7.default.createElement(import_Alert3.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ import_react7.default.createElement(CompactBody, { summary }) : /* @__PURE__ */ import_react7.default.createElement(FullBody, { summary })));
3241
- return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react7.default.createElement(
3242
- import_Box7.default,
3287
+ const body = /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, loading && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ import_react8.default.createElement(import_CircularProgress4.default, { size: 32 })), !loading && error && /* @__PURE__ */ import_react8.default.createElement(import_Alert3.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ import_react8.default.createElement(CompactBody, { summary }) : /* @__PURE__ */ import_react8.default.createElement(FullBody, { summary })));
3288
+ return /* @__PURE__ */ import_react8.default.createElement(import_Paper6.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react8.default.createElement(
3289
+ import_Box8.default,
3243
3290
  {
3244
3291
  onClick: () => setExpanded((e) => !e),
3245
3292
  sx: {
@@ -3251,14 +3298,14 @@ var init_LiteLLMBudgetWidget = __esm({
3251
3298
  mb: expanded ? 1.5 : 0
3252
3299
  }
3253
3300
  },
3254
- /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 0.5 } }, /* @__PURE__ */ import_react7.default.createElement(
3301
+ /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { mt: 0.5 } }, /* @__PURE__ */ import_react8.default.createElement(
3255
3302
  StatusPill,
3256
3303
  {
3257
3304
  label: summaryText,
3258
3305
  tone: budgetTone2(headline.closest?.pct ?? 0)
3259
3306
  }
3260
3307
  ))),
3261
- /* @__PURE__ */ import_react7.default.createElement(
3308
+ /* @__PURE__ */ import_react8.default.createElement(
3262
3309
  import_IconButton4.default,
3263
3310
  {
3264
3311
  size: "small",
@@ -3274,10 +3321,10 @@ var init_LiteLLMBudgetWidget = __esm({
3274
3321
  transition: theme.transitions.create("transform")
3275
3322
  })
3276
3323
  },
3277
- /* @__PURE__ */ import_react7.default.createElement(import_icons_material5.ExpandMore, null)
3324
+ /* @__PURE__ */ import_react8.default.createElement(import_icons_material5.ExpandMore, null)
3278
3325
  )
3279
- ) : /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ import_react7.default.createElement(import_Collapse.default, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ import_react7.default.createElement(
3280
- import_Box7.default,
3326
+ ) : /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ import_react8.default.createElement(import_Collapse.default, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ import_react8.default.createElement(
3327
+ import_Box8.default,
3281
3328
  {
3282
3329
  sx: (theme) => ({
3283
3330
  mt: 2,
@@ -3306,14 +3353,14 @@ function budgetStatValue(hidden, budget) {
3306
3353
  if (budget > 0) return fmtUsd2(budget);
3307
3354
  return "Unlimited";
3308
3355
  }
3309
- var import_react8, import_Paper6, import_Box8, import_Typography8, import_Skeleton4, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton5, import_Button5, import_CircularProgress5, import_styles5, import_icons_material6, import_recharts2, fmtUsd2, TeamCard, TeamUsage;
3356
+ var import_react9, import_Paper7, import_Box9, import_Typography9, import_Skeleton4, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton5, import_Button5, import_CircularProgress5, import_styles6, import_icons_material6, import_recharts2, fmtUsd2, TeamCard, TeamUsage;
3310
3357
  var init_TeamUsage = __esm({
3311
3358
  "src/components/TeamUsage.tsx"() {
3312
3359
  "use strict";
3313
- import_react8 = __toESM(require("react"));
3314
- import_Paper6 = __toESM(require("@mui/material/Paper"));
3315
- import_Box8 = __toESM(require("@mui/material/Box"));
3316
- import_Typography8 = __toESM(require("@mui/material/Typography"));
3360
+ import_react9 = __toESM(require("react"));
3361
+ import_Paper7 = __toESM(require("@mui/material/Paper"));
3362
+ import_Box9 = __toESM(require("@mui/material/Box"));
3363
+ import_Typography9 = __toESM(require("@mui/material/Typography"));
3317
3364
  import_Skeleton4 = __toESM(require("@mui/material/Skeleton"));
3318
3365
  import_Divider3 = __toESM(require("@mui/material/Divider"));
3319
3366
  import_Table4 = __toESM(require("@mui/material/Table"));
@@ -3326,13 +3373,13 @@ var init_TeamUsage = __esm({
3326
3373
  import_IconButton5 = __toESM(require("@mui/material/IconButton"));
3327
3374
  import_Button5 = __toESM(require("@mui/material/Button"));
3328
3375
  import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
3329
- import_styles5 = require("@mui/material/styles");
3376
+ import_styles6 = require("@mui/material/styles");
3330
3377
  import_icons_material6 = require("@mui/icons-material");
3331
3378
  import_recharts2 = require("recharts");
3332
3379
  init_ui();
3333
3380
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
3334
3381
  TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
3335
- const [expanded, setExpanded] = (0, import_react8.useState)(false);
3382
+ const [expanded, setExpanded] = (0, import_react9.useState)(false);
3336
3383
  const chart = useChartTheme();
3337
3384
  const hidden = team.budget_hidden === true;
3338
3385
  const budget = hidden ? 0 : team.max_budget ?? 0;
@@ -3344,24 +3391,24 @@ var init_TeamUsage = __esm({
3344
3391
  const renderDailySpendSection = () => {
3345
3392
  if (hidden) return null;
3346
3393
  if (usageLoading) {
3347
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react8.default.createElement(import_CircularProgress5.default, { size: 22 }));
3394
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react9.default.createElement(import_CircularProgress5.default, { size: 22 }));
3348
3395
  }
3349
3396
  if (dailyData.length === 0) return null;
3350
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mb: 2.5 }, /* @__PURE__ */ import_react8.default.createElement(
3351
- import_Typography8.default,
3397
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react9.default.createElement(
3398
+ import_Typography9.default,
3352
3399
  {
3353
3400
  variant: "caption",
3354
3401
  color: "text.secondary",
3355
3402
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
3356
3403
  },
3357
3404
  "Daily spend"
3358
- ), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { height: 150 }, /* @__PURE__ */ import_react8.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react8.default.createElement(import_recharts2.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react8.default.createElement(import_recharts2.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react8.default.createElement(import_recharts2.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react8.default.createElement(import_recharts2.YAxis, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ import_react8.default.createElement(
3405
+ ), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { height: 150 }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react9.default.createElement(import_recharts2.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react9.default.createElement(import_recharts2.YAxis, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ import_react9.default.createElement(
3359
3406
  import_recharts2.Tooltip,
3360
3407
  {
3361
3408
  cursor: chart.cursor,
3362
- content: /* @__PURE__ */ import_react8.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3409
+ content: /* @__PURE__ */ import_react9.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3363
3410
  }
3364
- ), /* @__PURE__ */ import_react8.default.createElement(
3411
+ ), /* @__PURE__ */ import_react9.default.createElement(
3365
3412
  import_recharts2.Area,
3366
3413
  {
3367
3414
  type: "monotone",
@@ -3374,8 +3421,8 @@ var init_TeamUsage = __esm({
3374
3421
  }
3375
3422
  )))));
3376
3423
  };
3377
- const sectionLabel = (label) => /* @__PURE__ */ import_react8.default.createElement(
3378
- import_Typography8.default,
3424
+ const sectionLabel = (label) => /* @__PURE__ */ import_react9.default.createElement(
3425
+ import_Typography9.default,
3379
3426
  {
3380
3427
  variant: "caption",
3381
3428
  color: "text.secondary",
@@ -3383,8 +3430,8 @@ var init_TeamUsage = __esm({
3383
3430
  },
3384
3431
  label
3385
3432
  );
3386
- return /* @__PURE__ */ import_react8.default.createElement(import_Paper6.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ import_react8.default.createElement(
3387
- import_Box8.default,
3433
+ return /* @__PURE__ */ import_react9.default.createElement(import_Paper7.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ import_react9.default.createElement(
3434
+ import_Box9.default,
3388
3435
  {
3389
3436
  sx: (theme) => ({
3390
3437
  width: 36,
@@ -3394,20 +3441,20 @@ var init_TeamUsage = __esm({
3394
3441
  display: "flex",
3395
3442
  alignItems: "center",
3396
3443
  justifyContent: "center",
3397
- bgcolor: (0, import_styles5.alpha)(theme.palette.primary.main, 0.1),
3444
+ bgcolor: (0, import_styles6.alpha)(theme.palette.primary.main, 0.1),
3398
3445
  color: theme.palette.primary.main
3399
3446
  })
3400
3447
  },
3401
- /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.Group, { fontSize: "small" })
3402
- ), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react8.default.createElement(
3403
- import_Typography8.default,
3448
+ /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Group, { fontSize: "small" })
3449
+ ), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react9.default.createElement(
3450
+ import_Typography9.default,
3404
3451
  {
3405
3452
  variant: "caption",
3406
3453
  color: "text.secondary",
3407
3454
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3408
3455
  },
3409
3456
  team.team_id
3410
- )), isOver && /* @__PURE__ */ import_react8.default.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ import_react8.default.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ import_react8.default.createElement(
3457
+ )), isOver && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ import_react9.default.createElement(
3411
3458
  import_Button5.default,
3412
3459
  {
3413
3460
  size: "small",
@@ -3416,7 +3463,7 @@ var init_TeamUsage = __esm({
3416
3463
  sx: { textTransform: "none" }
3417
3464
  },
3418
3465
  "Edit"
3419
- ), /* @__PURE__ */ import_react8.default.createElement(
3466
+ ), /* @__PURE__ */ import_react9.default.createElement(
3420
3467
  import_IconButton5.default,
3421
3468
  {
3422
3469
  size: "small",
@@ -3429,9 +3476,9 @@ var init_TeamUsage = __esm({
3429
3476
  transition: theme.transitions.create("transform")
3430
3477
  })
3431
3478
  },
3432
- /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.ExpandMore, null)
3433
- )), /* @__PURE__ */ import_react8.default.createElement(
3434
- import_Box8.default,
3479
+ /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.ExpandMore, null)
3480
+ )), /* @__PURE__ */ import_react9.default.createElement(
3481
+ import_Box9.default,
3435
3482
  {
3436
3483
  sx: {
3437
3484
  display: "grid",
@@ -3441,35 +3488,35 @@ var init_TeamUsage = __esm({
3441
3488
  maxWidth: 640
3442
3489
  }
3443
3490
  },
3444
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3445
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3446
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3447
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3448
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3449
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3450
- ), hidden && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ import_react8.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { 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__ */ import_react8.default.createElement(import_Box8.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react8.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react8.default.createElement(import_Collapse2.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react8.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react8.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react8.default.createElement(
3491
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3492
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3493
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3494
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3495
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3496
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3497
+ ), hidden && /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ import_react9.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { 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__ */ import_react9.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react9.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react9.default.createElement(import_Collapse2.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react9.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react9.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react9.default.createElement(
3451
3498
  import_TableContainer4.default,
3452
3499
  {
3453
- component: import_Paper6.default,
3500
+ component: import_Paper7.default,
3454
3501
  variant: "outlined",
3455
3502
  sx: { borderRadius: 1.5 }
3456
3503
  },
3457
- /* @__PURE__ */ import_react8.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react8.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, null, "User"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, { align: "right" }, "Role"))), /* @__PURE__ */ import_react8.default.createElement(import_TableBody4.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react8.default.createElement(import_TableRow4.default, { key: m.user_id }, /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, null, m.user_email ? /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react8.default.createElement(
3458
- import_Typography8.default,
3504
+ /* @__PURE__ */ import_react9.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react9.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, null, "User"), /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, { align: "right" }, "Role"))), /* @__PURE__ */ import_react9.default.createElement(import_TableBody4.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react9.default.createElement(import_TableRow4.default, { key: m.user_id }, /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, null, m.user_email ? /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react9.default.createElement(
3505
+ import_Typography9.default,
3459
3506
  {
3460
3507
  variant: "caption",
3461
3508
  color: "text.secondary",
3462
3509
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3463
3510
  },
3464
3511
  m.user_id
3465
- )) : /* @__PURE__ */ import_react8.default.createElement(
3466
- import_Typography8.default,
3512
+ )) : /* @__PURE__ */ import_react9.default.createElement(
3513
+ import_Typography9.default,
3467
3514
  {
3468
3515
  variant: "body2",
3469
3516
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
3470
3517
  },
3471
3518
  m.user_id
3472
- )), /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react8.default.createElement(
3519
+ )), /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(
3473
3520
  StatusPill,
3474
3521
  {
3475
3522
  label: m.role,
@@ -3477,7 +3524,7 @@ var init_TeamUsage = __esm({
3477
3524
  dot: false
3478
3525
  }
3479
3526
  ))))))
3480
- ) : /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3527
+ ) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3481
3528
  };
3482
3529
  TeamUsage = ({
3483
3530
  teams,
@@ -3489,22 +3536,22 @@ var init_TeamUsage = __esm({
3489
3536
  canCreate,
3490
3537
  onCreateTeam
3491
3538
  }) => {
3492
- const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react8.default.createElement(
3539
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react9.default.createElement(
3493
3540
  import_Button5.default,
3494
3541
  {
3495
3542
  variant: "contained",
3496
3543
  color: "primary",
3497
3544
  disableElevation: true,
3498
- startIcon: /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.Add, null),
3545
+ startIcon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Add, null),
3499
3546
  onClick: onCreateTeam
3500
3547
  },
3501
3548
  "Create Team"
3502
3549
  ) : void 0;
3503
3550
  if (loading) {
3504
- return /* @__PURE__ */ import_react8.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react8.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react8.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
3551
+ return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react9.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
3505
3552
  }
3506
3553
  if (!teams.length) {
3507
- return /* @__PURE__ */ import_react8.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react8.default.createElement(
3554
+ return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(
3508
3555
  EmptyState,
3509
3556
  {
3510
3557
  message: "You're not a member of any LiteLLM team yet.",
@@ -3512,14 +3559,14 @@ var init_TeamUsage = __esm({
3512
3559
  }
3513
3560
  ));
3514
3561
  }
3515
- return /* @__PURE__ */ import_react8.default.createElement(
3562
+ return /* @__PURE__ */ import_react9.default.createElement(
3516
3563
  SectionCard,
3517
3564
  {
3518
3565
  title: "Teams",
3519
3566
  subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
3520
3567
  actions: createAction
3521
3568
  },
3522
- /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react8.default.createElement(
3569
+ /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react9.default.createElement(
3523
3570
  TeamCard,
3524
3571
  {
3525
3572
  key: team.team_id,
@@ -3553,12 +3600,12 @@ function fmtTokens(n) {
3553
3600
  if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
3554
3601
  return String(n);
3555
3602
  }
3556
- var import_react9, import_Box9, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography9, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles6, ALL_PROXY_MODELS2, ModelsTable;
3603
+ var import_react10, import_Box10, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography10, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles7, ALL_PROXY_MODELS2, ModelsTable;
3557
3604
  var init_ModelsTable = __esm({
3558
3605
  "src/components/ModelsTable.tsx"() {
3559
3606
  "use strict";
3560
- import_react9 = __toESM(require("react"));
3561
- import_Box9 = __toESM(require("@mui/material/Box"));
3607
+ import_react10 = __toESM(require("react"));
3608
+ import_Box10 = __toESM(require("@mui/material/Box"));
3562
3609
  import_Table5 = __toESM(require("@mui/material/Table"));
3563
3610
  import_TableBody5 = __toESM(require("@mui/material/TableBody"));
3564
3611
  import_TableCell5 = __toESM(require("@mui/material/TableCell"));
@@ -3566,7 +3613,7 @@ var init_ModelsTable = __esm({
3566
3613
  import_TableHead5 = __toESM(require("@mui/material/TableHead"));
3567
3614
  import_TableRow5 = __toESM(require("@mui/material/TableRow"));
3568
3615
  import_TableSortLabel2 = __toESM(require("@mui/material/TableSortLabel"));
3569
- import_Typography9 = __toESM(require("@mui/material/Typography"));
3616
+ import_Typography10 = __toESM(require("@mui/material/Typography"));
3570
3617
  import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
3571
3618
  import_Select = __toESM(require("@mui/material/Select"));
3572
3619
  import_FormControl = __toESM(require("@mui/material/FormControl"));
@@ -3579,24 +3626,24 @@ var init_ModelsTable = __esm({
3579
3626
  import_Snackbar = __toESM(require("@mui/material/Snackbar"));
3580
3627
  import_Alert4 = __toESM(require("@mui/material/Alert"));
3581
3628
  init_ui();
3582
- import_styles6 = require("@mui/material/styles");
3629
+ import_styles7 = require("@mui/material/styles");
3583
3630
  ALL_PROXY_MODELS2 = "all-proxy-models";
3584
3631
  ModelsTable = ({ allModels, teams, loading }) => {
3585
- const theme = (0, import_styles6.useTheme)();
3586
- const [selectedTeamId, setSelectedTeamId] = (0, import_react9.useState)("");
3587
- const [filterText, setFilterText] = (0, import_react9.useState)("");
3588
- const [copiedSnackbar, setCopiedSnackbar] = (0, import_react9.useState)(false);
3589
- const [sortKey, setSortKey] = (0, import_react9.useState)("model_name");
3590
- const [sortDirection, setSortDirection] = (0, import_react9.useState)("asc");
3591
- const selectedTeam = (0, import_react9.useMemo)(
3632
+ const theme = (0, import_styles7.useTheme)();
3633
+ const [selectedTeamId, setSelectedTeamId] = (0, import_react10.useState)("");
3634
+ const [filterText, setFilterText] = (0, import_react10.useState)("");
3635
+ const [copiedSnackbar, setCopiedSnackbar] = (0, import_react10.useState)(false);
3636
+ const [sortKey, setSortKey] = (0, import_react10.useState)("model_name");
3637
+ const [sortDirection, setSortDirection] = (0, import_react10.useState)("asc");
3638
+ const selectedTeam = (0, import_react10.useMemo)(
3592
3639
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
3593
3640
  [teams, selectedTeamId]
3594
3641
  );
3595
- const teamModels = (0, import_react9.useMemo)(() => {
3642
+ const teamModels = (0, import_react10.useMemo)(() => {
3596
3643
  if (!selectedTeam) return [];
3597
3644
  return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
3598
3645
  }, [allModels, selectedTeam]);
3599
- const filteredModels = (0, import_react9.useMemo)(() => {
3646
+ const filteredModels = (0, import_react10.useMemo)(() => {
3600
3647
  const q = filterText.trim().toLowerCase();
3601
3648
  let models = teamModels;
3602
3649
  if (q) {
@@ -3643,10 +3690,10 @@ var init_ModelsTable = __esm({
3643
3690
  return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
3644
3691
  });
3645
3692
  }, [teamModels, filterText, sortKey, sortDirection]);
3646
- const handleCopyModelId = (0, import_react9.useCallback)((modelId) => {
3693
+ const handleCopyModelId = (0, import_react10.useCallback)((modelId) => {
3647
3694
  navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
3648
3695
  }, []);
3649
- const handleSort = (0, import_react9.useCallback)((key) => {
3696
+ const handleSort = (0, import_react10.useCallback)((key) => {
3650
3697
  if (sortKey === key) {
3651
3698
  setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
3652
3699
  } else {
@@ -3668,13 +3715,13 @@ var init_ModelsTable = __esm({
3668
3715
  return theme.palette.info.main;
3669
3716
  }
3670
3717
  };
3671
- return /* @__PURE__ */ import_react9.default.createElement(
3718
+ return /* @__PURE__ */ import_react10.default.createElement(
3672
3719
  SectionCard,
3673
3720
  {
3674
3721
  title: "Available Models",
3675
3722
  subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
3676
3723
  },
3677
- /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ import_react9.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react9.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react9.default.createElement(
3724
+ /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ import_react10.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react10.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react10.default.createElement(
3678
3725
  import_Select.default,
3679
3726
  {
3680
3727
  labelId: "team-select-label",
@@ -3682,8 +3729,8 @@ var init_ModelsTable = __esm({
3682
3729
  label: "Team",
3683
3730
  onChange: (e) => setSelectedTeamId(e.target.value)
3684
3731
  },
3685
- teams.map((t) => /* @__PURE__ */ import_react9.default.createElement(import_MenuItem4.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3686
- )), /* @__PURE__ */ import_react9.default.createElement(
3732
+ teams.map((t) => /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3733
+ )), /* @__PURE__ */ import_react10.default.createElement(
3687
3734
  import_TextField5.default,
3688
3735
  {
3689
3736
  label: "Filter Models",
@@ -3693,33 +3740,33 @@ var init_ModelsTable = __esm({
3693
3740
  disabled: !selectedTeamId,
3694
3741
  sx: { minWidth: 240, mb: 2 },
3695
3742
  InputProps: {
3696
- startAdornment: /* @__PURE__ */ import_react9.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react9.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
3743
+ startAdornment: /* @__PURE__ */ import_react10.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react10.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
3697
3744
  }
3698
3745
  }
3699
3746
  )),
3700
- loading && /* @__PURE__ */ import_react9.default.createElement(EmptyState, { message: "Loading models\u2026" }),
3701
- !loading && !selectedTeamId && /* @__PURE__ */ import_react9.default.createElement(
3747
+ loading && /* @__PURE__ */ import_react10.default.createElement(EmptyState, { message: "Loading models\u2026" }),
3748
+ !loading && !selectedTeamId && /* @__PURE__ */ import_react10.default.createElement(
3702
3749
  EmptyState,
3703
3750
  {
3704
3751
  message: "Select a team to see available models",
3705
3752
  hint: "Each team may have a different set of models assigned"
3706
3753
  }
3707
3754
  ),
3708
- !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react9.default.createElement(
3755
+ !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
3709
3756
  EmptyState,
3710
3757
  {
3711
3758
  message: "No models available for this team",
3712
3759
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
3713
3760
  }
3714
3761
  ),
3715
- !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react9.default.createElement(
3762
+ !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
3716
3763
  EmptyState,
3717
3764
  {
3718
3765
  message: "No models match your search",
3719
3766
  hint: "Try a different search term"
3720
3767
  }
3721
3768
  ),
3722
- !loading && filteredModels.length > 0 && /* @__PURE__ */ import_react9.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react9.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react9.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react9.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react9.default.createElement(
3769
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ import_react10.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react10.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react10.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react10.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react10.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react10.default.createElement(
3723
3770
  "span",
3724
3771
  {
3725
3772
  role: "button",
@@ -3729,9 +3776,9 @@ var init_ModelsTable = __esm({
3729
3776
  onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
3730
3777
  style: { cursor: "pointer", display: "inline-flex" }
3731
3778
  },
3732
- /* @__PURE__ */ import_react9.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3733
- )), /* @__PURE__ */ import_react9.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react9.default.createElement(
3734
- import_Box9.default,
3779
+ /* @__PURE__ */ import_react10.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3780
+ )), /* @__PURE__ */ import_react10.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react10.default.createElement(
3781
+ import_Box10.default,
3735
3782
  {
3736
3783
  component: "span",
3737
3784
  role: "button",
@@ -3741,9 +3788,9 @@ var init_ModelsTable = __esm({
3741
3788
  sx: quietIconButtonSx("accent"),
3742
3789
  style: { cursor: "pointer", display: "inline-flex" }
3743
3790
  },
3744
- /* @__PURE__ */ import_react9.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
3745
- )))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(
3746
- import_Box9.default,
3791
+ /* @__PURE__ */ import_react10.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
3792
+ )))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(
3793
+ import_Box10.default,
3747
3794
  {
3748
3795
  component: "span",
3749
3796
  sx: {
@@ -3754,13 +3801,13 @@ var init_ModelsTable = __esm({
3754
3801
  fontSize: 11,
3755
3802
  fontWeight: 600,
3756
3803
  textTransform: "capitalize",
3757
- bgcolor: (0, import_styles6.alpha)(modeColor(m.mode), 0.1),
3804
+ bgcolor: (0, import_styles7.alpha)(modeColor(m.mode), 0.1),
3758
3805
  color: modeColor(m.mode)
3759
3806
  }
3760
3807
  },
3761
3808
  m.mode
3762
- )), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react9.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react9.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3763
- /* @__PURE__ */ import_react9.default.createElement(
3809
+ )), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react10.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react10.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3810
+ /* @__PURE__ */ import_react10.default.createElement(
3764
3811
  import_Snackbar.default,
3765
3812
  {
3766
3813
  open: copiedSnackbar,
@@ -3768,7 +3815,7 @@ var init_ModelsTable = __esm({
3768
3815
  onClose: () => setCopiedSnackbar(false),
3769
3816
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
3770
3817
  },
3771
- /* @__PURE__ */ import_react9.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3818
+ /* @__PURE__ */ import_react10.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3772
3819
  )
3773
3820
  );
3774
3821
  };
@@ -3796,10 +3843,10 @@ function formatDateTime(iso) {
3796
3843
  }
3797
3844
  function renderAuditLogBody(loading, entries) {
3798
3845
  if (loading) {
3799
- return /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react10.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
3846
+ return /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react11.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
3800
3847
  }
3801
3848
  if (entries.length === 0) {
3802
- return /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react10.default.createElement(
3849
+ return /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react11.default.createElement(
3803
3850
  EmptyState,
3804
3851
  {
3805
3852
  message: "No audit events found",
@@ -3807,15 +3854,15 @@ function renderAuditLogBody(loading, entries) {
3807
3854
  }
3808
3855
  )));
3809
3856
  }
3810
- return entries.map((entry) => /* @__PURE__ */ import_react10.default.createElement(DetailRow, { key: entry.id, entry }));
3857
+ return entries.map((entry) => /* @__PURE__ */ import_react11.default.createElement(DetailRow, { key: entry.id, entry }));
3811
3858
  }
3812
- var import_react10, import_Box10, import_Typography10, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_TablePagination, import_TextField6, import_MenuItem5, import_Skeleton5, import_Collapse3, import_IconButton6, import_Alert5, import_styles7, import_icons_material8, import_react_use2, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
3859
+ var import_react11, import_Box11, import_Typography11, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_TablePagination, import_TextField6, import_MenuItem5, import_Skeleton5, import_Collapse3, import_IconButton6, import_Alert5, import_styles8, import_icons_material8, import_react_use2, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
3813
3860
  var init_AuditLog = __esm({
3814
3861
  "src/components/AuditLog.tsx"() {
3815
3862
  "use strict";
3816
- import_react10 = __toESM(require("react"));
3817
- import_Box10 = __toESM(require("@mui/material/Box"));
3818
- import_Typography10 = __toESM(require("@mui/material/Typography"));
3863
+ import_react11 = __toESM(require("react"));
3864
+ import_Box11 = __toESM(require("@mui/material/Box"));
3865
+ import_Typography11 = __toESM(require("@mui/material/Typography"));
3819
3866
  import_Table6 = __toESM(require("@mui/material/Table"));
3820
3867
  import_TableBody6 = __toESM(require("@mui/material/TableBody"));
3821
3868
  import_TableCell6 = __toESM(require("@mui/material/TableCell"));
@@ -3829,7 +3876,7 @@ var init_AuditLog = __esm({
3829
3876
  import_Collapse3 = __toESM(require("@mui/material/Collapse"));
3830
3877
  import_IconButton6 = __toESM(require("@mui/material/IconButton"));
3831
3878
  import_Alert5 = __toESM(require("@mui/material/Alert"));
3832
- import_styles7 = require("@mui/material/styles");
3879
+ import_styles8 = require("@mui/material/styles");
3833
3880
  import_icons_material8 = require("@mui/icons-material");
3834
3881
  import_react_use2 = require("react-use");
3835
3882
  init_ui();
@@ -3848,9 +3895,9 @@ var init_AuditLog = __esm({
3848
3895
  LiteLLM_UserTable: "User"
3849
3896
  };
3850
3897
  DetailRow = ({ entry }) => {
3851
- const [open, setOpen] = (0, import_react10.useState)(false);
3898
+ const [open, setOpen] = (0, import_react11.useState)(false);
3852
3899
  const hasDetail = entry.before_value || entry.updated_values;
3853
- return /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react10.default.createElement(
3900
+ return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react11.default.createElement(
3854
3901
  import_IconButton6.default,
3855
3902
  {
3856
3903
  size: "small",
@@ -3863,9 +3910,9 @@ var init_AuditLog = __esm({
3863
3910
  transition: theme.transitions.create("transform")
3864
3911
  })
3865
3912
  },
3866
- /* @__PURE__ */ import_react10.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
3867
- )), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, entry.action && /* @__PURE__ */ import_react10.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react10.default.createElement(
3868
- import_Typography10.default,
3913
+ /* @__PURE__ */ import_react11.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
3914
+ )), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, entry.action && /* @__PURE__ */ import_react11.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react11.default.createElement(
3915
+ import_Typography11.default,
3869
3916
  {
3870
3917
  variant: "body2",
3871
3918
  component: "code",
@@ -3874,8 +3921,8 @@ var init_AuditLog = __esm({
3874
3921
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3875
3922
  },
3876
3923
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
3877
- )), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react10.default.createElement(import_Collapse3.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react10.default.createElement(
3878
- import_Box10.default,
3924
+ )), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react11.default.createElement(import_Collapse3.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react11.default.createElement(
3925
+ import_Box11.default,
3879
3926
  {
3880
3927
  display: "flex",
3881
3928
  gap: 2,
@@ -3884,11 +3931,11 @@ var init_AuditLog = __esm({
3884
3931
  p: 2,
3885
3932
  mb: 1.5,
3886
3933
  borderRadius: 1.5,
3887
- bgcolor: (0, import_styles7.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3934
+ bgcolor: (0, import_styles8.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3888
3935
  })
3889
3936
  },
3890
- entry.before_value && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react10.default.createElement(
3891
- import_Typography10.default,
3937
+ entry.before_value && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react11.default.createElement(
3938
+ import_Typography11.default,
3892
3939
  {
3893
3940
  variant: "caption",
3894
3941
  color: "text.secondary",
@@ -3897,9 +3944,9 @@ var init_AuditLog = __esm({
3897
3944
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3898
3945
  },
3899
3946
  "Before"
3900
- ), /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { 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))),
3901
- entry.updated_values && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react10.default.createElement(
3902
- import_Typography10.default,
3947
+ ), /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { 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))),
3948
+ entry.updated_values && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react11.default.createElement(
3949
+ import_Typography11.default,
3903
3950
  {
3904
3951
  variant: "caption",
3905
3952
  color: "text.secondary",
@@ -3908,14 +3955,14 @@ var init_AuditLog = __esm({
3908
3955
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3909
3956
  },
3910
3957
  "After"
3911
- ), /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { 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)))
3958
+ ), /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { 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)))
3912
3959
  )))));
3913
3960
  };
3914
3961
  AuditLog = ({ api }) => {
3915
- const [page, setPage] = (0, import_react10.useState)(0);
3916
- const [pageSize, setPageSize] = (0, import_react10.useState)(25);
3917
- const [filters, setFilters] = (0, import_react10.useState)({});
3918
- const fetchParams = (0, import_react10.useMemo)(
3962
+ const [page, setPage] = (0, import_react11.useState)(0);
3963
+ const [pageSize, setPageSize] = (0, import_react11.useState)(25);
3964
+ const [filters, setFilters] = (0, import_react11.useState)({});
3965
+ const fetchParams = (0, import_react11.useMemo)(
3919
3966
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3920
3967
  [page, pageSize, filters]
3921
3968
  );
@@ -3925,13 +3972,13 @@ var init_AuditLog = __esm({
3925
3972
  );
3926
3973
  const entries = value?.audit_logs ?? [];
3927
3974
  const total = value?.total ?? 0;
3928
- return /* @__PURE__ */ import_react10.default.createElement(
3975
+ return /* @__PURE__ */ import_react11.default.createElement(
3929
3976
  SectionCard,
3930
3977
  {
3931
3978
  title: "Audit Log",
3932
3979
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3933
3980
  flush: true,
3934
- actions: /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(
3981
+ actions: /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(
3935
3982
  import_TextField6.default,
3936
3983
  {
3937
3984
  size: "small",
@@ -3944,12 +3991,12 @@ var init_AuditLog = __esm({
3944
3991
  },
3945
3992
  sx: { minWidth: 150 }
3946
3993
  },
3947
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
3948
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
3949
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
3950
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
3951
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
3952
- ), /* @__PURE__ */ import_react10.default.createElement(
3994
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
3995
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
3996
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
3997
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
3998
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
3999
+ ), /* @__PURE__ */ import_react11.default.createElement(
3953
4000
  import_TextField6.default,
3954
4001
  {
3955
4002
  size: "small",
@@ -3962,13 +4009,13 @@ var init_AuditLog = __esm({
3962
4009
  },
3963
4010
  sx: { minWidth: 150 }
3964
4011
  },
3965
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
3966
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
3967
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
3968
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
3969
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3970
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
3971
- ), /* @__PURE__ */ import_react10.default.createElement(
4012
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
4013
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
4014
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
4015
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
4016
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
4017
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
4018
+ ), /* @__PURE__ */ import_react11.default.createElement(
3972
4019
  import_TextField6.default,
3973
4020
  {
3974
4021
  size: "small",
@@ -3982,9 +4029,9 @@ var init_AuditLog = __esm({
3982
4029
  }
3983
4030
  ))
3984
4031
  },
3985
- error && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react10.default.createElement(import_Alert5.default, { severity: "error" }, error.message)),
3986
- /* @__PURE__ */ import_react10.default.createElement(import_TableContainer6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Table6.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react10.default.createElement(import_TableHead6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Time"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Action"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Table"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Object ID"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Changed By"))), /* @__PURE__ */ import_react10.default.createElement(import_TableBody6.default, null, renderAuditLogBody(loading, entries)))),
3987
- /* @__PURE__ */ import_react10.default.createElement(
4032
+ error && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react11.default.createElement(import_Alert5.default, { severity: "error" }, error.message)),
4033
+ /* @__PURE__ */ import_react11.default.createElement(import_TableContainer6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Table6.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react11.default.createElement(import_TableHead6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Time"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Action"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Table"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Object ID"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Changed By"))), /* @__PURE__ */ import_react11.default.createElement(import_TableBody6.default, null, renderAuditLogBody(loading, entries)))),
4034
+ /* @__PURE__ */ import_react11.default.createElement(
3988
4035
  import_TablePagination.default,
3989
4036
  {
3990
4037
  component: "div",
@@ -4053,17 +4100,17 @@ function initDateRange() {
4053
4100
  else start.setDate(start.getDate() - 7);
4054
4101
  return { start, end };
4055
4102
  }
4056
- var import_react11, import_Box11, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography11, import_Paper7, import_Tabs2, import_Tab2, import_Grid2, import_react_router_dom, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
4103
+ var import_react12, import_Box12, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography12, import_Paper8, import_Tabs2, import_Tab2, import_Grid2, import_react_router_dom, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
4057
4104
  var init_LiteLLMPage = __esm({
4058
4105
  "src/components/LiteLLMPage.tsx"() {
4059
4106
  "use strict";
4060
- import_react11 = __toESM(require("react"));
4061
- import_Box11 = __toESM(require("@mui/material/Box"));
4107
+ import_react12 = __toESM(require("react"));
4108
+ import_Box12 = __toESM(require("@mui/material/Box"));
4062
4109
  import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
4063
4110
  import_Alert6 = __toESM(require("@mui/material/Alert"));
4064
4111
  import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
4065
- import_Typography11 = __toESM(require("@mui/material/Typography"));
4066
- import_Paper7 = __toESM(require("@mui/material/Paper"));
4112
+ import_Typography12 = __toESM(require("@mui/material/Typography"));
4113
+ import_Paper8 = __toESM(require("@mui/material/Paper"));
4067
4114
  import_Tabs2 = __toESM(require("@mui/material/Tabs"));
4068
4115
  import_Tab2 = __toESM(require("@mui/material/Tab"));
4069
4116
  import_Grid2 = __toESM(require("@mui/material/Grid"));
@@ -4087,8 +4134,8 @@ var init_LiteLLMPage = __esm({
4087
4134
  isPageTab = (v) => !!v && PAGE_TABS.includes(v);
4088
4135
  LiteLLMPage = () => {
4089
4136
  const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
4090
- const [dateRange, setDateRange] = (0, import_react11.useState)(initDateRange);
4091
- const [currentPreset, setCurrentPreset] = (0, import_react11.useState)(() => {
4137
+ const [dateRange, setDateRange] = (0, import_react12.useState)(initDateRange);
4138
+ const [currentPreset, setCurrentPreset] = (0, import_react12.useState)(() => {
4092
4139
  try {
4093
4140
  return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
4094
4141
  } catch {
@@ -4096,11 +4143,26 @@ var init_LiteLLMPage = __esm({
4096
4143
  }
4097
4144
  });
4098
4145
  const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
4099
- const [activeTab, setActiveTab] = (0, import_react11.useState)(() => {
4146
+ const [activeTab, setActiveTab] = (0, import_react12.useState)(() => {
4100
4147
  const t = searchParams.get("tab");
4101
4148
  return isPageTab(t) ? t : "overview";
4102
4149
  });
4103
- const selectTab = (0, import_react11.useCallback)(
4150
+ const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react12.useState)(
4151
+ () => searchParams.get("generate") === "1"
4152
+ );
4153
+ (0, import_react12.useEffect)(() => {
4154
+ if (searchParams.get("generate") !== "1") return;
4155
+ setGenerateDialogOpen(true);
4156
+ setSearchParams(
4157
+ (prev) => {
4158
+ const next = new URLSearchParams(prev);
4159
+ next.delete("generate");
4160
+ return next;
4161
+ },
4162
+ { replace: true }
4163
+ );
4164
+ }, [searchParams, setSearchParams]);
4165
+ const selectTab = (0, import_react12.useCallback)(
4104
4166
  (tab) => {
4105
4167
  setActiveTab(tab);
4106
4168
  setSearchParams(
@@ -4114,11 +4176,10 @@ var init_LiteLLMPage = __esm({
4114
4176
  },
4115
4177
  [setSearchParams]
4116
4178
  );
4117
- const [snackbar, setSnackbar] = (0, import_react11.useState)(null);
4118
- const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react11.useState)(false);
4119
- const [manageTeam, setManageTeam] = (0, import_react11.useState)(null);
4120
- const [teamUsageCache, setTeamUsageCache] = (0, import_react11.useState)({});
4121
- const [teamUsageLoading, setTeamUsageLoading] = (0, import_react11.useState)({});
4179
+ const [snackbar, setSnackbar] = (0, import_react12.useState)(null);
4180
+ const [manageTeam, setManageTeam] = (0, import_react12.useState)(null);
4181
+ const [teamUsageCache, setTeamUsageCache] = (0, import_react12.useState)({});
4182
+ const [teamUsageLoading, setTeamUsageLoading] = (0, import_react12.useState)({});
4122
4183
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
4123
4184
  () => api.getUserInfo(),
4124
4185
  [api]
@@ -4162,7 +4223,7 @@ var init_LiteLLMPage = __esm({
4162
4223
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
4163
4224
  [api, objectPermsEnabled]
4164
4225
  );
4165
- const teams = (0, import_react11.useMemo)(() => {
4226
+ const teams = (0, import_react12.useMemo)(() => {
4166
4227
  if (!allTeams?.length) return [];
4167
4228
  if (!userInfo) return allTeams;
4168
4229
  const userId = userInfo.user_id;
@@ -4179,7 +4240,7 @@ var init_LiteLLMPage = __esm({
4179
4240
  const endDate = dateRange.end.toISOString().split("T")[0];
4180
4241
  return api.getUsage(startDate, endDate);
4181
4242
  }, [api, dateRange]);
4182
- const handleDateRangeChange = (0, import_react11.useCallback)((range, preset) => {
4243
+ const handleDateRangeChange = (0, import_react12.useCallback)((range, preset) => {
4183
4244
  setDateRange(range);
4184
4245
  if (preset) {
4185
4246
  setCurrentPreset(preset);
@@ -4187,7 +4248,7 @@ var init_LiteLLMPage = __esm({
4187
4248
  setTeamUsageCache({});
4188
4249
  setTeamUsageLoading({});
4189
4250
  }, [setDateRange, setCurrentPreset]);
4190
- const loadTeamUsage = (0, import_react11.useCallback)(async (teamId) => {
4251
+ const loadTeamUsage = (0, import_react12.useCallback)(async (teamId) => {
4191
4252
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
4192
4253
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
4193
4254
  try {
@@ -4201,7 +4262,7 @@ var init_LiteLLMPage = __esm({
4201
4262
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
4202
4263
  }
4203
4264
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
4204
- const allowedModels = (0, import_react11.useMemo)(() => {
4265
+ const allowedModels = (0, import_react12.useMemo)(() => {
4205
4266
  if (!allModels?.length) return [];
4206
4267
  const userModels = userInfo?.models;
4207
4268
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -4214,7 +4275,7 @@ var init_LiteLLMPage = __esm({
4214
4275
  ]);
4215
4276
  return allModels.filter((m) => allowed.has(m.model_name));
4216
4277
  }, [allModels, userInfo, teams]);
4217
- const handleGenerateKey = (0, import_react11.useCallback)(
4278
+ const handleGenerateKey = (0, import_react12.useCallback)(
4218
4279
  async (request) => {
4219
4280
  try {
4220
4281
  const response = await api.generateKey(request);
@@ -4228,7 +4289,7 @@ var init_LiteLLMPage = __esm({
4228
4289
  },
4229
4290
  [api, refreshKeys]
4230
4291
  );
4231
- const handleUpdateKey = (0, import_react11.useCallback)(
4292
+ const handleUpdateKey = (0, import_react12.useCallback)(
4232
4293
  async (keyId, request) => {
4233
4294
  try {
4234
4295
  await api.updateKey(keyId, request);
@@ -4241,7 +4302,7 @@ var init_LiteLLMPage = __esm({
4241
4302
  },
4242
4303
  [api, refreshKeys]
4243
4304
  );
4244
- const handleBlockKey = (0, import_react11.useCallback)(
4305
+ const handleBlockKey = (0, import_react12.useCallback)(
4245
4306
  async (keyId) => {
4246
4307
  try {
4247
4308
  await api.blockKey(keyId);
@@ -4253,7 +4314,7 @@ var init_LiteLLMPage = __esm({
4253
4314
  },
4254
4315
  [api, refreshKeys]
4255
4316
  );
4256
- const handleUnblockKey = (0, import_react11.useCallback)(
4317
+ const handleUnblockKey = (0, import_react12.useCallback)(
4257
4318
  async (keyId) => {
4258
4319
  try {
4259
4320
  await api.unblockKey(keyId);
@@ -4265,7 +4326,7 @@ var init_LiteLLMPage = __esm({
4265
4326
  },
4266
4327
  [api, refreshKeys]
4267
4328
  );
4268
- const handleResetKeySpend = (0, import_react11.useCallback)(
4329
+ const handleResetKeySpend = (0, import_react12.useCallback)(
4269
4330
  async (keyId) => {
4270
4331
  try {
4271
4332
  await api.resetKeySpend(keyId);
@@ -4277,7 +4338,7 @@ var init_LiteLLMPage = __esm({
4277
4338
  },
4278
4339
  [api, refreshKeys]
4279
4340
  );
4280
- const handleDeleteKey = (0, import_react11.useCallback)(
4341
+ const handleDeleteKey = (0, import_react12.useCallback)(
4281
4342
  async (keyId) => {
4282
4343
  try {
4283
4344
  await api.deleteKey(keyId);
@@ -4294,7 +4355,7 @@ var init_LiteLLMPage = __esm({
4294
4355
  },
4295
4356
  [api, refreshKeys]
4296
4357
  );
4297
- const handlePruneExpiredKeys = (0, import_react11.useCallback)(
4358
+ const handlePruneExpiredKeys = (0, import_react12.useCallback)(
4298
4359
  async () => {
4299
4360
  try {
4300
4361
  const result = await api.pruneExpiredKeys();
@@ -4309,14 +4370,14 @@ var init_LiteLLMPage = __esm({
4309
4370
  );
4310
4371
  const isInitialLoading = userLoading && !userInfo;
4311
4372
  if (isInitialLoading) {
4312
- return /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react11.default.createElement(import_CircularProgress6.default, null));
4373
+ return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react12.default.createElement(import_CircularProgress6.default, null));
4313
4374
  }
4314
4375
  if (userError || !userInfo) {
4315
4376
  const isProvisioningEnabled = userError?.body?.provisioning === true;
4316
4377
  const hint = userError?.body?.hint;
4317
- return /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { p: 3 }, /* @__PURE__ */ import_react11.default.createElement(import_Paper7.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { 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.")));
4378
+ return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { p: 3 }, /* @__PURE__ */ import_react12.default.createElement(import_Paper8.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { 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.")));
4318
4379
  }
4319
- const pageTabs = /* @__PURE__ */ import_react11.default.createElement(
4380
+ const pageTabs = /* @__PURE__ */ import_react12.default.createElement(
4320
4381
  import_Tabs2.default,
4321
4382
  {
4322
4383
  value: activeTab,
@@ -4329,13 +4390,13 @@ var init_LiteLLMPage = __esm({
4329
4390
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
4330
4391
  }
4331
4392
  },
4332
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
4333
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
4334
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
4335
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
4336
- userInfo.can_view_audit && /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
4393
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
4394
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
4395
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
4396
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
4397
+ userInfo.can_view_audit && /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
4337
4398
  );
4338
- return /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react11.default.createElement(
4399
+ return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react12.default.createElement(
4339
4400
  DashboardHeader,
4340
4401
  {
4341
4402
  userInfo,
@@ -4345,7 +4406,7 @@ var init_LiteLLMPage = __esm({
4345
4406
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
4346
4407
  tabs: pageTabs
4347
4408
  }
4348
- ), activeTab === "overview" && /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ import_react11.default.createElement(
4409
+ ), activeTab === "overview" && /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ import_react12.default.createElement(
4349
4410
  UsageStats,
4350
4411
  {
4351
4412
  usage: usage ?? null,
@@ -4356,7 +4417,7 @@ var init_LiteLLMPage = __esm({
4356
4417
  loading: usageLoading,
4357
4418
  userInfo
4358
4419
  }
4359
- )), /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ import_react11.default.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ import_react11.default.createElement(
4420
+ )), /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ import_react12.default.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ import_react12.default.createElement(
4360
4421
  KeysTable,
4361
4422
  {
4362
4423
  keys: keys ?? [],
@@ -4375,7 +4436,7 @@ var init_LiteLLMPage = __esm({
4375
4436
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
4376
4437
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
4377
4438
  const visibleTeams = Array.from(teamsById.values());
4378
- return /* @__PURE__ */ import_react11.default.createElement(
4439
+ return /* @__PURE__ */ import_react12.default.createElement(
4379
4440
  TeamUsage,
4380
4441
  {
4381
4442
  teams: visibleTeams,
@@ -4391,14 +4452,14 @@ var init_LiteLLMPage = __esm({
4391
4452
  onCreateTeam: () => setManageTeam({ mode: "create" })
4392
4453
  }
4393
4454
  );
4394
- })(), activeTab === "models" && /* @__PURE__ */ import_react11.default.createElement(
4455
+ })(), activeTab === "models" && /* @__PURE__ */ import_react12.default.createElement(
4395
4456
  ModelsTable,
4396
4457
  {
4397
4458
  allModels: allModels ?? [],
4398
4459
  teams: teams ?? [],
4399
4460
  loading: modelsLoading
4400
4461
  }
4401
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react11.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react11.default.createElement(
4462
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react12.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react12.default.createElement(
4402
4463
  GenerateKeyDialog,
4403
4464
  {
4404
4465
  open: generateDialogOpen,
@@ -4411,7 +4472,7 @@ var init_LiteLLMPage = __esm({
4411
4472
  onGenerateKey: handleGenerateKey,
4412
4473
  onGetConfig: () => api.getConfig()
4413
4474
  }
4414
- ), /* @__PURE__ */ import_react11.default.createElement(
4475
+ ), /* @__PURE__ */ import_react12.default.createElement(
4415
4476
  ManageTeamDialog,
4416
4477
  {
4417
4478
  open: !!manageTeam,
@@ -4473,7 +4534,7 @@ var init_LiteLLMPage = __esm({
4473
4534
  refreshTeams();
4474
4535
  }
4475
4536
  }
4476
- ), /* @__PURE__ */ import_react11.default.createElement(
4537
+ ), /* @__PURE__ */ import_react12.default.createElement(
4477
4538
  import_Snackbar2.default,
4478
4539
  {
4479
4540
  open: !!snackbar,
@@ -4481,7 +4542,7 @@ var init_LiteLLMPage = __esm({
4481
4542
  onClose: () => setSnackbar(null),
4482
4543
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
4483
4544
  },
4484
- snackbar ? /* @__PURE__ */ import_react11.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4545
+ snackbar ? /* @__PURE__ */ import_react12.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4485
4546
  ));
4486
4547
  };
4487
4548
  }
@@ -4512,7 +4573,7 @@ __export(index_exports, {
4512
4573
  module.exports = __toCommonJS(index_exports);
4513
4574
 
4514
4575
  // src/plugin.tsx
4515
- var import_react12 = __toESM(require("react"));
4576
+ var import_react13 = __toESM(require("react"));
4516
4577
  var import_icons_material9 = require("@mui/icons-material");
4517
4578
  var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
4518
4579
  init_api();
@@ -4527,10 +4588,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
4527
4588
  params: {
4528
4589
  path: "/litellm",
4529
4590
  title: "LiteLLM",
4530
- icon: /* @__PURE__ */ import_react12.default.createElement(import_icons_material9.TrendingUp, null),
4591
+ icon: /* @__PURE__ */ import_react13.default.createElement(import_icons_material9.TrendingUp, null),
4531
4592
  loader: async () => {
4532
4593
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
4533
- return /* @__PURE__ */ import_react12.default.createElement(LiteLLMPage2, null);
4594
+ return /* @__PURE__ */ import_react13.default.createElement(LiteLLMPage2, null);
4534
4595
  }
4535
4596
  }
4536
4597
  });
@@ -4547,10 +4608,10 @@ init_UsageStats();
4547
4608
  init_TeamUsage();
4548
4609
 
4549
4610
  // src/components/LiteLLMHomeWidget.tsx
4550
- var import_react13 = __toESM(require("react"));
4551
- var import_Paper8 = __toESM(require("@mui/material/Paper"));
4552
- var import_Box12 = __toESM(require("@mui/material/Box"));
4553
- var import_Typography12 = __toESM(require("@mui/material/Typography"));
4611
+ var import_react14 = __toESM(require("react"));
4612
+ var import_Paper9 = __toESM(require("@mui/material/Paper"));
4613
+ var import_Box13 = __toESM(require("@mui/material/Box"));
4614
+ var import_Typography13 = __toESM(require("@mui/material/Typography"));
4554
4615
  var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
4555
4616
  var import_Select2 = __toESM(require("@mui/material/Select"));
4556
4617
  var import_MenuItem6 = __toESM(require("@mui/material/MenuItem"));
@@ -4573,19 +4634,19 @@ function presetToDateRange(preset) {
4573
4634
  }
4574
4635
  return { start, end };
4575
4636
  }
4576
- var Kpi = ({ label, value }) => /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, null, /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "subtitle1", fontWeight: 600 }, value));
4637
+ var Kpi = ({ label, value }) => /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, null, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "subtitle1", fontWeight: 600 }, value));
4577
4638
  var LiteLLMHomeWidget = ({
4578
4639
  defaultPeriod = "7d",
4579
4640
  title = "LiteLLM Usage"
4580
4641
  }) => {
4581
4642
  const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
4582
- const [period, setPeriod] = (0, import_react13.useState)(defaultPeriod);
4583
- const [loading, setLoading] = (0, import_react13.useState)(true);
4584
- const [usageError, setUsageError] = (0, import_react13.useState)(null);
4585
- const [keysError, setKeysError] = (0, import_react13.useState)(null);
4586
- const [usage, setUsage] = (0, import_react13.useState)(null);
4587
- const [keys, setKeys] = (0, import_react13.useState)([]);
4588
- (0, import_react13.useEffect)(() => {
4643
+ const [period, setPeriod] = (0, import_react14.useState)(defaultPeriod);
4644
+ const [loading, setLoading] = (0, import_react14.useState)(true);
4645
+ const [usageError, setUsageError] = (0, import_react14.useState)(null);
4646
+ const [keysError, setKeysError] = (0, import_react14.useState)(null);
4647
+ const [usage, setUsage] = (0, import_react14.useState)(null);
4648
+ const [keys, setKeys] = (0, import_react14.useState)([]);
4649
+ (0, import_react14.useEffect)(() => {
4589
4650
  let cancelled = false;
4590
4651
  setLoading(true);
4591
4652
  setUsageError(null);
@@ -4622,17 +4683,17 @@ var LiteLLMHomeWidget = ({
4622
4683
  spend: d.spend
4623
4684
  }));
4624
4685
  const hasSparkline = dailyData.length > 0;
4625
- return /* @__PURE__ */ import_react13.default.createElement(import_Paper8.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "h6" }, title), /* @__PURE__ */ import_react13.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react13.default.createElement(
4686
+ return /* @__PURE__ */ import_react14.default.createElement(import_Paper9.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "h6" }, title), /* @__PURE__ */ import_react14.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react14.default.createElement(
4626
4687
  import_Select2.default,
4627
4688
  {
4628
4689
  value: period,
4629
4690
  onChange: (e) => setPeriod(e.target.value),
4630
4691
  displayEmpty: true
4631
4692
  },
4632
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
4633
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
4634
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
4635
- ))), loading && /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react13.default.createElement(import_CircularProgress7.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react13.default.createElement(import_Alert7.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react13.default.createElement(import_Alert7.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { height: 120 }, /* @__PURE__ */ import_react13.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react13.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react13.default.createElement(
4693
+ /* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
4694
+ /* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
4695
+ /* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
4696
+ ))), loading && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react14.default.createElement(import_CircularProgress7.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react14.default.createElement(import_Alert7.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react14.default.createElement(import_Alert7.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { height: 120 }, /* @__PURE__ */ import_react14.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react14.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react14.default.createElement(
4636
4697
  import_recharts3.Area,
4637
4698
  {
4638
4699
  type: "monotone",
@@ -4650,24 +4711,34 @@ var LiteLLMHomeWidget = ({
4650
4711
  init_LiteLLMBudgetWidget();
4651
4712
 
4652
4713
  // src/components/LiteLLMBudgetGauges.tsx
4653
- var import_react14 = __toESM(require("react"));
4654
- var import_Paper9 = __toESM(require("@mui/material/Paper"));
4655
- var import_Box13 = __toESM(require("@mui/material/Box"));
4656
- var import_Typography13 = __toESM(require("@mui/material/Typography"));
4714
+ var import_react15 = __toESM(require("react"));
4715
+ var import_Paper10 = __toESM(require("@mui/material/Paper"));
4716
+ var import_Box14 = __toESM(require("@mui/material/Box"));
4717
+ var import_Button6 = __toESM(require("@mui/material/Button"));
4718
+ var import_Typography14 = __toESM(require("@mui/material/Typography"));
4657
4719
  var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress"));
4658
4720
  var import_Alert8 = __toESM(require("@mui/material/Alert"));
4721
+ var import_Collapse4 = __toESM(require("@mui/material/Collapse"));
4722
+ var import_icons_material10 = require("@mui/icons-material");
4659
4723
  var import_core_plugin_api6 = require("@backstage/core-plugin-api");
4660
4724
  var import_core_components2 = require("@backstage/core-components");
4661
4725
  init_api();
4662
4726
  init_format();
4663
4727
  init_ui();
4728
+ init_BudgetLimitList();
4664
4729
  init_budget();
4665
- var DEFAULT_KEYS_HREF = "/litellm?tab=keys";
4730
+ var MODULE_PATH = "/litellm";
4666
4731
  var LEVEL = {
4667
4732
  key: { name: "Key", none: "No key budget" },
4668
4733
  user: { name: "User", none: "No personal budget" },
4669
4734
  team: { name: "Team", none: "No team budget" }
4670
4735
  };
4736
+ var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
4737
+ var CTA_LABELS = {
4738
+ "new-key": "New key",
4739
+ module: "Open module",
4740
+ "all-limits": "All limits"
4741
+ };
4671
4742
  function fmtUsdShort(n) {
4672
4743
  const v = n ?? 0;
4673
4744
  if (v >= 100 && Number.isInteger(v)) return `$${v}`;
@@ -4690,8 +4761,8 @@ var LevelGauge = ({
4690
4761
  const limit = gauge.limit;
4691
4762
  const tone = limit ? budgetTone2(limit.pct) : "neutral";
4692
4763
  const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
4693
- return /* @__PURE__ */ import_react14.default.createElement(
4694
- import_Box13.default,
4764
+ return /* @__PURE__ */ import_react15.default.createElement(
4765
+ import_Box14.default,
4695
4766
  {
4696
4767
  sx: {
4697
4768
  display: "flex",
@@ -4701,7 +4772,7 @@ var LevelGauge = ({
4701
4772
  flex: 1
4702
4773
  }
4703
4774
  },
4704
- /* @__PURE__ */ import_react14.default.createElement(
4775
+ /* @__PURE__ */ import_react15.default.createElement(
4705
4776
  Gauge,
4706
4777
  {
4707
4778
  value: limit?.pct ?? 0,
@@ -4710,8 +4781,8 @@ var LevelGauge = ({
4710
4781
  label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
4711
4782
  }
4712
4783
  ),
4713
- /* @__PURE__ */ import_react14.default.createElement(
4714
- import_Typography13.default,
4784
+ /* @__PURE__ */ import_react15.default.createElement(
4785
+ import_Typography14.default,
4715
4786
  {
4716
4787
  sx: (theme) => ({
4717
4788
  mt: 0.75,
@@ -4724,8 +4795,8 @@ var LevelGauge = ({
4724
4795
  },
4725
4796
  name
4726
4797
  ),
4727
- limit ? /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement(
4728
- import_Typography13.default,
4798
+ limit ? /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
4799
+ import_Typography14.default,
4729
4800
  {
4730
4801
  variant: "caption",
4731
4802
  title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
@@ -4740,8 +4811,8 @@ var LevelGauge = ({
4740
4811
  }
4741
4812
  },
4742
4813
  limit.label
4743
- ), /* @__PURE__ */ import_react14.default.createElement(
4744
- import_Typography13.default,
4814
+ ), /* @__PURE__ */ import_react15.default.createElement(
4815
+ import_Typography14.default,
4745
4816
  {
4746
4817
  variant: "caption",
4747
4818
  color: "text.secondary",
@@ -4752,8 +4823,8 @@ var LevelGauge = ({
4752
4823
  }
4753
4824
  },
4754
4825
  spendCaption(limit)
4755
- ), extra > 0 ? /* @__PURE__ */ import_react14.default.createElement(
4756
- import_Typography13.default,
4826
+ ), extra > 0 ? /* @__PURE__ */ import_react15.default.createElement(
4827
+ import_Typography14.default,
4757
4828
  {
4758
4829
  component: import_core_components2.Link,
4759
4830
  to: keysHref,
@@ -4768,8 +4839,8 @@ var LevelGauge = ({
4768
4839
  "+",
4769
4840
  extra,
4770
4841
  " more"
4771
- ) : /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ import_react14.default.createElement(
4772
- import_Typography13.default,
4842
+ ) : /* @__PURE__ */ import_react15.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ import_react15.default.createElement(
4843
+ import_Typography14.default,
4773
4844
  {
4774
4845
  variant: "caption",
4775
4846
  color: "text.secondary",
@@ -4782,16 +4853,32 @@ var LevelGauge = ({
4782
4853
  var LiteLLMBudgetGauges = ({
4783
4854
  title = "Budget",
4784
4855
  size = 72,
4785
- keysHref = DEFAULT_KEYS_HREF,
4856
+ keysHref,
4857
+ moduleHref = MODULE_PATH,
4858
+ onCreateKey,
4859
+ ctas,
4860
+ maxExpandedKeys = 8,
4861
+ expanded,
4862
+ defaultExpanded = false,
4863
+ onExpandedChange,
4786
4864
  action
4787
4865
  }) => {
4788
4866
  const api = (0, import_core_plugin_api6.useApi)(liteLlmApiRef);
4789
- const [loading, setLoading] = (0, import_react14.useState)(true);
4790
- const [error, setError] = (0, import_react14.useState)(null);
4791
- const [user, setUser] = (0, import_react14.useState)(null);
4792
- const [teams, setTeams] = (0, import_react14.useState)([]);
4793
- const [keys, setKeys] = (0, import_react14.useState)([]);
4794
- (0, import_react14.useEffect)(() => {
4867
+ const [loading, setLoading] = (0, import_react15.useState)(true);
4868
+ const [error, setError] = (0, import_react15.useState)(null);
4869
+ const [user, setUser] = (0, import_react15.useState)(null);
4870
+ const [teams, setTeams] = (0, import_react15.useState)([]);
4871
+ const [keys, setKeys] = (0, import_react15.useState)([]);
4872
+ const isControlled = expanded !== void 0;
4873
+ const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react15.useState)(defaultExpanded);
4874
+ const isExpanded = isControlled ? expanded : uncontrolledExpanded;
4875
+ const toggleExpanded = (0, import_react15.useCallback)(() => {
4876
+ const next = !isExpanded;
4877
+ if (!isControlled) setUncontrolledExpanded(next);
4878
+ onExpandedChange?.(next);
4879
+ }, [isControlled, isExpanded, onExpandedChange]);
4880
+ const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
4881
+ (0, import_react15.useEffect)(() => {
4795
4882
  let cancelled = false;
4796
4883
  setLoading(true);
4797
4884
  setError(null);
@@ -4815,29 +4902,109 @@ var LiteLLMBudgetGauges = ({
4815
4902
  cancelled = true;
4816
4903
  };
4817
4904
  }, [api]);
4818
- const summary = (0, import_react14.useMemo)(
4905
+ const summary = (0, import_react15.useMemo)(
4819
4906
  () => buildBudgetSummary(user, teams, keys, keys.length),
4820
4907
  [user, teams, keys]
4821
4908
  );
4822
- const gauges = (0, import_react14.useMemo)(() => buildBudgetGauges(summary), [summary]);
4823
- const headline = (0, import_react14.useMemo)(() => budgetHeadline(summary), [summary]);
4909
+ const gauges = (0, import_react15.useMemo)(() => buildBudgetGauges(summary), [summary]);
4910
+ const headline = (0, import_react15.useMemo)(() => budgetHeadline(summary), [summary]);
4911
+ const expandedSummary = (0, import_react15.useMemo)(
4912
+ () => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
4913
+ [user, teams, keys, maxExpandedKeys]
4914
+ );
4915
+ const expandedLimits = (0, import_react15.useMemo)(
4916
+ () => allBudgetLimits(expandedSummary),
4917
+ [expandedSummary]
4918
+ );
4824
4919
  const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
4825
- return /* @__PURE__ */ import_react14.default.createElement(import_Paper9.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ import_react14.default.createElement(
4920
+ const resolvedCtas = (0, import_react15.useMemo)(() => {
4921
+ const list = ctas ?? DEFAULT_CTAS;
4922
+ return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
4923
+ }, [ctas, headline.count]);
4924
+ const renderCta = (cta, index) => {
4925
+ const label = cta.label ?? CTA_LABELS[cta.kind];
4926
+ const key = `${cta.kind}-${index}`;
4927
+ switch (cta.kind) {
4928
+ case "new-key":
4929
+ return onCreateKey ? /* @__PURE__ */ import_react15.default.createElement(import_Button6.default, { key, size: "small", variant: "contained", onClick: onCreateKey }, label) : /* @__PURE__ */ import_react15.default.createElement(
4930
+ import_Button6.default,
4931
+ {
4932
+ key,
4933
+ size: "small",
4934
+ variant: "contained",
4935
+ component: import_core_components2.Link,
4936
+ to: `${moduleHref}?generate=1`
4937
+ },
4938
+ label
4939
+ );
4940
+ case "module":
4941
+ return /* @__PURE__ */ import_react15.default.createElement(import_Button6.default, { key, size: "small", variant: "outlined", component: import_core_components2.Link, to: moduleHref }, label);
4942
+ case "all-limits":
4943
+ return /* @__PURE__ */ import_react15.default.createElement(
4944
+ import_Button6.default,
4945
+ {
4946
+ key,
4947
+ size: "small",
4948
+ variant: "text",
4949
+ onClick: toggleExpanded,
4950
+ endIcon: /* @__PURE__ */ import_react15.default.createElement(
4951
+ import_icons_material10.ExpandMore,
4952
+ {
4953
+ sx: {
4954
+ transform: isExpanded ? "rotate(180deg)" : "none",
4955
+ transition: (theme) => theme.transitions.create("transform")
4956
+ }
4957
+ }
4958
+ )
4959
+ },
4960
+ label
4961
+ );
4962
+ /* istanbul ignore next — exhaustiveness */
4963
+ default:
4964
+ return null;
4965
+ }
4966
+ };
4967
+ const hasFooter = resolvedCtas.length > 0 || !!action;
4968
+ return /* @__PURE__ */ import_react15.default.createElement(import_Paper10.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react15.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ import_react15.default.createElement(import_Typography14.default, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ import_react15.default.createElement(
4826
4969
  StatusPill,
4827
4970
  {
4828
4971
  label: summaryText,
4829
4972
  tone: budgetTone2(headline.closest?.pct ?? 0),
4830
4973
  dot: headline.count > 0
4831
4974
  }
4832
- )), loading && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ import_react14.default.createElement(import_CircularProgress8.default, { size: 28 })), !loading && error && /* @__PURE__ */ import_react14.default.createElement(import_Alert8.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ import_react14.default.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref }))), action && /* @__PURE__ */ import_react14.default.createElement(
4833
- import_Box13.default,
4975
+ )), loading && /* @__PURE__ */ import_react15.default.createElement(import_Box14.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ import_react15.default.createElement(import_CircularProgress8.default, { size: 28 })), !loading && error && /* @__PURE__ */ import_react15.default.createElement(import_Alert8.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ import_react15.default.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref: keysTabHref }))), /* @__PURE__ */ import_react15.default.createElement(import_Collapse4.default, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ import_react15.default.createElement(
4976
+ import_Box14.default,
4977
+ {
4978
+ sx: {
4979
+ mt: 1.5,
4980
+ // Bound the expanded list so a user with many limits still
4981
+ // keeps the card compact and the toggle within reach.
4982
+ maxHeight: 300,
4983
+ overflowY: "auto"
4984
+ }
4985
+ },
4986
+ /* @__PURE__ */ import_react15.default.createElement(LimitListPanel, null, /* @__PURE__ */ import_react15.default.createElement(
4987
+ BudgetLimitList,
4988
+ {
4989
+ limits: expandedLimits,
4990
+ hiddenKeyCount: expandedSummary.hiddenBudgetedKeys,
4991
+ keysHref: keysTabHref
4992
+ }
4993
+ ))
4994
+ ))), hasFooter && /* @__PURE__ */ import_react15.default.createElement(
4995
+ import_Box14.default,
4834
4996
  {
4835
4997
  sx: (theme) => ({
4836
4998
  mt: 2,
4837
4999
  pt: 2,
4838
- borderTop: `1px solid ${theme.palette.divider}`
5000
+ borderTop: `1px solid ${theme.palette.divider}`,
5001
+ display: "flex",
5002
+ alignItems: "center",
5003
+ gap: 1,
5004
+ flexWrap: "wrap"
4839
5005
  })
4840
5006
  },
5007
+ resolvedCtas.map(renderCta),
4841
5008
  action
4842
5009
  ));
4843
5010
  };