@acarmisc/backstage-plugin-litellm 0.23.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
@@ -335,7 +335,7 @@ function SegmentedControl({
335
335
  })
336
336
  );
337
337
  }
338
- var import_react, import_Box, import_ButtonBase, import_Paper, import_Typography, import_styles, MONTHS, fmtDateShort, fmtCompact, fmtUsdCompact, CHART_COLORS, SERIES, OTHER_COLOR, ChartTooltip, SeriesLegend, StatusPill, TagChip, SectionCard, ChartCard, MetricStrip, Stat, Meter, dataTableSx, quietIconButtonSx, EmptyState;
338
+ var import_react, import_Box, import_ButtonBase, import_Paper, import_Typography, import_styles, MONTHS, fmtDateShort, fmtCompact, fmtUsdCompact, CHART_COLORS, SERIES, OTHER_COLOR, ChartTooltip, SeriesLegend, StatusPill, TagChip, SectionCard, ChartCard, MetricStrip, Stat, Meter, Gauge, dataTableSx, quietIconButtonSx, EmptyState;
339
339
  var init_ui = __esm({
340
340
  "src/components/ui.tsx"() {
341
341
  "use strict";
@@ -705,6 +705,102 @@ var init_ui = __esm({
705
705
  }
706
706
  )
707
707
  );
708
+ Gauge = ({ value, tone = "accent", size = 72, thickness = 6, label, caption }) => {
709
+ const clamped = Math.max(0, Math.min(100, value ?? 0));
710
+ const r = (size - thickness) / 2;
711
+ const c = 2 * Math.PI * r;
712
+ const dash = clamped / 100 * c;
713
+ return /* @__PURE__ */ import_react.default.createElement(
714
+ import_Box.default,
715
+ {
716
+ sx: (theme) => ({
717
+ position: "relative",
718
+ width: size,
719
+ height: size,
720
+ flexShrink: 0,
721
+ color: toneColor(theme, tone)
722
+ })
723
+ },
724
+ /* @__PURE__ */ import_react.default.createElement(
725
+ import_Box.default,
726
+ {
727
+ component: "svg",
728
+ viewBox: `0 0 ${size} ${size}`,
729
+ width: size,
730
+ height: size,
731
+ sx: { display: "block", transform: "rotate(-90deg)" }
732
+ },
733
+ /* @__PURE__ */ import_react.default.createElement(
734
+ import_Box.default,
735
+ {
736
+ component: "circle",
737
+ cx: size / 2,
738
+ cy: size / 2,
739
+ r,
740
+ fill: "none",
741
+ strokeWidth: thickness,
742
+ sx: (theme) => ({
743
+ stroke: (0, import_styles.alpha)(
744
+ theme.palette.text.primary,
745
+ theme.palette.mode === "dark" ? 0.14 : 0.09
746
+ )
747
+ })
748
+ }
749
+ ),
750
+ /* @__PURE__ */ import_react.default.createElement(
751
+ import_Box.default,
752
+ {
753
+ component: "circle",
754
+ cx: size / 2,
755
+ cy: size / 2,
756
+ r,
757
+ fill: "none",
758
+ strokeWidth: thickness,
759
+ strokeLinecap: "round",
760
+ strokeDasharray: `${dash} ${c - dash}`,
761
+ sx: (theme) => ({
762
+ stroke: "currentColor",
763
+ transition: theme.transitions.create("stroke-dasharray")
764
+ })
765
+ }
766
+ )
767
+ ),
768
+ /* @__PURE__ */ import_react.default.createElement(
769
+ import_Box.default,
770
+ {
771
+ sx: {
772
+ position: "absolute",
773
+ inset: 0,
774
+ display: "flex",
775
+ flexDirection: "column",
776
+ alignItems: "center",
777
+ justifyContent: "center",
778
+ lineHeight: 1
779
+ }
780
+ },
781
+ /* @__PURE__ */ import_react.default.createElement(
782
+ import_Typography.default,
783
+ {
784
+ sx: {
785
+ fontSize: size <= 64 ? 15 : 18,
786
+ fontWeight: 700,
787
+ fontVariantNumeric: "tabular-nums",
788
+ color: "text.primary"
789
+ }
790
+ },
791
+ label ?? `${Math.round(value)}%`
792
+ ),
793
+ caption && /* @__PURE__ */ import_react.default.createElement(
794
+ import_Typography.default,
795
+ {
796
+ variant: "caption",
797
+ sx: { fontSize: 9.5, color: "text.secondary", mt: 0.25 }
798
+ },
799
+ caption
800
+ )
801
+ )
802
+ );
803
+ };
708
804
  dataTableSx = (theme) => ({
709
805
  // Element selectors, not `.MuiTableCell-head`: host apps may set a MUI
710
806
  // classname prefix (Backstage ships `v5-`), which breaks global class targeting.
@@ -2835,6 +2931,29 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
2835
2931
  teams: teamLimits
2836
2932
  };
2837
2933
  }
2934
+ function allBudgetLimits(summary) {
2935
+ return [
2936
+ ...summary.keys,
2937
+ ...summary.user ? [summary.user] : [],
2938
+ ...summary.teams
2939
+ ];
2940
+ }
2941
+ function buildBudgetGauges(summary) {
2942
+ const byLevel = {
2943
+ key: summary.keys,
2944
+ user: summary.user ? [summary.user] : [],
2945
+ team: summary.teams
2946
+ };
2947
+ return GAUGE_KINDS.map((kind) => {
2948
+ const limits = byLevel[kind];
2949
+ const count = kind === "key" ? limits.length + summary.hiddenBudgetedKeys : limits.length;
2950
+ return {
2951
+ kind,
2952
+ count,
2953
+ limit: limits.length ? limits.reduce((a, b) => b.pct > a.pct ? b : a) : void 0
2954
+ };
2955
+ });
2956
+ }
2838
2957
  function budgetHeadline(summary) {
2839
2958
  const all = [
2840
2959
  ...summary.keys,
@@ -2861,13 +2980,15 @@ function budgetTone2(pct) {
2861
2980
  if (pct >= 80) return "warning";
2862
2981
  return "accent";
2863
2982
  }
2983
+ var GAUGE_KINDS;
2864
2984
  var init_budget = __esm({
2865
2985
  "src/budget.ts"() {
2866
2986
  "use strict";
2987
+ GAUGE_KINDS = ["key", "user", "team"];
2867
2988
  }
2868
2989
  });
2869
2990
 
2870
- // src/components/LiteLLMBudgetWidget.tsx
2991
+ // src/components/BudgetLimitList.tsx
2871
2992
  function levelToneColor(tone, theme) {
2872
2993
  switch (tone) {
2873
2994
  case "success":
@@ -2882,25 +3003,125 @@ function levelToneColor(tone, theme) {
2882
3003
  return theme.palette.primary.main;
2883
3004
  }
2884
3005
  }
2885
- 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;
2886
- var init_LiteLLMBudgetWidget = __esm({
2887
- "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"() {
2888
3009
  "use strict";
2889
3010
  import_react7 = __toESM(require("react"));
2890
- import_Paper5 = __toESM(require("@mui/material/Paper"));
2891
3011
  import_Box7 = __toESM(require("@mui/material/Box"));
3012
+ import_Paper5 = __toESM(require("@mui/material/Paper"));
2892
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"));
2893
3115
  import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
2894
3116
  import_Alert3 = __toESM(require("@mui/material/Alert"));
2895
3117
  import_Collapse = __toESM(require("@mui/material/Collapse"));
2896
3118
  import_IconButton4 = __toESM(require("@mui/material/IconButton"));
2897
3119
  import_icons_material5 = require("@mui/icons-material");
2898
- import_styles4 = require("@mui/material/styles");
3120
+ import_styles5 = require("@mui/material/styles");
2899
3121
  import_core_plugin_api3 = require("@backstage/core-plugin-api");
2900
- import_core_components = require("@backstage/core-components");
2901
3122
  init_api();
2902
- init_format();
2903
3123
  init_ui();
3124
+ init_BudgetLimitList();
2904
3125
  init_budget();
2905
3126
  LevelFrame = ({
2906
3127
  rank,
@@ -2910,8 +3131,8 @@ var init_LiteLLMBudgetWidget = __esm({
2910
3131
  tone,
2911
3132
  isLast,
2912
3133
  children
2913
- }) => /* @__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(
2914
- 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,
2915
3136
  {
2916
3137
  sx: (theme) => ({
2917
3138
  width: 26,
@@ -2920,7 +3141,7 @@ var init_LiteLLMBudgetWidget = __esm({
2920
3141
  display: "flex",
2921
3142
  alignItems: "center",
2922
3143
  justifyContent: "center",
2923
- bgcolor: (0, import_styles4.alpha)(levelToneColor(tone, theme), 0.14),
3144
+ bgcolor: (0, import_styles5.alpha)(levelToneColor(tone, theme), 0.14),
2924
3145
  color: levelToneColor(tone, theme),
2925
3146
  fontSize: 12,
2926
3147
  fontWeight: 700,
@@ -2928,18 +3149,18 @@ var init_LiteLLMBudgetWidget = __esm({
2928
3149
  })
2929
3150
  },
2930
3151
  rank
2931
- ), /* @__PURE__ */ import_react7.default.createElement(
2932
- import_Box7.default,
3152
+ ), /* @__PURE__ */ import_react8.default.createElement(
3153
+ import_Box8.default,
2933
3154
  {
2934
3155
  sx: (theme) => ({
2935
3156
  flex: 1,
2936
3157
  width: 2,
2937
3158
  minHeight: 14,
2938
- 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)
2939
3160
  })
2940
3161
  }
2941
- )), /* @__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(
2942
- 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,
2943
3164
  {
2944
3165
  sx: (theme) => ({
2945
3166
  fontSize: 12.5,
@@ -2950,102 +3171,42 @@ var init_LiteLLMBudgetWidget = __esm({
2950
3171
  })
2951
3172
  },
2952
3173
  name
2953
- ), /* @__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)));
2954
- EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react7.default.createElement(
2955
- 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,
2956
3177
  {
2957
3178
  variant: "outlined",
2958
3179
  sx: (theme) => ({
2959
3180
  px: 1.5,
2960
3181
  py: 1.25,
2961
3182
  borderRadius: 1.5,
2962
- 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)
2963
3184
  })
2964
3185
  },
2965
- /* @__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)
2966
3187
  );
2967
- LimitCard = ({ limit }) => {
2968
- const tone = budgetTone2(limit.pct);
2969
- const pct = Math.round(limit.pct);
2970
- const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
2971
- 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(
2972
- import_Typography7.default,
2973
- {
2974
- variant: "body2",
2975
- sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
2976
- title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
2977
- },
2978
- limit.label
2979
- ), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
2980
- import_Typography7.default,
2981
- {
2982
- variant: "caption",
2983
- color: "text.secondary",
2984
- sx: {
2985
- fontSize: 10.5,
2986
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
2987
- display: "block",
2988
- overflow: "hidden",
2989
- textOverflow: "ellipsis",
2990
- whiteSpace: "nowrap"
2991
- }
2992
- },
2993
- limit.sublabel
2994
- )), 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(
2995
- import_Box7.default,
2996
- {
2997
- sx: {
2998
- display: "flex",
2999
- justifyContent: "space-between",
3000
- alignItems: "center",
3001
- gap: 1,
3002
- mt: 0.75
3003
- }
3004
- },
3005
- /* @__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`)),
3006
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3007
- ));
3008
- };
3009
3188
  CLOSEST_LEVEL_LABEL = {
3010
3189
  key: "key",
3011
3190
  user: "personal",
3012
3191
  team: "team"
3013
3192
  };
3014
3193
  KEYS_PATH = "/litellm?tab=keys";
3015
- 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");
3016
- HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react7.default.createElement(
3017
- import_Box7.default,
3194
+ HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react8.default.createElement(
3195
+ import_Box8.default,
3018
3196
  {
3019
3197
  sx: (theme) => ({
3020
3198
  px: 1.5,
3021
3199
  py: 1.25,
3022
3200
  borderRadius: 1.5,
3023
3201
  border: "1px dashed",
3024
- borderColor: (0, import_styles4.alpha)(theme.palette.text.primary, 0.18)
3202
+ borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
3025
3203
  })
3026
3204
  },
3027
- /* @__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.")
3028
3206
  );
3029
- TaggedLimit = ({ limit }) => {
3030
- const tone = budgetTone2(limit.pct);
3031
- return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
3032
- import_Typography7.default,
3033
- {
3034
- sx: (theme) => ({
3035
- fontSize: 11,
3036
- fontWeight: 700,
3037
- letterSpacing: "0.08em",
3038
- textTransform: "uppercase",
3039
- color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
3040
- mb: 0.5
3041
- })
3042
- },
3043
- limit.kind
3044
- ), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
3045
- };
3046
3207
  FullBody = ({ summary }) => {
3047
3208
  const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
3048
- 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(
3049
3210
  LevelFrame,
3050
3211
  {
3051
3212
  rank: 1,
@@ -3053,10 +3214,10 @@ var init_LiteLLMBudgetWidget = __esm({
3053
3214
  tagline: "per-key cap \xB7 highest priority",
3054
3215
  note: hasBudgetedKeys ? void 0 : "no key budgets set"
3055
3216
  },
3056
- !hasBudgetedKeys && /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3057
- summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3058
- summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys })
3059
- ), /* @__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(
3060
3221
  LevelFrame,
3061
3222
  {
3062
3223
  rank: 2,
@@ -3064,15 +3225,15 @@ var init_LiteLLMBudgetWidget = __esm({
3064
3225
  tagline: "cap on everything you spend with your own keys",
3065
3226
  note: "skipped for team-bound keys"
3066
3227
  },
3067
- 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.")
3068
- ), /* @__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 })));
3069
3230
  };
3070
3231
  CompactBody = ({ summary }) => {
3071
3232
  const { count } = budgetHeadline(summary);
3072
3233
  if (count === 0) {
3073
- 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.");
3074
3235
  }
3075
- 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 })));
3076
3237
  };
3077
3238
  LiteLLMBudgetWidget = ({
3078
3239
  title = "Budget Policy",
@@ -3083,13 +3244,13 @@ var init_LiteLLMBudgetWidget = __esm({
3083
3244
  action
3084
3245
  }) => {
3085
3246
  const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
3086
- const [loading, setLoading] = (0, import_react7.useState)(true);
3087
- const [error, setError] = (0, import_react7.useState)(null);
3088
- const [user, setUser] = (0, import_react7.useState)(null);
3089
- const [teams, setTeams] = (0, import_react7.useState)([]);
3090
- const [keys, setKeys] = (0, import_react7.useState)([]);
3091
- const [expanded, setExpanded] = (0, import_react7.useState)(defaultExpanded);
3092
- (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)(() => {
3093
3254
  let cancelled = false;
3094
3255
  setLoading(true);
3095
3256
  setError(null);
@@ -3113,19 +3274,19 @@ var init_LiteLLMBudgetWidget = __esm({
3113
3274
  cancelled = true;
3114
3275
  };
3115
3276
  }, [api]);
3116
- const summary = (0, import_react7.useMemo)(
3277
+ const summary = (0, import_react8.useMemo)(
3117
3278
  () => buildBudgetSummary(user, teams, keys, maxKeys),
3118
3279
  [user, teams, keys, maxKeys]
3119
3280
  );
3120
- const headline = (0, import_react7.useMemo)(() => budgetHeadline(summary), [summary]);
3281
+ const headline = (0, import_react8.useMemo)(() => budgetHeadline(summary), [summary]);
3121
3282
  const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
3122
3283
  const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
3123
3284
  headline.closest.pct
3124
3285
  )}%`;
3125
3286
  const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
3126
- 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 })));
3127
- return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react7.default.createElement(
3128
- 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,
3129
3290
  {
3130
3291
  onClick: () => setExpanded((e) => !e),
3131
3292
  sx: {
@@ -3137,14 +3298,14 @@ var init_LiteLLMBudgetWidget = __esm({
3137
3298
  mb: expanded ? 1.5 : 0
3138
3299
  }
3139
3300
  },
3140
- /* @__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(
3141
3302
  StatusPill,
3142
3303
  {
3143
3304
  label: summaryText,
3144
3305
  tone: budgetTone2(headline.closest?.pct ?? 0)
3145
3306
  }
3146
3307
  ))),
3147
- /* @__PURE__ */ import_react7.default.createElement(
3308
+ /* @__PURE__ */ import_react8.default.createElement(
3148
3309
  import_IconButton4.default,
3149
3310
  {
3150
3311
  size: "small",
@@ -3160,10 +3321,10 @@ var init_LiteLLMBudgetWidget = __esm({
3160
3321
  transition: theme.transitions.create("transform")
3161
3322
  })
3162
3323
  },
3163
- /* @__PURE__ */ import_react7.default.createElement(import_icons_material5.ExpandMore, null)
3324
+ /* @__PURE__ */ import_react8.default.createElement(import_icons_material5.ExpandMore, null)
3164
3325
  )
3165
- ) : /* @__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(
3166
- 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,
3167
3328
  {
3168
3329
  sx: (theme) => ({
3169
3330
  mt: 2,
@@ -3192,14 +3353,14 @@ function budgetStatValue(hidden, budget) {
3192
3353
  if (budget > 0) return fmtUsd2(budget);
3193
3354
  return "Unlimited";
3194
3355
  }
3195
- 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;
3196
3357
  var init_TeamUsage = __esm({
3197
3358
  "src/components/TeamUsage.tsx"() {
3198
3359
  "use strict";
3199
- import_react8 = __toESM(require("react"));
3200
- import_Paper6 = __toESM(require("@mui/material/Paper"));
3201
- import_Box8 = __toESM(require("@mui/material/Box"));
3202
- 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"));
3203
3364
  import_Skeleton4 = __toESM(require("@mui/material/Skeleton"));
3204
3365
  import_Divider3 = __toESM(require("@mui/material/Divider"));
3205
3366
  import_Table4 = __toESM(require("@mui/material/Table"));
@@ -3212,13 +3373,13 @@ var init_TeamUsage = __esm({
3212
3373
  import_IconButton5 = __toESM(require("@mui/material/IconButton"));
3213
3374
  import_Button5 = __toESM(require("@mui/material/Button"));
3214
3375
  import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
3215
- import_styles5 = require("@mui/material/styles");
3376
+ import_styles6 = require("@mui/material/styles");
3216
3377
  import_icons_material6 = require("@mui/icons-material");
3217
3378
  import_recharts2 = require("recharts");
3218
3379
  init_ui();
3219
3380
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
3220
3381
  TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
3221
- const [expanded, setExpanded] = (0, import_react8.useState)(false);
3382
+ const [expanded, setExpanded] = (0, import_react9.useState)(false);
3222
3383
  const chart = useChartTheme();
3223
3384
  const hidden = team.budget_hidden === true;
3224
3385
  const budget = hidden ? 0 : team.max_budget ?? 0;
@@ -3230,24 +3391,24 @@ var init_TeamUsage = __esm({
3230
3391
  const renderDailySpendSection = () => {
3231
3392
  if (hidden) return null;
3232
3393
  if (usageLoading) {
3233
- 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 }));
3234
3395
  }
3235
3396
  if (dailyData.length === 0) return null;
3236
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mb: 2.5 }, /* @__PURE__ */ import_react8.default.createElement(
3237
- 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,
3238
3399
  {
3239
3400
  variant: "caption",
3240
3401
  color: "text.secondary",
3241
3402
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
3242
3403
  },
3243
3404
  "Daily spend"
3244
- ), /* @__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(
3245
3406
  import_recharts2.Tooltip,
3246
3407
  {
3247
3408
  cursor: chart.cursor,
3248
- 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)}` })
3249
3410
  }
3250
- ), /* @__PURE__ */ import_react8.default.createElement(
3411
+ ), /* @__PURE__ */ import_react9.default.createElement(
3251
3412
  import_recharts2.Area,
3252
3413
  {
3253
3414
  type: "monotone",
@@ -3260,8 +3421,8 @@ var init_TeamUsage = __esm({
3260
3421
  }
3261
3422
  )))));
3262
3423
  };
3263
- const sectionLabel = (label) => /* @__PURE__ */ import_react8.default.createElement(
3264
- import_Typography8.default,
3424
+ const sectionLabel = (label) => /* @__PURE__ */ import_react9.default.createElement(
3425
+ import_Typography9.default,
3265
3426
  {
3266
3427
  variant: "caption",
3267
3428
  color: "text.secondary",
@@ -3269,8 +3430,8 @@ var init_TeamUsage = __esm({
3269
3430
  },
3270
3431
  label
3271
3432
  );
3272
- 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(
3273
- 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,
3274
3435
  {
3275
3436
  sx: (theme) => ({
3276
3437
  width: 36,
@@ -3280,20 +3441,20 @@ var init_TeamUsage = __esm({
3280
3441
  display: "flex",
3281
3442
  alignItems: "center",
3282
3443
  justifyContent: "center",
3283
- bgcolor: (0, import_styles5.alpha)(theme.palette.primary.main, 0.1),
3444
+ bgcolor: (0, import_styles6.alpha)(theme.palette.primary.main, 0.1),
3284
3445
  color: theme.palette.primary.main
3285
3446
  })
3286
3447
  },
3287
- /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.Group, { fontSize: "small" })
3288
- ), /* @__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(
3289
- 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,
3290
3451
  {
3291
3452
  variant: "caption",
3292
3453
  color: "text.secondary",
3293
3454
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3294
3455
  },
3295
3456
  team.team_id
3296
- )), 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(
3297
3458
  import_Button5.default,
3298
3459
  {
3299
3460
  size: "small",
@@ -3302,7 +3463,7 @@ var init_TeamUsage = __esm({
3302
3463
  sx: { textTransform: "none" }
3303
3464
  },
3304
3465
  "Edit"
3305
- ), /* @__PURE__ */ import_react8.default.createElement(
3466
+ ), /* @__PURE__ */ import_react9.default.createElement(
3306
3467
  import_IconButton5.default,
3307
3468
  {
3308
3469
  size: "small",
@@ -3315,9 +3476,9 @@ var init_TeamUsage = __esm({
3315
3476
  transition: theme.transitions.create("transform")
3316
3477
  })
3317
3478
  },
3318
- /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.ExpandMore, null)
3319
- )), /* @__PURE__ */ import_react8.default.createElement(
3320
- import_Box8.default,
3479
+ /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.ExpandMore, null)
3480
+ )), /* @__PURE__ */ import_react9.default.createElement(
3481
+ import_Box9.default,
3321
3482
  {
3322
3483
  sx: {
3323
3484
  display: "grid",
@@ -3327,35 +3488,35 @@ var init_TeamUsage = __esm({
3327
3488
  maxWidth: 640
3328
3489
  }
3329
3490
  },
3330
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3331
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3332
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3333
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3334
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3335
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3336
- ), 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(
3337
3498
  import_TableContainer4.default,
3338
3499
  {
3339
- component: import_Paper6.default,
3500
+ component: import_Paper7.default,
3340
3501
  variant: "outlined",
3341
3502
  sx: { borderRadius: 1.5 }
3342
3503
  },
3343
- /* @__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(
3344
- 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,
3345
3506
  {
3346
3507
  variant: "caption",
3347
3508
  color: "text.secondary",
3348
3509
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3349
3510
  },
3350
3511
  m.user_id
3351
- )) : /* @__PURE__ */ import_react8.default.createElement(
3352
- import_Typography8.default,
3512
+ )) : /* @__PURE__ */ import_react9.default.createElement(
3513
+ import_Typography9.default,
3353
3514
  {
3354
3515
  variant: "body2",
3355
3516
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
3356
3517
  },
3357
3518
  m.user_id
3358
- )), /* @__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(
3359
3520
  StatusPill,
3360
3521
  {
3361
3522
  label: m.role,
@@ -3363,7 +3524,7 @@ var init_TeamUsage = __esm({
3363
3524
  dot: false
3364
3525
  }
3365
3526
  ))))))
3366
- ) : /* @__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."))));
3367
3528
  };
3368
3529
  TeamUsage = ({
3369
3530
  teams,
@@ -3375,22 +3536,22 @@ var init_TeamUsage = __esm({
3375
3536
  canCreate,
3376
3537
  onCreateTeam
3377
3538
  }) => {
3378
- const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react8.default.createElement(
3539
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react9.default.createElement(
3379
3540
  import_Button5.default,
3380
3541
  {
3381
3542
  variant: "contained",
3382
3543
  color: "primary",
3383
3544
  disableElevation: true,
3384
- startIcon: /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.Add, null),
3545
+ startIcon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Add, null),
3385
3546
  onClick: onCreateTeam
3386
3547
  },
3387
3548
  "Create Team"
3388
3549
  ) : void 0;
3389
3550
  if (loading) {
3390
- 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 }));
3391
3552
  }
3392
3553
  if (!teams.length) {
3393
- 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(
3394
3555
  EmptyState,
3395
3556
  {
3396
3557
  message: "You're not a member of any LiteLLM team yet.",
@@ -3398,14 +3559,14 @@ var init_TeamUsage = __esm({
3398
3559
  }
3399
3560
  ));
3400
3561
  }
3401
- return /* @__PURE__ */ import_react8.default.createElement(
3562
+ return /* @__PURE__ */ import_react9.default.createElement(
3402
3563
  SectionCard,
3403
3564
  {
3404
3565
  title: "Teams",
3405
3566
  subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
3406
3567
  actions: createAction
3407
3568
  },
3408
- /* @__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(
3409
3570
  TeamCard,
3410
3571
  {
3411
3572
  key: team.team_id,
@@ -3439,12 +3600,12 @@ function fmtTokens(n) {
3439
3600
  if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
3440
3601
  return String(n);
3441
3602
  }
3442
- 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;
3443
3604
  var init_ModelsTable = __esm({
3444
3605
  "src/components/ModelsTable.tsx"() {
3445
3606
  "use strict";
3446
- import_react9 = __toESM(require("react"));
3447
- import_Box9 = __toESM(require("@mui/material/Box"));
3607
+ import_react10 = __toESM(require("react"));
3608
+ import_Box10 = __toESM(require("@mui/material/Box"));
3448
3609
  import_Table5 = __toESM(require("@mui/material/Table"));
3449
3610
  import_TableBody5 = __toESM(require("@mui/material/TableBody"));
3450
3611
  import_TableCell5 = __toESM(require("@mui/material/TableCell"));
@@ -3452,7 +3613,7 @@ var init_ModelsTable = __esm({
3452
3613
  import_TableHead5 = __toESM(require("@mui/material/TableHead"));
3453
3614
  import_TableRow5 = __toESM(require("@mui/material/TableRow"));
3454
3615
  import_TableSortLabel2 = __toESM(require("@mui/material/TableSortLabel"));
3455
- import_Typography9 = __toESM(require("@mui/material/Typography"));
3616
+ import_Typography10 = __toESM(require("@mui/material/Typography"));
3456
3617
  import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
3457
3618
  import_Select = __toESM(require("@mui/material/Select"));
3458
3619
  import_FormControl = __toESM(require("@mui/material/FormControl"));
@@ -3465,24 +3626,24 @@ var init_ModelsTable = __esm({
3465
3626
  import_Snackbar = __toESM(require("@mui/material/Snackbar"));
3466
3627
  import_Alert4 = __toESM(require("@mui/material/Alert"));
3467
3628
  init_ui();
3468
- import_styles6 = require("@mui/material/styles");
3629
+ import_styles7 = require("@mui/material/styles");
3469
3630
  ALL_PROXY_MODELS2 = "all-proxy-models";
3470
3631
  ModelsTable = ({ allModels, teams, loading }) => {
3471
- const theme = (0, import_styles6.useTheme)();
3472
- const [selectedTeamId, setSelectedTeamId] = (0, import_react9.useState)("");
3473
- const [filterText, setFilterText] = (0, import_react9.useState)("");
3474
- const [copiedSnackbar, setCopiedSnackbar] = (0, import_react9.useState)(false);
3475
- const [sortKey, setSortKey] = (0, import_react9.useState)("model_name");
3476
- const [sortDirection, setSortDirection] = (0, import_react9.useState)("asc");
3477
- 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)(
3478
3639
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
3479
3640
  [teams, selectedTeamId]
3480
3641
  );
3481
- const teamModels = (0, import_react9.useMemo)(() => {
3642
+ const teamModels = (0, import_react10.useMemo)(() => {
3482
3643
  if (!selectedTeam) return [];
3483
3644
  return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
3484
3645
  }, [allModels, selectedTeam]);
3485
- const filteredModels = (0, import_react9.useMemo)(() => {
3646
+ const filteredModels = (0, import_react10.useMemo)(() => {
3486
3647
  const q = filterText.trim().toLowerCase();
3487
3648
  let models = teamModels;
3488
3649
  if (q) {
@@ -3529,10 +3690,10 @@ var init_ModelsTable = __esm({
3529
3690
  return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
3530
3691
  });
3531
3692
  }, [teamModels, filterText, sortKey, sortDirection]);
3532
- const handleCopyModelId = (0, import_react9.useCallback)((modelId) => {
3693
+ const handleCopyModelId = (0, import_react10.useCallback)((modelId) => {
3533
3694
  navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
3534
3695
  }, []);
3535
- const handleSort = (0, import_react9.useCallback)((key) => {
3696
+ const handleSort = (0, import_react10.useCallback)((key) => {
3536
3697
  if (sortKey === key) {
3537
3698
  setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
3538
3699
  } else {
@@ -3554,13 +3715,13 @@ var init_ModelsTable = __esm({
3554
3715
  return theme.palette.info.main;
3555
3716
  }
3556
3717
  };
3557
- return /* @__PURE__ */ import_react9.default.createElement(
3718
+ return /* @__PURE__ */ import_react10.default.createElement(
3558
3719
  SectionCard,
3559
3720
  {
3560
3721
  title: "Available Models",
3561
3722
  subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
3562
3723
  },
3563
- /* @__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(
3564
3725
  import_Select.default,
3565
3726
  {
3566
3727
  labelId: "team-select-label",
@@ -3568,8 +3729,8 @@ var init_ModelsTable = __esm({
3568
3729
  label: "Team",
3569
3730
  onChange: (e) => setSelectedTeamId(e.target.value)
3570
3731
  },
3571
- 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))
3572
- )), /* @__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(
3573
3734
  import_TextField5.default,
3574
3735
  {
3575
3736
  label: "Filter Models",
@@ -3579,33 +3740,33 @@ var init_ModelsTable = __esm({
3579
3740
  disabled: !selectedTeamId,
3580
3741
  sx: { minWidth: 240, mb: 2 },
3581
3742
  InputProps: {
3582
- 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" }))
3583
3744
  }
3584
3745
  }
3585
3746
  )),
3586
- loading && /* @__PURE__ */ import_react9.default.createElement(EmptyState, { message: "Loading models\u2026" }),
3587
- !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(
3588
3749
  EmptyState,
3589
3750
  {
3590
3751
  message: "Select a team to see available models",
3591
3752
  hint: "Each team may have a different set of models assigned"
3592
3753
  }
3593
3754
  ),
3594
- !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react9.default.createElement(
3755
+ !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
3595
3756
  EmptyState,
3596
3757
  {
3597
3758
  message: "No models available for this team",
3598
3759
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
3599
3760
  }
3600
3761
  ),
3601
- !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(
3602
3763
  EmptyState,
3603
3764
  {
3604
3765
  message: "No models match your search",
3605
3766
  hint: "Try a different search term"
3606
3767
  }
3607
3768
  ),
3608
- !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(
3609
3770
  "span",
3610
3771
  {
3611
3772
  role: "button",
@@ -3615,9 +3776,9 @@ var init_ModelsTable = __esm({
3615
3776
  onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
3616
3777
  style: { cursor: "pointer", display: "inline-flex" }
3617
3778
  },
3618
- /* @__PURE__ */ import_react9.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3619
- )), /* @__PURE__ */ import_react9.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react9.default.createElement(
3620
- 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,
3621
3782
  {
3622
3783
  component: "span",
3623
3784
  role: "button",
@@ -3627,9 +3788,9 @@ var init_ModelsTable = __esm({
3627
3788
  sx: quietIconButtonSx("accent"),
3628
3789
  style: { cursor: "pointer", display: "inline-flex" }
3629
3790
  },
3630
- /* @__PURE__ */ import_react9.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
3631
- )))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(
3632
- 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,
3633
3794
  {
3634
3795
  component: "span",
3635
3796
  sx: {
@@ -3640,13 +3801,13 @@ var init_ModelsTable = __esm({
3640
3801
  fontSize: 11,
3641
3802
  fontWeight: 600,
3642
3803
  textTransform: "capitalize",
3643
- bgcolor: (0, import_styles6.alpha)(modeColor(m.mode), 0.1),
3804
+ bgcolor: (0, import_styles7.alpha)(modeColor(m.mode), 0.1),
3644
3805
  color: modeColor(m.mode)
3645
3806
  }
3646
3807
  },
3647
3808
  m.mode
3648
- )), /* @__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" })))))))),
3649
- /* @__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(
3650
3811
  import_Snackbar.default,
3651
3812
  {
3652
3813
  open: copiedSnackbar,
@@ -3654,7 +3815,7 @@ var init_ModelsTable = __esm({
3654
3815
  onClose: () => setCopiedSnackbar(false),
3655
3816
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
3656
3817
  },
3657
- /* @__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")
3658
3819
  )
3659
3820
  );
3660
3821
  };
@@ -3682,10 +3843,10 @@ function formatDateTime(iso) {
3682
3843
  }
3683
3844
  function renderAuditLogBody(loading, entries) {
3684
3845
  if (loading) {
3685
- 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 })));
3686
3847
  }
3687
3848
  if (entries.length === 0) {
3688
- 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(
3689
3850
  EmptyState,
3690
3851
  {
3691
3852
  message: "No audit events found",
@@ -3693,15 +3854,15 @@ function renderAuditLogBody(loading, entries) {
3693
3854
  }
3694
3855
  )));
3695
3856
  }
3696
- 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 }));
3697
3858
  }
3698
- 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;
3699
3860
  var init_AuditLog = __esm({
3700
3861
  "src/components/AuditLog.tsx"() {
3701
3862
  "use strict";
3702
- import_react10 = __toESM(require("react"));
3703
- import_Box10 = __toESM(require("@mui/material/Box"));
3704
- 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"));
3705
3866
  import_Table6 = __toESM(require("@mui/material/Table"));
3706
3867
  import_TableBody6 = __toESM(require("@mui/material/TableBody"));
3707
3868
  import_TableCell6 = __toESM(require("@mui/material/TableCell"));
@@ -3715,7 +3876,7 @@ var init_AuditLog = __esm({
3715
3876
  import_Collapse3 = __toESM(require("@mui/material/Collapse"));
3716
3877
  import_IconButton6 = __toESM(require("@mui/material/IconButton"));
3717
3878
  import_Alert5 = __toESM(require("@mui/material/Alert"));
3718
- import_styles7 = require("@mui/material/styles");
3879
+ import_styles8 = require("@mui/material/styles");
3719
3880
  import_icons_material8 = require("@mui/icons-material");
3720
3881
  import_react_use2 = require("react-use");
3721
3882
  init_ui();
@@ -3734,9 +3895,9 @@ var init_AuditLog = __esm({
3734
3895
  LiteLLM_UserTable: "User"
3735
3896
  };
3736
3897
  DetailRow = ({ entry }) => {
3737
- const [open, setOpen] = (0, import_react10.useState)(false);
3898
+ const [open, setOpen] = (0, import_react11.useState)(false);
3738
3899
  const hasDetail = entry.before_value || entry.updated_values;
3739
- 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(
3740
3901
  import_IconButton6.default,
3741
3902
  {
3742
3903
  size: "small",
@@ -3749,9 +3910,9 @@ var init_AuditLog = __esm({
3749
3910
  transition: theme.transitions.create("transform")
3750
3911
  })
3751
3912
  },
3752
- /* @__PURE__ */ import_react10.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
3753
- )), /* @__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(
3754
- 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,
3755
3916
  {
3756
3917
  variant: "body2",
3757
3918
  component: "code",
@@ -3760,8 +3921,8 @@ var init_AuditLog = __esm({
3760
3921
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3761
3922
  },
3762
3923
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
3763
- )), /* @__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(
3764
- 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,
3765
3926
  {
3766
3927
  display: "flex",
3767
3928
  gap: 2,
@@ -3770,11 +3931,11 @@ var init_AuditLog = __esm({
3770
3931
  p: 2,
3771
3932
  mb: 1.5,
3772
3933
  borderRadius: 1.5,
3773
- 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)
3774
3935
  })
3775
3936
  },
3776
- entry.before_value && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react10.default.createElement(
3777
- 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,
3778
3939
  {
3779
3940
  variant: "caption",
3780
3941
  color: "text.secondary",
@@ -3783,9 +3944,9 @@ var init_AuditLog = __esm({
3783
3944
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3784
3945
  },
3785
3946
  "Before"
3786
- ), /* @__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))),
3787
- entry.updated_values && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react10.default.createElement(
3788
- 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,
3789
3950
  {
3790
3951
  variant: "caption",
3791
3952
  color: "text.secondary",
@@ -3794,14 +3955,14 @@ var init_AuditLog = __esm({
3794
3955
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3795
3956
  },
3796
3957
  "After"
3797
- ), /* @__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)))
3798
3959
  )))));
3799
3960
  };
3800
3961
  AuditLog = ({ api }) => {
3801
- const [page, setPage] = (0, import_react10.useState)(0);
3802
- const [pageSize, setPageSize] = (0, import_react10.useState)(25);
3803
- const [filters, setFilters] = (0, import_react10.useState)({});
3804
- 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)(
3805
3966
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3806
3967
  [page, pageSize, filters]
3807
3968
  );
@@ -3811,13 +3972,13 @@ var init_AuditLog = __esm({
3811
3972
  );
3812
3973
  const entries = value?.audit_logs ?? [];
3813
3974
  const total = value?.total ?? 0;
3814
- return /* @__PURE__ */ import_react10.default.createElement(
3975
+ return /* @__PURE__ */ import_react11.default.createElement(
3815
3976
  SectionCard,
3816
3977
  {
3817
3978
  title: "Audit Log",
3818
3979
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3819
3980
  flush: true,
3820
- 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(
3821
3982
  import_TextField6.default,
3822
3983
  {
3823
3984
  size: "small",
@@ -3830,12 +3991,12 @@ var init_AuditLog = __esm({
3830
3991
  },
3831
3992
  sx: { minWidth: 150 }
3832
3993
  },
3833
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
3834
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
3835
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
3836
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
3837
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
3838
- ), /* @__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(
3839
4000
  import_TextField6.default,
3840
4001
  {
3841
4002
  size: "small",
@@ -3848,13 +4009,13 @@ var init_AuditLog = __esm({
3848
4009
  },
3849
4010
  sx: { minWidth: 150 }
3850
4011
  },
3851
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
3852
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
3853
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
3854
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
3855
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3856
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
3857
- ), /* @__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(
3858
4019
  import_TextField6.default,
3859
4020
  {
3860
4021
  size: "small",
@@ -3868,9 +4029,9 @@ var init_AuditLog = __esm({
3868
4029
  }
3869
4030
  ))
3870
4031
  },
3871
- 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)),
3872
- /* @__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)))),
3873
- /* @__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(
3874
4035
  import_TablePagination.default,
3875
4036
  {
3876
4037
  component: "div",
@@ -3939,17 +4100,17 @@ function initDateRange() {
3939
4100
  else start.setDate(start.getDate() - 7);
3940
4101
  return { start, end };
3941
4102
  }
3942
- 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;
3943
4104
  var init_LiteLLMPage = __esm({
3944
4105
  "src/components/LiteLLMPage.tsx"() {
3945
4106
  "use strict";
3946
- import_react11 = __toESM(require("react"));
3947
- import_Box11 = __toESM(require("@mui/material/Box"));
4107
+ import_react12 = __toESM(require("react"));
4108
+ import_Box12 = __toESM(require("@mui/material/Box"));
3948
4109
  import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
3949
4110
  import_Alert6 = __toESM(require("@mui/material/Alert"));
3950
4111
  import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
3951
- import_Typography11 = __toESM(require("@mui/material/Typography"));
3952
- import_Paper7 = __toESM(require("@mui/material/Paper"));
4112
+ import_Typography12 = __toESM(require("@mui/material/Typography"));
4113
+ import_Paper8 = __toESM(require("@mui/material/Paper"));
3953
4114
  import_Tabs2 = __toESM(require("@mui/material/Tabs"));
3954
4115
  import_Tab2 = __toESM(require("@mui/material/Tab"));
3955
4116
  import_Grid2 = __toESM(require("@mui/material/Grid"));
@@ -3973,8 +4134,8 @@ var init_LiteLLMPage = __esm({
3973
4134
  isPageTab = (v) => !!v && PAGE_TABS.includes(v);
3974
4135
  LiteLLMPage = () => {
3975
4136
  const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
3976
- const [dateRange, setDateRange] = (0, import_react11.useState)(initDateRange);
3977
- const [currentPreset, setCurrentPreset] = (0, import_react11.useState)(() => {
4137
+ const [dateRange, setDateRange] = (0, import_react12.useState)(initDateRange);
4138
+ const [currentPreset, setCurrentPreset] = (0, import_react12.useState)(() => {
3978
4139
  try {
3979
4140
  return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
3980
4141
  } catch {
@@ -3982,11 +4143,26 @@ var init_LiteLLMPage = __esm({
3982
4143
  }
3983
4144
  });
3984
4145
  const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
3985
- const [activeTab, setActiveTab] = (0, import_react11.useState)(() => {
4146
+ const [activeTab, setActiveTab] = (0, import_react12.useState)(() => {
3986
4147
  const t = searchParams.get("tab");
3987
4148
  return isPageTab(t) ? t : "overview";
3988
4149
  });
3989
- 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)(
3990
4166
  (tab) => {
3991
4167
  setActiveTab(tab);
3992
4168
  setSearchParams(
@@ -4000,11 +4176,10 @@ var init_LiteLLMPage = __esm({
4000
4176
  },
4001
4177
  [setSearchParams]
4002
4178
  );
4003
- const [snackbar, setSnackbar] = (0, import_react11.useState)(null);
4004
- const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react11.useState)(false);
4005
- const [manageTeam, setManageTeam] = (0, import_react11.useState)(null);
4006
- const [teamUsageCache, setTeamUsageCache] = (0, import_react11.useState)({});
4007
- 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)({});
4008
4183
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
4009
4184
  () => api.getUserInfo(),
4010
4185
  [api]
@@ -4048,7 +4223,7 @@ var init_LiteLLMPage = __esm({
4048
4223
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
4049
4224
  [api, objectPermsEnabled]
4050
4225
  );
4051
- const teams = (0, import_react11.useMemo)(() => {
4226
+ const teams = (0, import_react12.useMemo)(() => {
4052
4227
  if (!allTeams?.length) return [];
4053
4228
  if (!userInfo) return allTeams;
4054
4229
  const userId = userInfo.user_id;
@@ -4065,7 +4240,7 @@ var init_LiteLLMPage = __esm({
4065
4240
  const endDate = dateRange.end.toISOString().split("T")[0];
4066
4241
  return api.getUsage(startDate, endDate);
4067
4242
  }, [api, dateRange]);
4068
- const handleDateRangeChange = (0, import_react11.useCallback)((range, preset) => {
4243
+ const handleDateRangeChange = (0, import_react12.useCallback)((range, preset) => {
4069
4244
  setDateRange(range);
4070
4245
  if (preset) {
4071
4246
  setCurrentPreset(preset);
@@ -4073,7 +4248,7 @@ var init_LiteLLMPage = __esm({
4073
4248
  setTeamUsageCache({});
4074
4249
  setTeamUsageLoading({});
4075
4250
  }, [setDateRange, setCurrentPreset]);
4076
- const loadTeamUsage = (0, import_react11.useCallback)(async (teamId) => {
4251
+ const loadTeamUsage = (0, import_react12.useCallback)(async (teamId) => {
4077
4252
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
4078
4253
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
4079
4254
  try {
@@ -4087,7 +4262,7 @@ var init_LiteLLMPage = __esm({
4087
4262
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
4088
4263
  }
4089
4264
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
4090
- const allowedModels = (0, import_react11.useMemo)(() => {
4265
+ const allowedModels = (0, import_react12.useMemo)(() => {
4091
4266
  if (!allModels?.length) return [];
4092
4267
  const userModels = userInfo?.models;
4093
4268
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -4100,7 +4275,7 @@ var init_LiteLLMPage = __esm({
4100
4275
  ]);
4101
4276
  return allModels.filter((m) => allowed.has(m.model_name));
4102
4277
  }, [allModels, userInfo, teams]);
4103
- const handleGenerateKey = (0, import_react11.useCallback)(
4278
+ const handleGenerateKey = (0, import_react12.useCallback)(
4104
4279
  async (request) => {
4105
4280
  try {
4106
4281
  const response = await api.generateKey(request);
@@ -4114,7 +4289,7 @@ var init_LiteLLMPage = __esm({
4114
4289
  },
4115
4290
  [api, refreshKeys]
4116
4291
  );
4117
- const handleUpdateKey = (0, import_react11.useCallback)(
4292
+ const handleUpdateKey = (0, import_react12.useCallback)(
4118
4293
  async (keyId, request) => {
4119
4294
  try {
4120
4295
  await api.updateKey(keyId, request);
@@ -4127,7 +4302,7 @@ var init_LiteLLMPage = __esm({
4127
4302
  },
4128
4303
  [api, refreshKeys]
4129
4304
  );
4130
- const handleBlockKey = (0, import_react11.useCallback)(
4305
+ const handleBlockKey = (0, import_react12.useCallback)(
4131
4306
  async (keyId) => {
4132
4307
  try {
4133
4308
  await api.blockKey(keyId);
@@ -4139,7 +4314,7 @@ var init_LiteLLMPage = __esm({
4139
4314
  },
4140
4315
  [api, refreshKeys]
4141
4316
  );
4142
- const handleUnblockKey = (0, import_react11.useCallback)(
4317
+ const handleUnblockKey = (0, import_react12.useCallback)(
4143
4318
  async (keyId) => {
4144
4319
  try {
4145
4320
  await api.unblockKey(keyId);
@@ -4151,7 +4326,7 @@ var init_LiteLLMPage = __esm({
4151
4326
  },
4152
4327
  [api, refreshKeys]
4153
4328
  );
4154
- const handleResetKeySpend = (0, import_react11.useCallback)(
4329
+ const handleResetKeySpend = (0, import_react12.useCallback)(
4155
4330
  async (keyId) => {
4156
4331
  try {
4157
4332
  await api.resetKeySpend(keyId);
@@ -4163,7 +4338,7 @@ var init_LiteLLMPage = __esm({
4163
4338
  },
4164
4339
  [api, refreshKeys]
4165
4340
  );
4166
- const handleDeleteKey = (0, import_react11.useCallback)(
4341
+ const handleDeleteKey = (0, import_react12.useCallback)(
4167
4342
  async (keyId) => {
4168
4343
  try {
4169
4344
  await api.deleteKey(keyId);
@@ -4180,7 +4355,7 @@ var init_LiteLLMPage = __esm({
4180
4355
  },
4181
4356
  [api, refreshKeys]
4182
4357
  );
4183
- const handlePruneExpiredKeys = (0, import_react11.useCallback)(
4358
+ const handlePruneExpiredKeys = (0, import_react12.useCallback)(
4184
4359
  async () => {
4185
4360
  try {
4186
4361
  const result = await api.pruneExpiredKeys();
@@ -4195,14 +4370,14 @@ var init_LiteLLMPage = __esm({
4195
4370
  );
4196
4371
  const isInitialLoading = userLoading && !userInfo;
4197
4372
  if (isInitialLoading) {
4198
- 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));
4199
4374
  }
4200
4375
  if (userError || !userInfo) {
4201
4376
  const isProvisioningEnabled = userError?.body?.provisioning === true;
4202
4377
  const hint = userError?.body?.hint;
4203
- 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.")));
4204
4379
  }
4205
- const pageTabs = /* @__PURE__ */ import_react11.default.createElement(
4380
+ const pageTabs = /* @__PURE__ */ import_react12.default.createElement(
4206
4381
  import_Tabs2.default,
4207
4382
  {
4208
4383
  value: activeTab,
@@ -4215,13 +4390,13 @@ var init_LiteLLMPage = __esm({
4215
4390
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
4216
4391
  }
4217
4392
  },
4218
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
4219
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
4220
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
4221
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
4222
- 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" })
4223
4398
  );
4224
- 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(
4225
4400
  DashboardHeader,
4226
4401
  {
4227
4402
  userInfo,
@@ -4231,7 +4406,7 @@ var init_LiteLLMPage = __esm({
4231
4406
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
4232
4407
  tabs: pageTabs
4233
4408
  }
4234
- ), 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(
4235
4410
  UsageStats,
4236
4411
  {
4237
4412
  usage: usage ?? null,
@@ -4242,7 +4417,7 @@ var init_LiteLLMPage = __esm({
4242
4417
  loading: usageLoading,
4243
4418
  userInfo
4244
4419
  }
4245
- )), /* @__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(
4246
4421
  KeysTable,
4247
4422
  {
4248
4423
  keys: keys ?? [],
@@ -4261,7 +4436,7 @@ var init_LiteLLMPage = __esm({
4261
4436
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
4262
4437
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
4263
4438
  const visibleTeams = Array.from(teamsById.values());
4264
- return /* @__PURE__ */ import_react11.default.createElement(
4439
+ return /* @__PURE__ */ import_react12.default.createElement(
4265
4440
  TeamUsage,
4266
4441
  {
4267
4442
  teams: visibleTeams,
@@ -4277,14 +4452,14 @@ var init_LiteLLMPage = __esm({
4277
4452
  onCreateTeam: () => setManageTeam({ mode: "create" })
4278
4453
  }
4279
4454
  );
4280
- })(), activeTab === "models" && /* @__PURE__ */ import_react11.default.createElement(
4455
+ })(), activeTab === "models" && /* @__PURE__ */ import_react12.default.createElement(
4281
4456
  ModelsTable,
4282
4457
  {
4283
4458
  allModels: allModels ?? [],
4284
4459
  teams: teams ?? [],
4285
4460
  loading: modelsLoading
4286
4461
  }
4287
- ), 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(
4288
4463
  GenerateKeyDialog,
4289
4464
  {
4290
4465
  open: generateDialogOpen,
@@ -4297,7 +4472,7 @@ var init_LiteLLMPage = __esm({
4297
4472
  onGenerateKey: handleGenerateKey,
4298
4473
  onGetConfig: () => api.getConfig()
4299
4474
  }
4300
- ), /* @__PURE__ */ import_react11.default.createElement(
4475
+ ), /* @__PURE__ */ import_react12.default.createElement(
4301
4476
  ManageTeamDialog,
4302
4477
  {
4303
4478
  open: !!manageTeam,
@@ -4359,7 +4534,7 @@ var init_LiteLLMPage = __esm({
4359
4534
  refreshTeams();
4360
4535
  }
4361
4536
  }
4362
- ), /* @__PURE__ */ import_react11.default.createElement(
4537
+ ), /* @__PURE__ */ import_react12.default.createElement(
4363
4538
  import_Snackbar2.default,
4364
4539
  {
4365
4540
  open: !!snackbar,
@@ -4367,7 +4542,7 @@ var init_LiteLLMPage = __esm({
4367
4542
  onClose: () => setSnackbar(null),
4368
4543
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
4369
4544
  },
4370
- 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
4371
4546
  ));
4372
4547
  };
4373
4548
  }
@@ -4379,6 +4554,7 @@ __export(index_exports, {
4379
4554
  DashboardHeader: () => DashboardHeader,
4380
4555
  GenerateKeyDialog: () => GenerateKeyDialog,
4381
4556
  KeysTable: () => KeysTable,
4557
+ LiteLLMBudgetGauges: () => LiteLLMBudgetGauges,
4382
4558
  LiteLLMBudgetWidget: () => LiteLLMBudgetWidget,
4383
4559
  LiteLLMHomeWidget: () => LiteLLMHomeWidget,
4384
4560
  LiteLLMPage: () => LiteLLMPage,
@@ -4397,7 +4573,7 @@ __export(index_exports, {
4397
4573
  module.exports = __toCommonJS(index_exports);
4398
4574
 
4399
4575
  // src/plugin.tsx
4400
- var import_react12 = __toESM(require("react"));
4576
+ var import_react13 = __toESM(require("react"));
4401
4577
  var import_icons_material9 = require("@mui/icons-material");
4402
4578
  var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
4403
4579
  init_api();
@@ -4412,10 +4588,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
4412
4588
  params: {
4413
4589
  path: "/litellm",
4414
4590
  title: "LiteLLM",
4415
- icon: /* @__PURE__ */ import_react12.default.createElement(import_icons_material9.TrendingUp, null),
4591
+ icon: /* @__PURE__ */ import_react13.default.createElement(import_icons_material9.TrendingUp, null),
4416
4592
  loader: async () => {
4417
4593
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
4418
- return /* @__PURE__ */ import_react12.default.createElement(LiteLLMPage2, null);
4594
+ return /* @__PURE__ */ import_react13.default.createElement(LiteLLMPage2, null);
4419
4595
  }
4420
4596
  }
4421
4597
  });
@@ -4432,10 +4608,10 @@ init_UsageStats();
4432
4608
  init_TeamUsage();
4433
4609
 
4434
4610
  // src/components/LiteLLMHomeWidget.tsx
4435
- var import_react13 = __toESM(require("react"));
4436
- var import_Paper8 = __toESM(require("@mui/material/Paper"));
4437
- var import_Box12 = __toESM(require("@mui/material/Box"));
4438
- 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"));
4439
4615
  var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
4440
4616
  var import_Select2 = __toESM(require("@mui/material/Select"));
4441
4617
  var import_MenuItem6 = __toESM(require("@mui/material/MenuItem"));
@@ -4458,19 +4634,19 @@ function presetToDateRange(preset) {
4458
4634
  }
4459
4635
  return { start, end };
4460
4636
  }
4461
- 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));
4462
4638
  var LiteLLMHomeWidget = ({
4463
4639
  defaultPeriod = "7d",
4464
4640
  title = "LiteLLM Usage"
4465
4641
  }) => {
4466
4642
  const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
4467
- const [period, setPeriod] = (0, import_react13.useState)(defaultPeriod);
4468
- const [loading, setLoading] = (0, import_react13.useState)(true);
4469
- const [usageError, setUsageError] = (0, import_react13.useState)(null);
4470
- const [keysError, setKeysError] = (0, import_react13.useState)(null);
4471
- const [usage, setUsage] = (0, import_react13.useState)(null);
4472
- const [keys, setKeys] = (0, import_react13.useState)([]);
4473
- (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)(() => {
4474
4650
  let cancelled = false;
4475
4651
  setLoading(true);
4476
4652
  setUsageError(null);
@@ -4507,17 +4683,17 @@ var LiteLLMHomeWidget = ({
4507
4683
  spend: d.spend
4508
4684
  }));
4509
4685
  const hasSparkline = dailyData.length > 0;
4510
- 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(
4511
4687
  import_Select2.default,
4512
4688
  {
4513
4689
  value: period,
4514
4690
  onChange: (e) => setPeriod(e.target.value),
4515
4691
  displayEmpty: true
4516
4692
  },
4517
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
4518
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
4519
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
4520
- ))), 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(
4521
4697
  import_recharts3.Area,
4522
4698
  {
4523
4699
  type: "monotone",
@@ -4533,6 +4709,307 @@ var LiteLLMHomeWidget = ({
4533
4709
 
4534
4710
  // src/index.ts
4535
4711
  init_LiteLLMBudgetWidget();
4712
+
4713
+ // src/components/LiteLLMBudgetGauges.tsx
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"));
4719
+ var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress"));
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");
4723
+ var import_core_plugin_api6 = require("@backstage/core-plugin-api");
4724
+ var import_core_components2 = require("@backstage/core-components");
4725
+ init_api();
4726
+ init_format();
4727
+ init_ui();
4728
+ init_BudgetLimitList();
4729
+ init_budget();
4730
+ var MODULE_PATH = "/litellm";
4731
+ var LEVEL = {
4732
+ key: { name: "Key", none: "No key budget" },
4733
+ user: { name: "User", none: "No personal budget" },
4734
+ team: { name: "Team", none: "No team budget" }
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
+ };
4742
+ function fmtUsdShort(n) {
4743
+ const v = n ?? 0;
4744
+ if (v >= 100 && Number.isInteger(v)) return `$${v}`;
4745
+ return fmtUsd(v);
4746
+ }
4747
+ function spendCaption(limit) {
4748
+ if (limit.hidden) return "hidden by admin";
4749
+ return `${fmtUsdShort(limit.spend)} / ${fmtUsdShort(limit.budget)}`;
4750
+ }
4751
+ function resetLabel(limit) {
4752
+ const window2 = fmtBudgetDuration(limit.budgetDuration);
4753
+ return window2 ? `resets ${window2}` : "never resets";
4754
+ }
4755
+ var LevelGauge = ({
4756
+ gauge,
4757
+ size,
4758
+ keysHref
4759
+ }) => {
4760
+ const { name, none } = LEVEL[gauge.kind];
4761
+ const limit = gauge.limit;
4762
+ const tone = limit ? budgetTone2(limit.pct) : "neutral";
4763
+ const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
4764
+ return /* @__PURE__ */ import_react15.default.createElement(
4765
+ import_Box14.default,
4766
+ {
4767
+ sx: {
4768
+ display: "flex",
4769
+ flexDirection: "column",
4770
+ alignItems: "center",
4771
+ minWidth: 0,
4772
+ flex: 1
4773
+ }
4774
+ },
4775
+ /* @__PURE__ */ import_react15.default.createElement(
4776
+ Gauge,
4777
+ {
4778
+ value: limit?.pct ?? 0,
4779
+ tone,
4780
+ size,
4781
+ label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
4782
+ }
4783
+ ),
4784
+ /* @__PURE__ */ import_react15.default.createElement(
4785
+ import_Typography14.default,
4786
+ {
4787
+ sx: (theme) => ({
4788
+ mt: 0.75,
4789
+ fontSize: 10.5,
4790
+ fontWeight: 700,
4791
+ letterSpacing: "0.08em",
4792
+ textTransform: "uppercase",
4793
+ color: tone === "neutral" ? theme.palette.text.secondary : void 0
4794
+ })
4795
+ },
4796
+ name
4797
+ ),
4798
+ limit ? /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
4799
+ import_Typography14.default,
4800
+ {
4801
+ variant: "caption",
4802
+ title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
4803
+ sx: {
4804
+ mt: 0.25,
4805
+ fontSize: 11,
4806
+ fontWeight: 600,
4807
+ maxWidth: "100%",
4808
+ overflow: "hidden",
4809
+ textOverflow: "ellipsis",
4810
+ whiteSpace: "nowrap"
4811
+ }
4812
+ },
4813
+ limit.label
4814
+ ), /* @__PURE__ */ import_react15.default.createElement(
4815
+ import_Typography14.default,
4816
+ {
4817
+ variant: "caption",
4818
+ color: "text.secondary",
4819
+ sx: {
4820
+ fontSize: 10.5,
4821
+ fontVariantNumeric: "tabular-nums",
4822
+ textAlign: "center"
4823
+ }
4824
+ },
4825
+ spendCaption(limit)
4826
+ ), extra > 0 ? /* @__PURE__ */ import_react15.default.createElement(
4827
+ import_Typography14.default,
4828
+ {
4829
+ component: import_core_components2.Link,
4830
+ to: keysHref,
4831
+ variant: "caption",
4832
+ sx: {
4833
+ fontSize: 10.5,
4834
+ color: "primary.main",
4835
+ textDecoration: "none",
4836
+ "&:hover": { textDecoration: "underline" }
4837
+ }
4838
+ },
4839
+ "+",
4840
+ extra,
4841
+ " more"
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,
4844
+ {
4845
+ variant: "caption",
4846
+ color: "text.secondary",
4847
+ sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
4848
+ },
4849
+ none
4850
+ )
4851
+ );
4852
+ };
4853
+ var LiteLLMBudgetGauges = ({
4854
+ title = "Budget",
4855
+ size = 72,
4856
+ keysHref,
4857
+ moduleHref = MODULE_PATH,
4858
+ onCreateKey,
4859
+ ctas,
4860
+ maxExpandedKeys = 8,
4861
+ expanded,
4862
+ defaultExpanded = false,
4863
+ onExpandedChange,
4864
+ action
4865
+ }) => {
4866
+ const api = (0, import_core_plugin_api6.useApi)(liteLlmApiRef);
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)(() => {
4882
+ let cancelled = false;
4883
+ setLoading(true);
4884
+ setError(null);
4885
+ Promise.allSettled([api.getUserInfo(), api.getTeams(), api.listKeys()]).then(
4886
+ ([userResult, teamsResult, keysResult]) => {
4887
+ if (cancelled) return;
4888
+ if (userResult.status === "fulfilled") {
4889
+ setUser(userResult.value);
4890
+ } else {
4891
+ setUser(null);
4892
+ setError(
4893
+ userResult.reason?.message ?? "Failed to load your LiteLLM profile"
4894
+ );
4895
+ }
4896
+ setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
4897
+ setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
4898
+ setLoading(false);
4899
+ }
4900
+ );
4901
+ return () => {
4902
+ cancelled = true;
4903
+ };
4904
+ }, [api]);
4905
+ const summary = (0, import_react15.useMemo)(
4906
+ () => buildBudgetSummary(user, teams, keys, keys.length),
4907
+ [user, teams, keys]
4908
+ );
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
+ );
4919
+ const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
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(
4969
+ StatusPill,
4970
+ {
4971
+ label: summaryText,
4972
+ tone: budgetTone2(headline.closest?.pct ?? 0),
4973
+ dot: headline.count > 0
4974
+ }
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,
4996
+ {
4997
+ sx: (theme) => ({
4998
+ mt: 2,
4999
+ pt: 2,
5000
+ borderTop: `1px solid ${theme.palette.divider}`,
5001
+ display: "flex",
5002
+ alignItems: "center",
5003
+ gap: 1,
5004
+ flexWrap: "wrap"
5005
+ })
5006
+ },
5007
+ resolvedCtas.map(renderCta),
5008
+ action
5009
+ ));
5010
+ };
5011
+
5012
+ // src/index.ts
4536
5013
  init_GenerateKeyDialog();
4537
5014
  init_ManageTeamDialog();
4538
5015
  init_api();