@acarmisc/backstage-plugin-litellm 0.25.0 → 0.27.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
@@ -858,6 +858,66 @@ var init_ui = __esm({
858
858
  }
859
859
  });
860
860
 
861
+ // src/components/GenerateKeyButton.tsx
862
+ var import_react2, import_Button, import_icons_material, import_core_components, GenerateKeyButton;
863
+ var init_GenerateKeyButton = __esm({
864
+ "src/components/GenerateKeyButton.tsx"() {
865
+ "use strict";
866
+ import_react2 = __toESM(require("react"));
867
+ import_Button = __toESM(require("@mui/material/Button"));
868
+ import_icons_material = require("@mui/icons-material");
869
+ import_core_components = require("@backstage/core-components");
870
+ GenerateKeyButton = ({
871
+ to,
872
+ onClick,
873
+ label = "Generate New Key",
874
+ size
875
+ }) => {
876
+ if (onClick) {
877
+ return /* @__PURE__ */ import_react2.default.createElement(
878
+ import_Button.default,
879
+ {
880
+ variant: "contained",
881
+ color: "primary",
882
+ disableElevation: true,
883
+ size,
884
+ startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
885
+ onClick
886
+ },
887
+ label
888
+ );
889
+ }
890
+ if (to) {
891
+ return /* @__PURE__ */ import_react2.default.createElement(
892
+ import_Button.default,
893
+ {
894
+ variant: "contained",
895
+ color: "primary",
896
+ disableElevation: true,
897
+ size,
898
+ startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
899
+ component: import_core_components.Link,
900
+ to
901
+ },
902
+ label
903
+ );
904
+ }
905
+ return /* @__PURE__ */ import_react2.default.createElement(
906
+ import_Button.default,
907
+ {
908
+ variant: "contained",
909
+ color: "primary",
910
+ disableElevation: true,
911
+ size,
912
+ startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
913
+ disabled: true
914
+ },
915
+ label
916
+ );
917
+ };
918
+ }
919
+ });
920
+
861
921
  // src/components/DashboardHeader.tsx
862
922
  function initials(name) {
863
923
  const local = name.split("@")[0] ?? name;
@@ -865,24 +925,23 @@ function initials(name) {
865
925
  if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
866
926
  return local.slice(0, 2).toUpperCase();
867
927
  }
868
- var import_react2, import_Box2, import_Typography2, import_Skeleton, import_Paper2, import_Button, import_Divider, import_styles2, import_icons_material, CountStat, DashboardHeader;
928
+ var import_react3, import_Box2, import_Typography2, import_Skeleton, import_Paper2, import_Divider, import_styles2, CountStat, DashboardHeader;
869
929
  var init_DashboardHeader = __esm({
870
930
  "src/components/DashboardHeader.tsx"() {
871
931
  "use strict";
872
- import_react2 = __toESM(require("react"));
932
+ import_react3 = __toESM(require("react"));
873
933
  import_Box2 = __toESM(require("@mui/material/Box"));
874
934
  import_Typography2 = __toESM(require("@mui/material/Typography"));
875
935
  import_Skeleton = __toESM(require("@mui/material/Skeleton"));
876
936
  import_Paper2 = __toESM(require("@mui/material/Paper"));
877
- import_Button = __toESM(require("@mui/material/Button"));
878
937
  import_Divider = __toESM(require("@mui/material/Divider"));
879
938
  import_styles2 = require("@mui/material/styles");
880
- import_icons_material = require("@mui/icons-material");
881
939
  init_api();
882
940
  init_ui();
941
+ init_GenerateKeyButton();
883
942
  CountStat = ({ value, label, tone }) => {
884
943
  const active = value > 0;
885
- return /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ import_react2.default.createElement(
944
+ return /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ import_react3.default.createElement(
886
945
  import_Box2.default,
887
946
  {
888
947
  sx: (theme) => ({
@@ -893,14 +952,14 @@ var init_DashboardHeader = __esm({
893
952
  bgcolor: active ? tone === "danger" && theme.palette.error.main || tone === "warning" && theme.palette.warning.main || theme.palette.primary.main : (0, import_styles2.alpha)(theme.palette.text.primary, 0.25)
894
953
  })
895
954
  }
896
- ), /* @__PURE__ */ import_react2.default.createElement(
955
+ ), /* @__PURE__ */ import_react3.default.createElement(
897
956
  import_Typography2.default,
898
957
  {
899
958
  component: "span",
900
959
  sx: { fontSize: 15, fontWeight: 700, lineHeight: 1, fontVariantNumeric: "tabular-nums" }
901
960
  },
902
961
  value
903
- ), /* @__PURE__ */ import_react2.default.createElement(
962
+ ), /* @__PURE__ */ import_react3.default.createElement(
904
963
  import_Typography2.default,
905
964
  {
906
965
  component: "span",
@@ -918,13 +977,13 @@ var init_DashboardHeader = __esm({
918
977
  onGenerateKeyClick,
919
978
  tabs
920
979
  }) => {
921
- const counts = (0, import_react2.useMemo)(() => {
980
+ const counts = (0, import_react3.useMemo)(() => {
922
981
  const expired = keys.filter((k) => expiryStatus(k.expires_at) === "expired").length;
923
982
  const expiringSoon = keys.filter((k) => expiryStatus(k.expires_at) === "soon").length;
924
983
  return { total: keys.length, expired, expiringSoon };
925
984
  }, [keys]);
926
985
  const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;
927
- return /* @__PURE__ */ import_react2.default.createElement(import_Paper2.default, { sx: { borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ import_react2.default.createElement(
986
+ return /* @__PURE__ */ import_react3.default.createElement(import_Paper2.default, { sx: { borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ import_react3.default.createElement(
928
987
  import_Box2.default,
929
988
  {
930
989
  sx: {
@@ -937,7 +996,7 @@ var init_DashboardHeader = __esm({
937
996
  pb: 2
938
997
  }
939
998
  },
940
- /* @__PURE__ */ import_react2.default.createElement(
999
+ /* @__PURE__ */ import_react3.default.createElement(
941
1000
  import_Box2.default,
942
1001
  {
943
1002
  sx: (theme) => ({
@@ -957,7 +1016,7 @@ var init_DashboardHeader = __esm({
957
1016
  },
958
1017
  initials(displayName)
959
1018
  ),
960
- /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { sx: { flexGrow: 1, minWidth: 220 } }, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "h6", sx: { lineHeight: 1.25, wordBreak: "break-word" } }, displayName), /* @__PURE__ */ import_react2.default.createElement(
1019
+ /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { flexGrow: 1, minWidth: 220 } }, /* @__PURE__ */ import_react3.default.createElement(import_Typography2.default, { variant: "h6", sx: { lineHeight: 1.25, wordBreak: "break-word" } }, displayName), /* @__PURE__ */ import_react3.default.createElement(
961
1020
  import_Typography2.default,
962
1021
  {
963
1022
  variant: "caption",
@@ -965,7 +1024,7 @@ var init_DashboardHeader = __esm({
965
1024
  sx: { display: "block", fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
966
1025
  },
967
1026
  userInfo.user_id
968
- ), /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { sx: { display: "flex", gap: 0.75, flexWrap: "wrap", mt: 1, minHeight: 20 } }, loading ? /* @__PURE__ */ import_react2.default.createElement(import_Skeleton.default, { variant: "rounded", width: 140, height: 20 }) : teams.map((team) => /* @__PURE__ */ import_react2.default.createElement(
1027
+ ), /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { display: "flex", gap: 0.75, flexWrap: "wrap", mt: 1, minHeight: 20 } }, loading ? /* @__PURE__ */ import_react3.default.createElement(import_Skeleton.default, { variant: "rounded", width: 140, height: 20 }) : teams.map((team) => /* @__PURE__ */ import_react3.default.createElement(
969
1028
  TagChip,
970
1029
  {
971
1030
  key: team.team_id,
@@ -973,18 +1032,8 @@ var init_DashboardHeader = __esm({
973
1032
  title: team.team_id
974
1033
  }
975
1034
  )))),
976
- /* @__PURE__ */ import_react2.default.createElement(
977
- import_Button.default,
978
- {
979
- variant: "contained",
980
- color: "primary",
981
- disableElevation: true,
982
- startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
983
- onClick: onGenerateKeyClick
984
- },
985
- "Generate New Key"
986
- )
987
- ), /* @__PURE__ */ import_react2.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react2.default.createElement(
1035
+ /* @__PURE__ */ import_react3.default.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick })
1036
+ ), /* @__PURE__ */ import_react3.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react3.default.createElement(
988
1037
  import_Box2.default,
989
1038
  {
990
1039
  sx: {
@@ -996,12 +1045,12 @@ var init_DashboardHeader = __esm({
996
1045
  py: 1.5
997
1046
  }
998
1047
  },
999
- /* @__PURE__ */ import_react2.default.createElement(CountStat, { value: counts.total, label: "keys", tone: "accent" }),
1000
- /* @__PURE__ */ import_react2.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
1001
- /* @__PURE__ */ import_react2.default.createElement(CountStat, { value: counts.expired, label: "expired", tone: "danger" }),
1002
- /* @__PURE__ */ import_react2.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
1003
- /* @__PURE__ */ import_react2.default.createElement(CountStat, { value: counts.expiringSoon, label: "expiring soon", tone: "warning" })
1004
- ), tabs && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { sx: { px: 1 } }, tabs)));
1048
+ /* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.total, label: "keys", tone: "accent" }),
1049
+ /* @__PURE__ */ import_react3.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
1050
+ /* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.expired, label: "expired", tone: "danger" }),
1051
+ /* @__PURE__ */ import_react3.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
1052
+ /* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.expiringSoon, label: "expiring soon", tone: "warning" })
1053
+ ), tabs && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { px: 1 } }, tabs)));
1005
1054
  };
1006
1055
  }
1007
1056
  });
@@ -1020,12 +1069,12 @@ function expiryTone(status) {
1020
1069
  function ExpiryCell({ expiresAt }) {
1021
1070
  const status = expiryStatus(expiresAt);
1022
1071
  if (!status) {
1023
- return /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, "Never");
1072
+ return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, "Never");
1024
1073
  }
1025
1074
  if (status === "ok") {
1026
- return /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, formatDate(expiresAt));
1075
+ return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, formatDate(expiresAt));
1027
1076
  }
1028
- return /* @__PURE__ */ import_react3.default.createElement(
1077
+ return /* @__PURE__ */ import_react4.default.createElement(
1029
1078
  StatusPill,
1030
1079
  {
1031
1080
  label: expiryChipLabel(status, expiresAt),
@@ -1040,36 +1089,16 @@ function budgetTone(pct) {
1040
1089
  return "accent";
1041
1090
  }
1042
1091
  function keyBlockIcon(isBlocking, blocked) {
1043
- if (isBlocking) return /* @__PURE__ */ import_react3.default.createElement(import_CircularProgress.default, { size: 18 });
1044
- if (blocked) return /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.LockOpen, { fontSize: "small" });
1045
- return /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Lock, { fontSize: "small" });
1092
+ if (isBlocking) return /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 18 });
1093
+ if (blocked) return /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.LockOpen, { fontSize: "small" });
1094
+ return /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Lock, { fontSize: "small" });
1046
1095
  }
1047
1096
  function BudgetCell({ spend, maxBudget }) {
1048
1097
  if (!maxBudget) {
1049
- return /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, "$", spend.toFixed(2), /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { component: "span", variant: "caption", sx: { ml: 0.5, opacity: 0.7 } }, "/ \u221E"));
1098
+ return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, "$", spend.toFixed(2), /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { component: "span", variant: "caption", sx: { ml: 0.5, opacity: 0.7 } }, "/ \u221E"));
1050
1099
  }
1051
1100
  const pct = Math.min(100, spend / maxBudget * 100);
1052
- return /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { minWidth: 104 }, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums", mb: 0.5 } }, "$", spend.toFixed(2), /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 0.5 } }, "/ $", maxBudget)), /* @__PURE__ */ import_react3.default.createElement(Meter, { value: pct, tone: budgetTone(pct), height: 4 }));
1053
- }
1054
- function fmtCost(perToken) {
1055
- if (!perToken) return null;
1056
- const per1k = perToken * 1e3;
1057
- return per1k < 0.01 ? `$${(perToken * 1e6).toFixed(2)}/M` : `$${per1k.toFixed(3)}/1K`;
1058
- }
1059
- function formatContextWindow(maxInput, maxOutput) {
1060
- if (!maxInput && !maxOutput) return null;
1061
- const fmt = (n) => {
1062
- if (!n) return null;
1063
- if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
1064
- if (n >= 1e3) return `${Math.round(n / 1e3)}K`;
1065
- return String(n);
1066
- };
1067
- const inPart = fmt(maxInput);
1068
- const outPart = fmt(maxOutput);
1069
- if (inPart && outPart) return `ctx ${inPart} in / ${outPart} out`;
1070
- if (inPart) return `ctx ${inPart}`;
1071
- if (outPart) return `ctx ${outPart} out`;
1072
- return null;
1101
+ return /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { minWidth: 104 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums", mb: 0.5 } }, "$", spend.toFixed(2), /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 0.5 } }, "/ $", maxBudget)), /* @__PURE__ */ import_react4.default.createElement(Meter, { value: pct, tone: budgetTone(pct), height: 4 }));
1073
1102
  }
1074
1103
  function compareKeys(a, b, sortKey) {
1075
1104
  switch (sortKey) {
@@ -1089,11 +1118,11 @@ function compareKeys(a, b, sortKey) {
1089
1118
  return 0;
1090
1119
  }
1091
1120
  }
1092
- var import_react3, import_Table, import_TableBody, import_TableCell, import_TableContainer, import_TableHead, import_TableRow, import_TableSortLabel, import_Button2, import_IconButton, import_Box3, import_Typography3, import_Dialog, import_DialogTitle, import_DialogContent, import_DialogActions, import_TextField, import_CircularProgress, import_Autocomplete, import_Skeleton2, import_InputAdornment, import_styles3, import_icons_material2, maskKey, shortKeyId, formatDate, keyToEditForm, KeysTable;
1121
+ var import_react4, import_Table, import_TableBody, import_TableCell, import_TableContainer, import_TableHead, import_TableRow, import_TableSortLabel, import_Button2, import_IconButton, import_Box3, import_Typography3, import_Dialog, import_DialogTitle, import_DialogContent, import_DialogActions, import_TextField, import_CircularProgress, import_Skeleton2, import_InputAdornment, import_styles3, import_icons_material2, shortKeyId, formatDate, KeysTable;
1093
1122
  var init_KeysTable = __esm({
1094
1123
  "src/components/KeysTable.tsx"() {
1095
1124
  "use strict";
1096
- import_react3 = __toESM(require("react"));
1125
+ import_react4 = __toESM(require("react"));
1097
1126
  import_Table = __toESM(require("@mui/material/Table"));
1098
1127
  import_TableBody = __toESM(require("@mui/material/TableBody"));
1099
1128
  import_TableCell = __toESM(require("@mui/material/TableCell"));
@@ -1111,17 +1140,13 @@ var init_KeysTable = __esm({
1111
1140
  import_DialogActions = __toESM(require("@mui/material/DialogActions"));
1112
1141
  import_TextField = __toESM(require("@mui/material/TextField"));
1113
1142
  import_CircularProgress = __toESM(require("@mui/material/CircularProgress"));
1114
- import_Autocomplete = __toESM(require("@mui/material/Autocomplete"));
1115
1143
  import_Skeleton2 = __toESM(require("@mui/material/Skeleton"));
1116
1144
  import_InputAdornment = __toESM(require("@mui/material/InputAdornment"));
1117
1145
  import_styles3 = require("@mui/material/styles");
1118
1146
  import_icons_material2 = require("@mui/icons-material");
1119
1147
  init_api();
1148
+ init_GenerateKeyButton();
1120
1149
  init_ui();
1121
- maskKey = (key) => {
1122
- if (key.length <= 8) return "***";
1123
- return `${key.slice(0, 4)}...${key.slice(-4)}`;
1124
- };
1125
1150
  shortKeyId = (token) => {
1126
1151
  if (!token) return "-";
1127
1152
  if (token.length <= 16) return token;
@@ -1134,36 +1159,22 @@ var init_KeysTable = __esm({
1134
1159
  return dateStr;
1135
1160
  }
1136
1161
  };
1137
- keyToEditForm = (k) => ({
1138
- key_alias: k.key_alias ?? "",
1139
- models: k.models ?? [],
1140
- max_budget: k.max_budget,
1141
- tpm_limit: k.tpm_limit,
1142
- rpm_limit: k.rpm_limit
1143
- });
1144
1162
  KeysTable = ({
1145
1163
  keys,
1146
- models,
1147
1164
  loading,
1148
1165
  onGenerateKeyClick,
1149
- onUpdateKey,
1166
+ onEditKey,
1150
1167
  onBlockKey,
1151
1168
  onUnblockKey,
1152
- onResetKeySpend,
1153
1169
  onDeleteKey,
1154
1170
  onPruneExpiredKeys
1155
1171
  }) => {
1156
- const [editingKey, setEditingKey] = (0, import_react3.useState)(null);
1157
- const [editForm, setEditForm] = (0, import_react3.useState)({});
1158
- const [editSubmitting, setEditSubmitting] = (0, import_react3.useState)(false);
1159
- const [blockingKeyId, setBlockingKeyId] = (0, import_react3.useState)(null);
1160
- const [deleteConfirmId, setDeleteConfirmId] = (0, import_react3.useState)(null);
1161
- const [deleteSubmitting, setDeleteSubmitting] = (0, import_react3.useState)(false);
1162
- const [resetSpendConfirm, setResetSpendConfirm] = (0, import_react3.useState)(false);
1163
- const [resetSpendSubmitting, setResetSpendSubmitting] = (0, import_react3.useState)(false);
1164
- const [filterText, setFilterText] = (0, import_react3.useState)("");
1165
- const [sortKey, setSortKey] = (0, import_react3.useState)("created");
1166
- const [sortDirection, setSortDirection] = (0, import_react3.useState)("desc");
1172
+ const [blockingKeyId, setBlockingKeyId] = (0, import_react4.useState)(null);
1173
+ const [deleteConfirmId, setDeleteConfirmId] = (0, import_react4.useState)(null);
1174
+ const [deleteSubmitting, setDeleteSubmitting] = (0, import_react4.useState)(false);
1175
+ const [filterText, setFilterText] = (0, import_react4.useState)("");
1176
+ const [sortKey, setSortKey] = (0, import_react4.useState)("created");
1177
+ const [sortDirection, setSortDirection] = (0, import_react4.useState)("desc");
1167
1178
  const handleSort = (nextSortKey) => {
1168
1179
  if (sortKey === nextSortKey) {
1169
1180
  setSortDirection((current) => current === "asc" ? "desc" : "asc");
@@ -1172,7 +1183,7 @@ var init_KeysTable = __esm({
1172
1183
  setSortDirection("asc");
1173
1184
  }
1174
1185
  };
1175
- const filteredKeys = (0, import_react3.useMemo)(() => {
1186
+ const filteredKeys = (0, import_react4.useMemo)(() => {
1176
1187
  let result = keys;
1177
1188
  if (filterText.trim()) {
1178
1189
  const q = filterText.toLowerCase();
@@ -1185,27 +1196,8 @@ var init_KeysTable = __esm({
1185
1196
  return sortDirection === "asc" ? comparison : -comparison;
1186
1197
  });
1187
1198
  }, [keys, filterText, sortKey, sortDirection]);
1188
- const editSelectedModels = models.filter((m) => (editForm.models || []).includes(m.model_name));
1189
1199
  const handleOpenEdit = (k) => {
1190
- setEditingKey(k);
1191
- setEditForm(keyToEditForm(k));
1192
- };
1193
- const handleCloseEdit = () => {
1194
- setEditingKey(null);
1195
- setEditForm({});
1196
- setResetSpendConfirm(false);
1197
- };
1198
- const handleUpdate = async () => {
1199
- if (!editingKey) return;
1200
- setEditSubmitting(true);
1201
- try {
1202
- await onUpdateKey(editingKey.token ?? editingKey.key, editForm);
1203
- handleCloseEdit();
1204
- } catch (error) {
1205
- console.error("Failed to update key:", error);
1206
- } finally {
1207
- setEditSubmitting(false);
1208
- }
1200
+ onEditKey(k);
1209
1201
  };
1210
1202
  const handleToggleBlock = async (key) => {
1211
1203
  const keyId = key.token ?? key.key;
@@ -1220,17 +1212,6 @@ var init_KeysTable = __esm({
1220
1212
  setBlockingKeyId(null);
1221
1213
  }
1222
1214
  };
1223
- const handleResetSpend = async () => {
1224
- if (!editingKey) return;
1225
- setResetSpendSubmitting(true);
1226
- try {
1227
- await onResetKeySpend(editingKey.token ?? editingKey.key);
1228
- setResetSpendConfirm(false);
1229
- handleCloseEdit();
1230
- } finally {
1231
- setResetSpendSubmitting(false);
1232
- }
1233
- };
1234
1215
  const handleConfirmDelete = async () => {
1235
1216
  if (!deleteConfirmId) return;
1236
1217
  setDeleteSubmitting(true);
@@ -1244,9 +1225,9 @@ var init_KeysTable = __esm({
1244
1225
  const copyToClipboard = (text) => {
1245
1226
  navigator.clipboard.writeText(text);
1246
1227
  };
1247
- const [pruneConfirmCount, setPruneConfirmCount] = (0, import_react3.useState)(null);
1248
- const [pruneSubmitting, setPruneSubmitting] = (0, import_react3.useState)(false);
1249
- const expiredKeys = (0, import_react3.useMemo)(() => {
1228
+ const [pruneConfirmCount, setPruneConfirmCount] = (0, import_react4.useState)(null);
1229
+ const [pruneSubmitting, setPruneSubmitting] = (0, import_react4.useState)(false);
1230
+ const expiredKeys = (0, import_react4.useMemo)(() => {
1250
1231
  return keys.filter((k) => expiryStatus(k.expires_at) === "expired");
1251
1232
  }, [keys]);
1252
1233
  const handlePruneExpired = async () => {
@@ -1261,40 +1242,29 @@ var init_KeysTable = __esm({
1261
1242
  setPruneConfirmCount(null);
1262
1243
  }
1263
1244
  };
1264
- const modelOption = (m) => {
1265
- const inCost = fmtCost(m.input_cost_per_token);
1266
- const outCost = fmtCost(m.output_cost_per_token);
1267
- const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
1268
- return /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, null, /* @__PURE__ */ import_react3.default.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
1269
- };
1270
1245
  const renderTableBody = () => {
1271
1246
  if (loading) {
1272
- return /* @__PURE__ */ import_react3.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { colSpan: 8, sx: { py: 2 } }, /* @__PURE__ */ import_react3.default.createElement(import_Skeleton2.default, { variant: "rounded", height: 140 })));
1247
+ return /* @__PURE__ */ import_react4.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { colSpan: 8, sx: { py: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_Skeleton2.default, { variant: "rounded", height: 140 })));
1273
1248
  }
1274
1249
  if (filteredKeys.length === 0) {
1275
- return /* @__PURE__ */ import_react3.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { colSpan: 8 }, filterText ? /* @__PURE__ */ import_react3.default.createElement(
1250
+ return /* @__PURE__ */ import_react4.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { colSpan: 8 }, filterText ? /* @__PURE__ */ import_react4.default.createElement(
1276
1251
  EmptyState,
1277
1252
  {
1278
1253
  message: "No keys match that filter",
1279
1254
  hint: "Try a different alias or model name."
1280
1255
  }
1281
- ) : /* @__PURE__ */ import_react3.default.createElement(
1256
+ ) : /* @__PURE__ */ import_react4.default.createElement(
1282
1257
  EmptyState,
1283
1258
  {
1284
1259
  message: "No keys yet",
1285
1260
  hint: "Generate your first key to start calling models.",
1286
- action: /* @__PURE__ */ import_react3.default.createElement(
1287
- import_Button2.default,
1261
+ action: /* @__PURE__ */ import_react4.default.createElement(
1262
+ GenerateKeyButton,
1288
1263
  {
1289
- variant: "contained",
1290
- color: "primary",
1291
1264
  size: "small",
1292
- disableElevation: true,
1293
- startIcon: /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Add, null),
1294
- onClick: onGenerateKeyClick,
1295
- sx: { mt: 1 }
1296
- },
1297
- "Generate Your First Key"
1265
+ label: "Generate Your First Key",
1266
+ onClick: onGenerateKeyClick
1267
+ }
1298
1268
  )
1299
1269
  }
1300
1270
  )));
@@ -1303,7 +1273,7 @@ var init_KeysTable = __esm({
1303
1273
  const keyId = key.token ?? key.key;
1304
1274
  const isBlocking = blockingKeyId === keyId;
1305
1275
  const keyModels = key.models ?? [];
1306
- return /* @__PURE__ */ import_react3.default.createElement(
1276
+ return /* @__PURE__ */ import_react4.default.createElement(
1307
1277
  import_TableRow.default,
1308
1278
  {
1309
1279
  key: keyId,
@@ -1314,8 +1284,8 @@ var init_KeysTable = __esm({
1314
1284
  "& td": { color: theme.palette.text.secondary }
1315
1285
  }) : void 0
1316
1286
  },
1317
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2" }, key.key_alias || "\u2014"), key.blocked && /* @__PURE__ */ import_react3.default.createElement(StatusPill, { label: "Blocked", tone: "danger" }))),
1318
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ import_react3.default.createElement(
1287
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2" }, key.key_alias || "\u2014"), key.blocked && /* @__PURE__ */ import_react4.default.createElement(StatusPill, { label: "Blocked", tone: "danger" }))),
1288
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ import_react4.default.createElement(
1319
1289
  import_Typography3.default,
1320
1290
  {
1321
1291
  variant: "body2",
@@ -1332,7 +1302,7 @@ var init_KeysTable = __esm({
1332
1302
  })
1333
1303
  },
1334
1304
  shortKeyId(keyId)
1335
- ), /* @__PURE__ */ import_react3.default.createElement(
1305
+ ), /* @__PURE__ */ import_react4.default.createElement(
1336
1306
  import_IconButton.default,
1337
1307
  {
1338
1308
  size: "small",
@@ -1340,13 +1310,13 @@ var init_KeysTable = __esm({
1340
1310
  title: "Copy Key ID",
1341
1311
  sx: quietIconButtonSx("accent")
1342
1312
  },
1343
- /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.ContentCopy, { sx: { fontSize: 15 } })
1313
+ /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.ContentCopy, { sx: { fontSize: 15 } })
1344
1314
  ))),
1345
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, formatDate(key.created_at))),
1346
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react3.default.createElement(ExpiryCell, { expiresAt: key.expires_at })),
1347
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react3.default.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })),
1348
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, key.tpm_limit ?? "\u221E", " / ", key.rpm_limit ?? "\u221E")),
1349
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, keyModels.length === 0 ? /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, "All models") : /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, keyModels.slice(0, 2).map((model) => /* @__PURE__ */ import_react3.default.createElement(TagChip, { key: model, label: model, title: model })), keyModels.length > 2 && /* @__PURE__ */ import_react3.default.createElement(
1315
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, formatDate(key.created_at))),
1316
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(ExpiryCell, { expiresAt: key.expires_at })),
1317
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })),
1318
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, key.tpm_limit ?? "\u221E", " / ", key.rpm_limit ?? "\u221E")),
1319
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, keyModels.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, "All models") : /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, keyModels.slice(0, 2).map((model) => /* @__PURE__ */ import_react4.default.createElement(TagChip, { key: model, label: model, title: model })), keyModels.length > 2 && /* @__PURE__ */ import_react4.default.createElement(
1350
1320
  TagChip,
1351
1321
  {
1352
1322
  label: `+${keyModels.length - 2}`,
@@ -1354,7 +1324,7 @@ var init_KeysTable = __esm({
1354
1324
  title: keyModels.slice(2).join(", ")
1355
1325
  }
1356
1326
  ))),
1357
- /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { align: "right" }, /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", justifyContent: "flex-end", gap: 0.25 }, /* @__PURE__ */ import_react3.default.createElement(
1327
+ /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { align: "right" }, /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", justifyContent: "flex-end", gap: 0.25 }, /* @__PURE__ */ import_react4.default.createElement(
1358
1328
  import_IconButton.default,
1359
1329
  {
1360
1330
  size: "small",
@@ -1362,8 +1332,8 @@ var init_KeysTable = __esm({
1362
1332
  title: "Edit key",
1363
1333
  sx: quietIconButtonSx("accent")
1364
1334
  },
1365
- /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Edit, { fontSize: "small" })
1366
- ), /* @__PURE__ */ import_react3.default.createElement(
1335
+ /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Edit, { fontSize: "small" })
1336
+ ), /* @__PURE__ */ import_react4.default.createElement(
1367
1337
  import_IconButton.default,
1368
1338
  {
1369
1339
  size: "small",
@@ -1373,7 +1343,7 @@ var init_KeysTable = __esm({
1373
1343
  sx: quietIconButtonSx("warning")
1374
1344
  },
1375
1345
  keyBlockIcon(isBlocking, key.blocked)
1376
- ), /* @__PURE__ */ import_react3.default.createElement(
1346
+ ), /* @__PURE__ */ import_react4.default.createElement(
1377
1347
  import_IconButton.default,
1378
1348
  {
1379
1349
  size: "small",
@@ -1381,18 +1351,18 @@ var init_KeysTable = __esm({
1381
1351
  title: "Revoke key",
1382
1352
  sx: quietIconButtonSx("danger")
1383
1353
  },
1384
- /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Delete, { fontSize: "small" })
1354
+ /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Delete, { fontSize: "small" })
1385
1355
  )))
1386
1356
  );
1387
1357
  });
1388
1358
  };
1389
- return /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(
1359
+ return /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
1390
1360
  SectionCard,
1391
1361
  {
1392
1362
  title: "Virtual Keys",
1393
1363
  subtitle: loading ? "Loading\u2026" : `${filteredKeys.length}${filterText ? ` of ${keys.length}` : ""} key${filteredKeys.length === 1 ? "" : "s"}`,
1394
1364
  flush: true,
1395
- actions: /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(
1365
+ actions: /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
1396
1366
  import_TextField.default,
1397
1367
  {
1398
1368
  size: "small",
@@ -1402,15 +1372,15 @@ var init_KeysTable = __esm({
1402
1372
  onChange: (e) => setFilterText(e.target.value),
1403
1373
  sx: { minWidth: 240 },
1404
1374
  InputProps: {
1405
- startAdornment: /* @__PURE__ */ import_react3.default.createElement(import_InputAdornment.default, { position: "start" }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Search, { fontSize: "small", color: "disabled" }))
1375
+ startAdornment: /* @__PURE__ */ import_react4.default.createElement(import_InputAdornment.default, { position: "start" }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Search, { fontSize: "small", color: "disabled" }))
1406
1376
  }
1407
1377
  }
1408
- ), expiredKeys.length > 0 && /* @__PURE__ */ import_react3.default.createElement(
1378
+ ), expiredKeys.length > 0 && /* @__PURE__ */ import_react4.default.createElement(
1409
1379
  import_Button2.default,
1410
1380
  {
1411
1381
  variant: "outlined",
1412
1382
  color: "inherit",
1413
- startIcon: /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Autorenew, null),
1383
+ startIcon: /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Autorenew, null),
1414
1384
  onClick: () => setPruneConfirmCount(expiredKeys.length),
1415
1385
  sx: (theme) => ({
1416
1386
  color: theme.palette.text.secondary,
@@ -1425,88 +1395,10 @@ var init_KeysTable = __esm({
1425
1395
  "Prune expired (",
1426
1396
  expiredKeys.length,
1427
1397
  ")"
1428
- ), /* @__PURE__ */ import_react3.default.createElement(
1429
- import_Button2.default,
1430
- {
1431
- variant: "contained",
1432
- color: "primary",
1433
- disableElevation: true,
1434
- startIcon: /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Add, null),
1435
- onClick: onGenerateKeyClick
1436
- },
1437
- "Generate New Key"
1438
- ))
1398
+ ), /* @__PURE__ */ import_react4.default.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick }))
1439
1399
  },
1440
- /* @__PURE__ */ import_react3.default.createElement(import_TableContainer.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Table.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react3.default.createElement(import_TableHead.default, null, /* @__PURE__ */ import_react3.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { sortDirection: sortKey === "alias" ? sortDirection : false }, /* @__PURE__ */ import_react3.default.createElement(import_TableSortLabel.default, { active: sortKey === "alias", direction: sortKey === "alias" ? sortDirection : "asc", onClick: () => handleSort("alias") }, "Alias")), /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, null, "Key ID"), /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { sortDirection: sortKey === "created" ? sortDirection : false }, /* @__PURE__ */ import_react3.default.createElement(import_TableSortLabel.default, { active: sortKey === "created", direction: sortKey === "created" ? sortDirection : "asc", onClick: () => handleSort("created") }, "Created")), /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { sortDirection: sortKey === "expires" ? sortDirection : false }, /* @__PURE__ */ import_react3.default.createElement(import_TableSortLabel.default, { active: sortKey === "expires", direction: sortKey === "expires" ? sortDirection : "asc", onClick: () => handleSort("expires") }, "Expires")), /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { sortDirection: sortKey === "budget" ? sortDirection : false }, /* @__PURE__ */ import_react3.default.createElement(import_TableSortLabel.default, { active: sortKey === "budget", direction: sortKey === "budget" ? sortDirection : "asc", onClick: () => handleSort("budget") }, "Budget")), /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { sortDirection: sortKey === "limits" ? sortDirection : false }, /* @__PURE__ */ import_react3.default.createElement(import_TableSortLabel.default, { active: sortKey === "limits", direction: sortKey === "limits" ? sortDirection : "asc", onClick: () => handleSort("limits") }, "TPM / RPM")), /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { sortDirection: sortKey === "models" ? sortDirection : false }, /* @__PURE__ */ import_react3.default.createElement(import_TableSortLabel.default, { active: sortKey === "models", direction: sortKey === "models" ? sortDirection : "asc", onClick: () => handleSort("models") }, "Models")), /* @__PURE__ */ import_react3.default.createElement(import_TableCell.default, { align: "right" }, "Actions"))), /* @__PURE__ */ import_react3.default.createElement(import_TableBody.default, null, renderTableBody())))
1441
- ), /* @__PURE__ */ import_react3.default.createElement(import_Dialog.default, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react3.default.createElement(import_DialogTitle.default, null, "Edit Key"), /* @__PURE__ */ import_react3.default.createElement(import_DialogContent.default, null, editingKey && /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ import_react3.default.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editingKey.key))), /* @__PURE__ */ import_react3.default.createElement(
1442
- import_TextField.default,
1443
- {
1444
- label: "Alias",
1445
- value: editForm.key_alias || "",
1446
- onChange: (e) => setEditForm({ ...editForm, key_alias: e.target.value }),
1447
- fullWidth: true
1448
- }
1449
- ), models.length > 0 && /* @__PURE__ */ import_react3.default.createElement(
1450
- import_Autocomplete.default,
1451
- {
1452
- multiple: true,
1453
- options: models,
1454
- groupBy: (m) => m.mode || "other",
1455
- getOptionLabel: (m) => m.model_name,
1456
- value: editSelectedModels,
1457
- onChange: (_e, selected) => setEditForm({ ...editForm, models: selected.map((m) => m.model_name) }),
1458
- renderOption: (props, m) => /* @__PURE__ */ import_react3.default.createElement("li", { ...props }, modelOption(m)),
1459
- renderInput: (params) => /* @__PURE__ */ import_react3.default.createElement(import_TextField.default, { ...params, label: "Models", fullWidth: true })
1460
- }
1461
- ), /* @__PURE__ */ import_react3.default.createElement(
1462
- import_TextField.default,
1463
- {
1464
- label: "Max Budget (USD)",
1465
- type: "number",
1466
- value: editForm.max_budget ?? "",
1467
- onChange: (e) => setEditForm({ ...editForm, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
1468
- fullWidth: true
1469
- }
1470
- ), /* @__PURE__ */ import_react3.default.createElement(
1471
- import_TextField.default,
1472
- {
1473
- label: "TPM Limit",
1474
- type: "number",
1475
- value: editForm.tpm_limit ?? "",
1476
- onChange: (e) => setEditForm({ ...editForm, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
1477
- helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
1478
- fullWidth: true
1479
- }
1480
- ), /* @__PURE__ */ import_react3.default.createElement(
1481
- import_TextField.default,
1482
- {
1483
- label: "RPM Limit",
1484
- type: "number",
1485
- value: editForm.rpm_limit ?? "",
1486
- onChange: (e) => setEditForm({ ...editForm, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
1487
- helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
1488
- fullWidth: true
1489
- }
1490
- ), /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ import_react3.default.createElement(
1491
- import_Button2.default,
1492
- {
1493
- size: "small",
1494
- color: "warning",
1495
- variant: "outlined",
1496
- onClick: () => setResetSpendConfirm(true)
1497
- },
1498
- "Reset Spend to $0"
1499
- ) : /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ import_react3.default.createElement(
1500
- import_Button2.default,
1501
- {
1502
- size: "small",
1503
- color: "warning",
1504
- variant: "contained",
1505
- disabled: resetSpendSubmitting,
1506
- onClick: handleResetSpend
1507
- },
1508
- resetSpendSubmitting ? /* @__PURE__ */ import_react3.default.createElement(import_CircularProgress.default, { size: 16 }) : "Confirm"
1509
- ), /* @__PURE__ */ import_react3.default.createElement(import_Button2.default, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel"))))), /* @__PURE__ */ import_react3.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Button2.default, { onClick: handleCloseEdit }, "Cancel"), /* @__PURE__ */ import_react3.default.createElement(import_Button2.default, { onClick: handleUpdate, variant: "contained", color: "primary", disabled: editSubmitting }, editSubmitting ? /* @__PURE__ */ import_react3.default.createElement(import_CircularProgress.default, { size: 24 }) : "Save"))), /* @__PURE__ */ import_react3.default.createElement(import_Dialog.default, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react3.default.createElement(import_DialogTitle.default, null, "Revoke Key?"), /* @__PURE__ */ import_react3.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ import_react3.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Button2.default, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ import_react3.default.createElement(
1400
+ /* @__PURE__ */ import_react4.default.createElement(import_TableContainer.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Table.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react4.default.createElement(import_TableHead.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "alias" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "alias", direction: sortKey === "alias" ? sortDirection : "asc", onClick: () => handleSort("alias") }, "Alias")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, "Key ID"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "created" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "created", direction: sortKey === "created" ? sortDirection : "asc", onClick: () => handleSort("created") }, "Created")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "expires" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "expires", direction: sortKey === "expires" ? sortDirection : "asc", onClick: () => handleSort("expires") }, "Expires")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "budget" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "budget", direction: sortKey === "budget" ? sortDirection : "asc", onClick: () => handleSort("budget") }, "Budget")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "limits" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "limits", direction: sortKey === "limits" ? sortDirection : "asc", onClick: () => handleSort("limits") }, "TPM / RPM")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "models" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "models", direction: sortKey === "models" ? sortDirection : "asc", onClick: () => handleSort("models") }, "Models")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { align: "right" }, "Actions"))), /* @__PURE__ */ import_react4.default.createElement(import_TableBody.default, null, renderTableBody())))
1401
+ ), /* @__PURE__ */ import_react4.default.createElement(import_Dialog.default, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react4.default.createElement(import_DialogTitle.default, null, "Revoke Key?"), /* @__PURE__ */ import_react4.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ import_react4.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Button2.default, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ import_react4.default.createElement(
1510
1402
  import_Button2.default,
1511
1403
  {
1512
1404
  onClick: handleConfirmDelete,
@@ -1514,8 +1406,8 @@ var init_KeysTable = __esm({
1514
1406
  color: "error",
1515
1407
  disabled: deleteSubmitting
1516
1408
  },
1517
- deleteSubmitting ? /* @__PURE__ */ import_react3.default.createElement(import_CircularProgress.default, { size: 20 }) : "Revoke"
1518
- ))), /* @__PURE__ */ import_react3.default.createElement(import_Dialog.default, { open: pruneConfirmCount !== null, onClose: () => setPruneConfirmCount(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react3.default.createElement(import_DialogTitle.default, null, "Prune Expired Keys?"), /* @__PURE__ */ import_react3.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, null, "This will permanently delete ", pruneConfirmCount, " expired key", pruneConfirmCount !== 1 ? "s" : "", " from LiteLLM. Any integrations using them will stop working immediately.")), /* @__PURE__ */ import_react3.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Button2.default, { onClick: () => setPruneConfirmCount(null), disabled: pruneSubmitting }, "Cancel"), /* @__PURE__ */ import_react3.default.createElement(
1409
+ deleteSubmitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 20 }) : "Revoke"
1410
+ ))), /* @__PURE__ */ import_react4.default.createElement(import_Dialog.default, { open: pruneConfirmCount !== null, onClose: () => setPruneConfirmCount(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react4.default.createElement(import_DialogTitle.default, null, "Prune Expired Keys?"), /* @__PURE__ */ import_react4.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, null, "This will permanently delete ", pruneConfirmCount, " expired key", pruneConfirmCount !== 1 ? "s" : "", " from LiteLLM. Any integrations using them will stop working immediately.")), /* @__PURE__ */ import_react4.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Button2.default, { onClick: () => setPruneConfirmCount(null), disabled: pruneSubmitting }, "Cancel"), /* @__PURE__ */ import_react4.default.createElement(
1519
1411
  import_Button2.default,
1520
1412
  {
1521
1413
  onClick: handlePruneExpired,
@@ -1523,7 +1415,7 @@ var init_KeysTable = __esm({
1523
1415
  color: "error",
1524
1416
  disabled: pruneSubmitting
1525
1417
  },
1526
- pruneSubmitting ? /* @__PURE__ */ import_react3.default.createElement(import_CircularProgress.default, { size: 20 }) : "Prune"
1418
+ pruneSubmitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 20 }) : "Prune"
1527
1419
  ))));
1528
1420
  };
1529
1421
  }
@@ -1543,7 +1435,7 @@ var init_format = __esm({
1543
1435
  }
1544
1436
  });
1545
1437
 
1546
- // src/components/GenerateKeyDialog.tsx
1438
+ // src/components/KeyFormDialog.tsx
1547
1439
  function trimSlash(url) {
1548
1440
  return url.replace(/\/+$/, "");
1549
1441
  }
@@ -1636,17 +1528,17 @@ function teamHelperText(teamError, teamRequired) {
1636
1528
  if (teamRequired) return "Bind this key to a team for scoped access";
1637
1529
  return "Optional: bind this key to a specific team for scoped access";
1638
1530
  }
1639
- function budgetHelperText(budgetInvalid, budgetEstimate) {
1640
- if (budgetInvalid) return 'Enter a positive budget or tick "Unlimited"';
1531
+ function budgetHelperText(budgetInvalid, budgetEstimate, unlimited) {
1532
+ if (budgetInvalid && !unlimited) return 'Enter a positive budget or tick "Unlimited"';
1641
1533
  if (budgetEstimate !== null) return `\u2248 ${fmtInt(budgetEstimate)} tokens at the selected model's rate`;
1642
1534
  return void 0;
1643
1535
  }
1644
- function fmtCost2(perToken) {
1536
+ function fmtCost(perToken) {
1645
1537
  if (!perToken) return null;
1646
1538
  const per1k = perToken * 1e3;
1647
1539
  return per1k < 0.01 ? `$${(perToken * 1e6).toFixed(2)}/M` : `$${per1k.toFixed(3)}/1K`;
1648
1540
  }
1649
- function formatContextWindow2(maxInput, maxOutput) {
1541
+ function formatContextWindow(maxInput, maxOutput) {
1650
1542
  if (!maxInput && !maxOutput) return null;
1651
1543
  const fmt = (n) => {
1652
1544
  if (!n) return null;
@@ -1661,11 +1553,11 @@ function formatContextWindow2(maxInput, maxOutput) {
1661
1553
  if (outPart) return `ctx ${outPart} out`;
1662
1554
  return null;
1663
1555
  }
1664
- var import_react4, import_Box4, import_Typography4, import_Dialog2, import_DialogTitle2, import_DialogContent2, import_DialogActions2, import_TextField2, import_MenuItem, import_Button3, import_IconButton2, import_CircularProgress2, import_Autocomplete2, import_Checkbox, import_FormControlLabel, import_Alert, import_Accordion, import_AccordionSummary, import_AccordionDetails, import_Tabs, import_Tab, import_icons_material3, generateDefaultAlias, emptyForm, ALL_PROXY_MODELS, SNIPPET_FILE_HINTS, SnippetTabs, GenerateKeyDialog;
1665
- var init_GenerateKeyDialog = __esm({
1666
- "src/components/GenerateKeyDialog.tsx"() {
1556
+ var import_react5, import_Box4, import_Typography4, import_Dialog2, import_DialogTitle2, import_DialogContent2, import_DialogActions2, import_TextField2, import_MenuItem, import_Button3, import_IconButton2, import_CircularProgress2, import_Autocomplete, import_Checkbox, import_FormControlLabel, import_Alert, import_Accordion, import_AccordionSummary, import_AccordionDetails, import_Tabs, import_Tab, import_icons_material3, generateDefaultAlias, createForm, editForm, ALL_PROXY_MODELS, SNIPPET_FILE_HINTS, SnippetTabs, KeyFormDialog;
1557
+ var init_KeyFormDialog = __esm({
1558
+ "src/components/KeyFormDialog.tsx"() {
1667
1559
  "use strict";
1668
- import_react4 = __toESM(require("react"));
1560
+ import_react5 = __toESM(require("react"));
1669
1561
  import_Box4 = __toESM(require("@mui/material/Box"));
1670
1562
  import_Typography4 = __toESM(require("@mui/material/Typography"));
1671
1563
  import_Dialog2 = __toESM(require("@mui/material/Dialog"));
@@ -1677,7 +1569,7 @@ var init_GenerateKeyDialog = __esm({
1677
1569
  import_Button3 = __toESM(require("@mui/material/Button"));
1678
1570
  import_IconButton2 = __toESM(require("@mui/material/IconButton"));
1679
1571
  import_CircularProgress2 = __toESM(require("@mui/material/CircularProgress"));
1680
- import_Autocomplete2 = __toESM(require("@mui/material/Autocomplete"));
1572
+ import_Autocomplete = __toESM(require("@mui/material/Autocomplete"));
1681
1573
  import_Checkbox = __toESM(require("@mui/material/Checkbox"));
1682
1574
  import_FormControlLabel = __toESM(require("@mui/material/FormControlLabel"));
1683
1575
  import_Alert = __toESM(require("@mui/material/Alert"));
@@ -1693,8 +1585,8 @@ var init_GenerateKeyDialog = __esm({
1693
1585
  const hash = Math.random().toString(36).slice(2, 8);
1694
1586
  return `${base}-${hash}`;
1695
1587
  };
1696
- emptyForm = () => ({
1697
- alias: "",
1588
+ createForm = (username) => ({
1589
+ alias: generateDefaultAlias(username),
1698
1590
  models: [],
1699
1591
  duration: "30d",
1700
1592
  max_budget: 100,
@@ -1703,6 +1595,13 @@ var init_GenerateKeyDialog = __esm({
1703
1595
  team_id: void 0,
1704
1596
  key_type: "llm_api"
1705
1597
  });
1598
+ editForm = (k) => ({
1599
+ key_alias: k.key_alias ?? "",
1600
+ models: k.models ?? [],
1601
+ max_budget: k.max_budget,
1602
+ tpm_limit: k.tpm_limit,
1603
+ rpm_limit: k.rpm_limit
1604
+ });
1706
1605
  ALL_PROXY_MODELS = "all-proxy-models";
1707
1606
  SNIPPET_FILE_HINTS = {
1708
1607
  opencode: "Add to ~/.config/opencode/opencode.json",
@@ -1710,18 +1609,18 @@ var init_GenerateKeyDialog = __esm({
1710
1609
  "claude-code": "Requires Claude Code CLI installed"
1711
1610
  };
1712
1611
  SnippetTabs = ({ snippets, model, onCopy }) => {
1713
- const [tab, setTab] = (0, import_react4.useState)("curl");
1612
+ const [tab, setTab] = (0, import_react5.useState)("curl");
1714
1613
  const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
1715
1614
  const code = snippets[snippetKey];
1716
1615
  const fileHint = SNIPPET_FILE_HINTS[tab];
1717
- return /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Tabs.default, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 }, variant: "scrollable" }, /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "curl", value: "curl" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "OpenAI SDK", value: "openai" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "opencode", value: "opencode" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "pi", value: "pi" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "Claude Code", value: "claude-code" })), fileHint && /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, fileHint), /* @__PURE__ */ import_react4.default.createElement(
1616
+ return /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Tabs.default, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 }, variant: "scrollable" }, /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "curl", value: "curl" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "OpenAI SDK", value: "openai" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "opencode", value: "opencode" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "pi", value: "pi" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "Claude Code", value: "claude-code" })), fileHint && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, fileHint), /* @__PURE__ */ import_react5.default.createElement(
1718
1617
  import_Box4.default,
1719
1618
  {
1720
1619
  position: "relative",
1721
1620
  p: 1.5,
1722
1621
  sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
1723
1622
  },
1724
- /* @__PURE__ */ import_react4.default.createElement(
1623
+ /* @__PURE__ */ import_react5.default.createElement(
1725
1624
  import_IconButton2.default,
1726
1625
  {
1727
1626
  size: "small",
@@ -1729,9 +1628,9 @@ var init_GenerateKeyDialog = __esm({
1729
1628
  title: "Copy snippet",
1730
1629
  sx: { position: "absolute", top: 4, right: 4 }
1731
1630
  },
1732
- /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" })
1631
+ /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" })
1733
1632
  ),
1734
- /* @__PURE__ */ import_react4.default.createElement(
1633
+ /* @__PURE__ */ import_react5.default.createElement(
1735
1634
  import_Typography4.default,
1736
1635
  {
1737
1636
  component: "pre",
@@ -1746,50 +1645,83 @@ var init_GenerateKeyDialog = __esm({
1746
1645
  },
1747
1646
  code
1748
1647
  ),
1749
- model && /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mt: 1 }, "Using model \u201C", model, "\u201D \u2014 swap it for any model you have access to.")
1648
+ model && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mt: 1 }, "Using model \u201C", model, "\u201D \u2014 swap it for any model you have access to.")
1750
1649
  ));
1751
1650
  };
1752
- GenerateKeyDialog = ({
1651
+ KeyFormDialog = ({
1753
1652
  open,
1754
1653
  onClose,
1654
+ mode,
1655
+ keyToEdit = null,
1755
1656
  keys,
1756
1657
  models,
1757
1658
  teams,
1758
1659
  username,
1759
1660
  keyGenerationSettings,
1760
- onGenerateKey,
1661
+ onCreateKey,
1662
+ onUpdateKey,
1663
+ onResetKeySpend,
1761
1664
  onGetConfig
1762
1665
  }) => {
1763
- const [formData, setFormData] = (0, import_react4.useState)(emptyForm());
1764
- const [unlimitedBudget, setUnlimitedBudget] = (0, import_react4.useState)(false);
1765
- const [submitting, setSubmitting] = (0, import_react4.useState)(false);
1766
- const [newKeyValue, setNewKeyValue] = (0, import_react4.useState)(null);
1767
- const [newKeySnippets, setNewKeySnippets] = (0, import_react4.useState)(null);
1768
- const [newKeyModel, setNewKeyModel] = (0, import_react4.useState)("");
1769
- const [generateError, setGenerateError] = (0, import_react4.useState)(null);
1770
- (0, import_react4.useEffect)(() => {
1666
+ const isCreate = mode === "create";
1667
+ const [generateForm, setGenerateForm] = (0, import_react5.useState)(createForm(username));
1668
+ const [editFormState, setEditFormState] = (0, import_react5.useState)({});
1669
+ const [unlimitedBudget, setUnlimitedBudget] = (0, import_react5.useState)(false);
1670
+ const [submitting, setSubmitting] = (0, import_react5.useState)(false);
1671
+ const [newKeyValue, setNewKeyValue] = (0, import_react5.useState)(null);
1672
+ const [newKeySnippets, setNewKeySnippets] = (0, import_react5.useState)(null);
1673
+ const [newKeyModel, setNewKeyModel] = (0, import_react5.useState)("");
1674
+ const [generateError, setGenerateError] = (0, import_react5.useState)(null);
1675
+ const [editError, setEditError] = (0, import_react5.useState)(null);
1676
+ const [resetSpendConfirm, setResetSpendConfirm] = (0, import_react5.useState)(false);
1677
+ const [resetSpendSubmitting, setResetSpendSubmitting] = (0, import_react5.useState)(false);
1678
+ (0, import_react5.useEffect)(() => {
1771
1679
  if (open) {
1772
- setFormData({ ...emptyForm(), alias: generateDefaultAlias(username) });
1773
- setUnlimitedBudget(false);
1680
+ if (isCreate) {
1681
+ setGenerateForm(createForm(username));
1682
+ } else if (keyToEdit) {
1683
+ setEditFormState(editForm(keyToEdit));
1684
+ setUnlimitedBudget(
1685
+ keyGenerationSettings?.allowUnlimitedBudget === true && (keyToEdit.max_budget === null || keyToEdit.max_budget === void 0)
1686
+ );
1687
+ } else {
1688
+ setEditFormState({});
1689
+ setUnlimitedBudget(false);
1690
+ }
1774
1691
  setNewKeyValue(null);
1775
1692
  setNewKeySnippets(null);
1776
1693
  setNewKeyModel("");
1777
1694
  setGenerateError(null);
1695
+ setEditError(null);
1696
+ setResetSpendConfirm(false);
1778
1697
  }
1779
- }, [open, username]);
1698
+ }, [open, isCreate, keyToEdit, username, keyGenerationSettings?.allowUnlimitedBudget]);
1780
1699
  const allowUnlimitedBudget = keyGenerationSettings?.allowUnlimitedBudget ?? false;
1781
1700
  const teamRequired = keyGenerationSettings?.teamRequired ?? true;
1782
- const selectedTeam = teams.find((t) => t.team_id === formData.team_id) ?? null;
1783
- const availableModels = (0, import_react4.useMemo)(() => {
1701
+ const formData = isCreate ? generateForm : editFormState;
1702
+ const setFormData = (patch) => {
1703
+ if (isCreate) {
1704
+ setGenerateForm((prev) => ({ ...prev, ...patch }));
1705
+ } else {
1706
+ setEditFormState((prev) => ({ ...prev, ...patch }));
1707
+ }
1708
+ };
1709
+ const currentAlias = (isCreate ? generateForm.alias : editFormState.key_alias) ?? "";
1710
+ const setAliasField = (value) => {
1711
+ if (isCreate) setFormData({ alias: value });
1712
+ else setFormData({ key_alias: value });
1713
+ };
1714
+ const selectedTeam = teams.find((t) => t.team_id === (isCreate ? generateForm.team_id : void 0)) ?? null;
1715
+ const availableModels = (0, import_react5.useMemo)(() => {
1784
1716
  return models.filter((m) => isModelAllowedByTeam(m, selectedTeam?.models));
1785
1717
  }, [models, selectedTeam]);
1786
1718
  const selectedModels = availableModels.filter((m) => (formData.models || []).includes(m.model_name));
1787
- const aliasError = !(formData.alias || "").trim();
1788
- const teamError = teamRequired && !formData.team_id;
1719
+ const aliasError = !currentAlias.trim();
1720
+ const teamError = isCreate && teamRequired && !generateForm.team_id;
1789
1721
  const budgetInvalid = !unlimitedBudget && (formData.max_budget === void 0 || formData.max_budget === null || formData.max_budget <= 0);
1790
- const canGenerate = !aliasError && !teamError && !budgetInvalid && !submitting;
1791
- const aliasDuplicate = !!formData.alias && keys.some((k) => k.key_alias === formData.alias);
1792
- const budgetEstimate = (0, import_react4.useMemo)(() => {
1722
+ const canSubmit = !aliasError && !teamError && !budgetInvalid && !submitting;
1723
+ const aliasDuplicate = !!currentAlias && keys.some((k) => k.key_alias === currentAlias && k !== keyToEdit);
1724
+ const budgetEstimate = (0, import_react5.useMemo)(() => {
1793
1725
  if (unlimitedBudget || budgetInvalid) return null;
1794
1726
  const inputCosts = selectedModels.map((m) => m.input_cost_per_token).filter((c) => typeof c === "number" && c > 0);
1795
1727
  if (inputCosts.length === 0) return null;
@@ -1801,11 +1733,11 @@ var init_GenerateKeyDialog = __esm({
1801
1733
  setGenerateError(null);
1802
1734
  try {
1803
1735
  const request = {
1804
- ...formData,
1805
- max_budget: unlimitedBudget ? null : formData.max_budget
1736
+ ...generateForm,
1737
+ max_budget: unlimitedBudget ? null : generateForm.max_budget
1806
1738
  };
1807
- const response = await onGenerateKey(request);
1808
- const model = formData.models?.[0] ?? availableModels[0]?.model_name ?? "";
1739
+ const response = await onCreateKey(request);
1740
+ const model = generateForm.models?.[0] ?? availableModels[0]?.model_name ?? "";
1809
1741
  setNewKeyValue(response.key);
1810
1742
  setNewKeyModel(model);
1811
1743
  setNewKeySnippets(null);
@@ -1814,7 +1746,7 @@ var init_GenerateKeyDialog = __esm({
1814
1746
  setNewKeySnippets(buildSnippets(config.baseUrl, response.key, model));
1815
1747
  } catch {
1816
1748
  }
1817
- setFormData(emptyForm());
1749
+ setGenerateForm(createForm(username));
1818
1750
  setUnlimitedBudget(false);
1819
1751
  } catch (error) {
1820
1752
  setGenerateError(error?.message ?? "Failed to generate key");
@@ -1822,27 +1754,58 @@ var init_GenerateKeyDialog = __esm({
1822
1754
  setSubmitting(false);
1823
1755
  }
1824
1756
  };
1757
+ const handleUpdate = async () => {
1758
+ if (!keyToEdit) return;
1759
+ setSubmitting(true);
1760
+ setEditError(null);
1761
+ try {
1762
+ const request = {
1763
+ ...editFormState,
1764
+ max_budget: unlimitedBudget ? null : editFormState.max_budget
1765
+ };
1766
+ await onUpdateKey(keyToEdit.token ?? keyToEdit.key, request);
1767
+ onClose();
1768
+ } catch (error) {
1769
+ setEditError(error?.message ?? "Failed to update key");
1770
+ } finally {
1771
+ setSubmitting(false);
1772
+ }
1773
+ };
1774
+ const handleResetSpend = async () => {
1775
+ if (!keyToEdit || !onResetKeySpend) return;
1776
+ setResetSpendSubmitting(true);
1777
+ try {
1778
+ await onResetKeySpend(keyToEdit.token ?? keyToEdit.key);
1779
+ setResetSpendConfirm(false);
1780
+ onClose();
1781
+ } finally {
1782
+ setResetSpendSubmitting(false);
1783
+ }
1784
+ };
1825
1785
  const handleClose = () => {
1826
1786
  onClose();
1827
1787
  setGenerateError(null);
1788
+ setEditError(null);
1828
1789
  setNewKeyValue(null);
1829
1790
  setNewKeySnippets(null);
1830
- setFormData(emptyForm());
1791
+ setGenerateForm(createForm(username));
1792
+ setEditFormState({});
1831
1793
  setUnlimitedBudget(false);
1794
+ setResetSpendConfirm(false);
1832
1795
  };
1833
1796
  const copyToClipboard = (text) => {
1834
1797
  navigator.clipboard.writeText(text);
1835
1798
  };
1836
1799
  const modelOption = (m) => {
1837
- const inCost = fmtCost2(m.input_cost_per_token);
1838
- const outCost = fmtCost2(m.output_cost_per_token);
1839
- const ctx = formatContextWindow2(m.max_input_tokens, m.max_output_tokens);
1840
- return /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react4.default.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
1800
+ const inCost = fmtCost(m.input_cost_per_token);
1801
+ const outCost = fmtCost(m.output_cost_per_token);
1802
+ const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
1803
+ return /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react5.default.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
1841
1804
  };
1842
1805
  const renderTeamField = () => {
1843
1806
  if (teams.length > 0) {
1844
- return /* @__PURE__ */ import_react4.default.createElement(
1845
- import_Autocomplete2.default,
1807
+ return /* @__PURE__ */ import_react5.default.createElement(
1808
+ import_Autocomplete.default,
1846
1809
  {
1847
1810
  options: teams,
1848
1811
  getOptionLabel: (t) => t.team_alias || t.team_id,
@@ -1852,28 +1815,61 @@ var init_GenerateKeyDialog = __esm({
1852
1815
  const model = models.find((m) => m.model_name === name);
1853
1816
  return model ? isModelAllowedByTeam(model, team?.models) : false;
1854
1817
  });
1855
- setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
1818
+ setFormData({ team_id: team?.team_id, models: restrictedModels });
1856
1819
  },
1857
- renderInput: (params) => /* @__PURE__ */ import_react4.default.createElement(
1820
+ disabled: !isCreate,
1821
+ renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
1858
1822
  import_TextField2.default,
1859
1823
  {
1860
1824
  ...params,
1861
1825
  label: "Team",
1862
1826
  error: teamError,
1863
- helperText: teamHelperText(teamError, teamRequired),
1864
- required: teamRequired,
1827
+ helperText: isCreate ? teamHelperText(teamError, teamRequired) : "Team binding is fixed after creation \u2014 delete and recreate the key to change it",
1828
+ required: isCreate && teamRequired,
1865
1829
  fullWidth: true
1866
1830
  }
1867
1831
  )
1868
1832
  }
1869
1833
  );
1870
1834
  }
1871
- if (teamRequired) {
1872
- return /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.");
1835
+ if (teamRequired && isCreate) {
1836
+ return /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.");
1873
1837
  }
1874
1838
  return null;
1875
1839
  };
1876
- return /* @__PURE__ */ import_react4.default.createElement(import_Dialog2.default, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react4.default.createElement(import_DialogTitle2.default, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ import_react4.default.createElement(import_DialogContent2.default, null, newKeyValue ? /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ import_react4.default.createElement(
1840
+ const editKeyId = keyToEdit?.token ?? keyToEdit?.key ?? "";
1841
+ const renderDangerZone = () => {
1842
+ if (isCreate || !onResetKeySpend) return null;
1843
+ return /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ import_react5.default.createElement(
1844
+ import_Button3.default,
1845
+ {
1846
+ size: "small",
1847
+ color: "warning",
1848
+ variant: "outlined",
1849
+ onClick: () => setResetSpendConfirm(true)
1850
+ },
1851
+ "Reset Spend to $0"
1852
+ ) : /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ import_react5.default.createElement(
1853
+ import_Button3.default,
1854
+ {
1855
+ size: "small",
1856
+ color: "warning",
1857
+ variant: "contained",
1858
+ disabled: resetSpendSubmitting,
1859
+ onClick: handleResetSpend
1860
+ },
1861
+ resetSpendSubmitting ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress2.default, { size: 16 }) : "Confirm"
1862
+ ), /* @__PURE__ */ import_react5.default.createElement(import_Button3.default, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel")));
1863
+ };
1864
+ const maskKey = (key) => {
1865
+ if (key.length <= 8) return "***";
1866
+ return `${key.slice(0, 4)}...${key.slice(-4)}`;
1867
+ };
1868
+ let dialogTitle = "Edit Key";
1869
+ if (newKeyValue) dialogTitle = "Key Generated";
1870
+ else if (isCreate) dialogTitle = "Generate New Key";
1871
+ const submitLabel = isCreate ? "Generate" : "Save";
1872
+ return /* @__PURE__ */ import_react5.default.createElement(import_Dialog2.default, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react5.default.createElement(import_DialogTitle2.default, null, dialogTitle), /* @__PURE__ */ import_react5.default.createElement(import_DialogContent2.default, null, newKeyValue ? /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ import_react5.default.createElement(
1877
1873
  import_Box4.default,
1878
1874
  {
1879
1875
  display: "flex",
@@ -1888,7 +1884,7 @@ var init_GenerateKeyDialog = __esm({
1888
1884
  borderRadius: 1
1889
1885
  }
1890
1886
  },
1891
- /* @__PURE__ */ import_react4.default.createElement(
1887
+ /* @__PURE__ */ import_react5.default.createElement(
1892
1888
  import_Typography4.default,
1893
1889
  {
1894
1890
  component: "code",
@@ -1897,8 +1893,8 @@ var init_GenerateKeyDialog = __esm({
1897
1893
  },
1898
1894
  newKeyValue
1899
1895
  ),
1900
- /* @__PURE__ */ import_react4.default.createElement(import_IconButton2.default, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.ContentCopy, null))
1901
- ), newKeySnippets && /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { mt: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "Public endpoint \u2014 paste into any tool's base URL / API base field"), /* @__PURE__ */ import_react4.default.createElement(
1896
+ /* @__PURE__ */ import_react5.default.createElement(import_IconButton2.default, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, null))
1897
+ ), newKeySnippets && /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { mt: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "Public endpoint \u2014 paste into any tool's base URL / API base field"), /* @__PURE__ */ import_react5.default.createElement(
1902
1898
  import_Box4.default,
1903
1899
  {
1904
1900
  display: "flex",
@@ -1912,7 +1908,7 @@ var init_GenerateKeyDialog = __esm({
1912
1908
  borderRadius: 1
1913
1909
  }
1914
1910
  },
1915
- /* @__PURE__ */ import_react4.default.createElement(
1911
+ /* @__PURE__ */ import_react5.default.createElement(
1916
1912
  import_Typography4.default,
1917
1913
  {
1918
1914
  component: "code",
@@ -1921,15 +1917,16 @@ var init_GenerateKeyDialog = __esm({
1921
1917
  },
1922
1918
  newKeySnippets.publicEndpoint
1923
1919
  ),
1924
- /* @__PURE__ */ import_react4.default.createElement(import_IconButton2.default, { size: "small", onClick: () => copyToClipboard(newKeySnippets.publicEndpoint) }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" }))
1925
- )), newKeySnippets && /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { mt: 3 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ import_react4.default.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, generateError && /* @__PURE__ */ import_react4.default.createElement(import_Alert.default, { severity: "error", onClose: () => setGenerateError(null) }, generateError), /* @__PURE__ */ import_react4.default.createElement(
1920
+ /* @__PURE__ */ import_react5.default.createElement(import_IconButton2.default, { size: "small", onClick: () => copyToClipboard(newKeySnippets.publicEndpoint) }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" }))
1921
+ )), newKeySnippets && /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { mt: 3 }, /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ import_react5.default.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, !isCreate && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ import_react5.default.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editKeyId))), generateError && /* @__PURE__ */ import_react5.default.createElement(import_Alert.default, { severity: "error", onClose: () => setGenerateError(null) }, generateError), editError && /* @__PURE__ */ import_react5.default.createElement(import_Alert.default, { severity: "error", onClose: () => setEditError(null) }, editError), /* @__PURE__ */ import_react5.default.createElement(
1926
1922
  import_TextField2.default,
1927
1923
  {
1928
1924
  label: "Alias",
1929
- value: formData.alias || "",
1925
+ value: currentAlias,
1930
1926
  onChange: (e) => {
1931
- setFormData({ ...formData, alias: e.target.value });
1927
+ setAliasField(e.target.value);
1932
1928
  setGenerateError(null);
1929
+ setEditError(null);
1933
1930
  },
1934
1931
  error: aliasError,
1935
1932
  color: aliasDuplicate ? "warning" : void 0,
@@ -1937,31 +1934,31 @@ var init_GenerateKeyDialog = __esm({
1937
1934
  required: true,
1938
1935
  fullWidth: true
1939
1936
  }
1940
- ), /* @__PURE__ */ import_react4.default.createElement(
1937
+ ), !isCreate ? null : /* @__PURE__ */ import_react5.default.createElement(
1941
1938
  import_TextField2.default,
1942
1939
  {
1943
1940
  select: true,
1944
1941
  label: "Duration",
1945
- value: formData.duration || "30d",
1946
- onChange: (e) => setFormData({ ...formData, duration: e.target.value }),
1942
+ value: generateForm.duration || "30d",
1943
+ onChange: (e) => setFormData({ duration: e.target.value }),
1947
1944
  fullWidth: true
1948
1945
  },
1949
- /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "1d" }, "1 Day"),
1950
- /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "7d" }, "7 Days"),
1951
- /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "30d" }, "30 Days"),
1952
- /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "90d" }, "90 Days"),
1953
- /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "1y" }, "1 Year")
1954
- ), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ import_react4.default.createElement(
1955
- import_Autocomplete2.default,
1946
+ /* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "1d" }, "1 Day"),
1947
+ /* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "7d" }, "7 Days"),
1948
+ /* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "30d" }, "30 Days"),
1949
+ /* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "90d" }, "90 Days"),
1950
+ /* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "1y" }, "1 Year")
1951
+ ), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ import_react5.default.createElement(
1952
+ import_Autocomplete.default,
1956
1953
  {
1957
1954
  multiple: true,
1958
1955
  options: availableModels,
1959
1956
  groupBy: (m) => m.mode || "other",
1960
1957
  getOptionLabel: (m) => m.model_name,
1961
1958
  value: selectedModels,
1962
- onChange: (_e, selected) => setFormData({ ...formData, models: selected.map((m) => m.model_name) }),
1963
- renderOption: (props, m) => /* @__PURE__ */ import_react4.default.createElement("li", { ...props }, modelOption(m)),
1964
- renderInput: (params) => /* @__PURE__ */ import_react4.default.createElement(
1959
+ onChange: (_e, selected) => setFormData({ models: selected.map((m) => m.model_name) }),
1960
+ renderOption: (props, m) => /* @__PURE__ */ import_react5.default.createElement("li", { ...props }, modelOption(m)),
1961
+ renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
1965
1962
  import_TextField2.default,
1966
1963
  {
1967
1964
  ...params,
@@ -1971,78 +1968,78 @@ var init_GenerateKeyDialog = __esm({
1971
1968
  }
1972
1969
  )
1973
1970
  }
1974
- ), allowUnlimitedBudget && /* @__PURE__ */ import_react4.default.createElement(
1971
+ ), allowUnlimitedBudget && /* @__PURE__ */ import_react5.default.createElement(
1975
1972
  import_FormControlLabel.default,
1976
1973
  {
1977
- control: /* @__PURE__ */ import_react4.default.createElement(
1974
+ control: /* @__PURE__ */ import_react5.default.createElement(
1978
1975
  import_Checkbox.default,
1979
1976
  {
1980
1977
  checked: unlimitedBudget,
1981
1978
  onChange: (e) => {
1982
1979
  setUnlimitedBudget(e.target.checked);
1983
1980
  if (e.target.checked) {
1984
- setFormData({ ...formData, max_budget: null });
1981
+ setFormData({ max_budget: null });
1985
1982
  } else {
1986
- setFormData({ ...formData, max_budget: 100 });
1983
+ setFormData({ max_budget: editFormState.max_budget ?? generateForm.max_budget ?? 100 });
1987
1984
  }
1988
1985
  }
1989
1986
  }
1990
1987
  ),
1991
1988
  label: "Unlimited budget"
1992
1989
  }
1993
- ), /* @__PURE__ */ import_react4.default.createElement(
1990
+ ), /* @__PURE__ */ import_react5.default.createElement(
1994
1991
  import_TextField2.default,
1995
1992
  {
1996
1993
  label: "Max Budget (USD)",
1997
1994
  type: "number",
1998
1995
  value: unlimitedBudget ? "" : formData.max_budget ?? "",
1999
- onChange: (e) => setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
1996
+ onChange: (e) => setFormData({ max_budget: e.target.value ? Number(e.target.value) : void 0 }),
2000
1997
  error: budgetInvalid,
2001
- helperText: budgetHelperText(budgetInvalid, budgetEstimate),
1998
+ helperText: budgetHelperText(budgetInvalid, budgetEstimate, unlimitedBudget),
2002
1999
  disabled: unlimitedBudget,
2003
2000
  required: true,
2004
2001
  fullWidth: true
2005
2002
  }
2006
- ), /* @__PURE__ */ import_react4.default.createElement(import_Accordion.default, { disableGutters: true, elevation: 0, sx: { border: "1px solid", borderColor: "divider", "&:before": { display: "none" } } }, /* @__PURE__ */ import_react4.default.createElement(import_AccordionSummary.default, { expandIcon: /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.ExpandMore, null) }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "body2", fontWeight: 600 }, "Advanced")), /* @__PURE__ */ import_react4.default.createElement(import_AccordionDetails.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react4.default.createElement(
2003
+ ), /* @__PURE__ */ import_react5.default.createElement(import_Accordion.default, { disableGutters: true, elevation: 0, sx: { border: "1px solid", borderColor: "divider", "&:before": { display: "none" } } }, /* @__PURE__ */ import_react5.default.createElement(import_AccordionSummary.default, { expandIcon: /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ExpandMore, null) }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", fontWeight: 600 }, "Advanced")), /* @__PURE__ */ import_react5.default.createElement(import_AccordionDetails.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react5.default.createElement(
2007
2004
  import_TextField2.default,
2008
2005
  {
2009
2006
  label: "TPM Limit",
2010
2007
  type: "number",
2011
2008
  value: formData.tpm_limit ?? "",
2012
- onChange: (e) => setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
2009
+ onChange: (e) => setFormData({ tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
2013
2010
  helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
2014
2011
  fullWidth: true
2015
2012
  }
2016
- ), /* @__PURE__ */ import_react4.default.createElement(
2013
+ ), /* @__PURE__ */ import_react5.default.createElement(
2017
2014
  import_TextField2.default,
2018
2015
  {
2019
2016
  label: "RPM Limit",
2020
2017
  type: "number",
2021
2018
  value: formData.rpm_limit ?? "",
2022
- onChange: (e) => setFormData({ ...formData, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
2019
+ onChange: (e) => setFormData({ rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
2023
2020
  helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
2024
2021
  fullWidth: true
2025
2022
  }
2026
- ))))), /* @__PURE__ */ import_react4.default.createElement(import_DialogActions2.default, null, newKeyValue ? /* @__PURE__ */ import_react4.default.createElement(import_Button3.default, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(import_Button3.default, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ import_react4.default.createElement(
2023
+ ))), renderDangerZone())), /* @__PURE__ */ import_react5.default.createElement(import_DialogActions2.default, null, newKeyValue ? /* @__PURE__ */ import_react5.default.createElement(import_Button3.default, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_Button3.default, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ import_react5.default.createElement(
2027
2024
  import_Button3.default,
2028
2025
  {
2029
- onClick: handleGenerate,
2026
+ onClick: isCreate ? handleGenerate : handleUpdate,
2030
2027
  variant: "contained",
2031
2028
  color: "primary",
2032
- disabled: !canGenerate
2029
+ disabled: !canSubmit
2033
2030
  },
2034
- submitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress2.default, { size: 24 }) : "Generate"
2031
+ submitting ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress2.default, { size: 24 }) : submitLabel
2035
2032
  ))));
2036
2033
  };
2037
2034
  }
2038
2035
  });
2039
2036
 
2040
2037
  // src/components/ManageTeamDialog.tsx
2041
- var import_react5, import_Dialog3, import_DialogTitle3, import_DialogContent3, import_DialogActions3, import_TextField3, import_Button4, import_Alert2, import_Checkbox2, import_FormControlLabel2, import_Autocomplete3, import_CircularProgress3, import_MenuItem2, import_Box5, import_Divider2, import_Typography5, import_IconButton3, import_Paper3, import_Table2, import_TableBody2, import_TableCell2, import_TableContainer2, import_TableHead2, import_TableRow2, import_icons_material4, import_core_plugin_api2, import_plugin_catalog_react, import_catalog_model, import_react_use, BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
2038
+ var import_react6, import_Dialog3, import_DialogTitle3, import_DialogContent3, import_DialogActions3, import_TextField3, import_Button4, import_Alert2, import_Checkbox2, import_FormControlLabel2, import_Autocomplete2, import_CircularProgress3, import_MenuItem2, import_Box5, import_Divider2, import_Typography5, import_IconButton3, import_Paper3, import_Table2, import_TableBody2, import_TableCell2, import_TableContainer2, import_TableHead2, import_TableRow2, import_icons_material4, import_core_plugin_api2, import_plugin_catalog_react, import_catalog_model, import_react_use, BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
2042
2039
  var init_ManageTeamDialog = __esm({
2043
2040
  "src/components/ManageTeamDialog.tsx"() {
2044
2041
  "use strict";
2045
- import_react5 = __toESM(require("react"));
2042
+ import_react6 = __toESM(require("react"));
2046
2043
  import_Dialog3 = __toESM(require("@mui/material/Dialog"));
2047
2044
  import_DialogTitle3 = __toESM(require("@mui/material/DialogTitle"));
2048
2045
  import_DialogContent3 = __toESM(require("@mui/material/DialogContent"));
@@ -2052,7 +2049,7 @@ var init_ManageTeamDialog = __esm({
2052
2049
  import_Alert2 = __toESM(require("@mui/material/Alert"));
2053
2050
  import_Checkbox2 = __toESM(require("@mui/material/Checkbox"));
2054
2051
  import_FormControlLabel2 = __toESM(require("@mui/material/FormControlLabel"));
2055
- import_Autocomplete3 = __toESM(require("@mui/material/Autocomplete"));
2052
+ import_Autocomplete2 = __toESM(require("@mui/material/Autocomplete"));
2056
2053
  import_CircularProgress3 = __toESM(require("@mui/material/CircularProgress"));
2057
2054
  import_MenuItem2 = __toESM(require("@mui/material/MenuItem"));
2058
2055
  import_Box5 = __toESM(require("@mui/material/Box"));
@@ -2099,29 +2096,29 @@ var init_ManageTeamDialog = __esm({
2099
2096
  mcpServers,
2100
2097
  onSaveMcpServers
2101
2098
  }) => {
2102
- const [alias, setAlias] = (0, import_react5.useState)("");
2103
- const [models, setModels] = (0, import_react5.useState)([]);
2104
- const [maxBudget, setMaxBudget] = (0, import_react5.useState)("");
2105
- const [unlimited, setUnlimited] = (0, import_react5.useState)(false);
2106
- const [budgetDuration, setBudgetDuration] = (0, import_react5.useState)("");
2107
- const [submitting, setSubmitting] = (0, import_react5.useState)(false);
2108
- const [error, setError] = (0, import_react5.useState)(null);
2109
- const [memberQuery, setMemberQuery] = (0, import_react5.useState)("");
2110
- const [memberRef, setMemberRef] = (0, import_react5.useState)("");
2111
- const [memberBudget, setMemberBudget] = (0, import_react5.useState)("");
2112
- const [memberBusy, setMemberBusy] = (0, import_react5.useState)(false);
2113
- const [memberError, setMemberError] = (0, import_react5.useState)(null);
2114
- const [kbIds, setKbIds] = (0, import_react5.useState)([]);
2115
- const [kbBusy, setKbBusy] = (0, import_react5.useState)(false);
2116
- const [kbError, setKbError] = (0, import_react5.useState)(null);
2117
- const [mcpIds, setMcpIds] = (0, import_react5.useState)([]);
2118
- const [mcpBusy, setMcpBusy] = (0, import_react5.useState)(false);
2119
- const [mcpError, setMcpError] = (0, import_react5.useState)(null);
2099
+ const [alias, setAlias] = (0, import_react6.useState)("");
2100
+ const [models, setModels] = (0, import_react6.useState)([]);
2101
+ const [maxBudget, setMaxBudget] = (0, import_react6.useState)("");
2102
+ const [unlimited, setUnlimited] = (0, import_react6.useState)(false);
2103
+ const [budgetDuration, setBudgetDuration] = (0, import_react6.useState)("");
2104
+ const [submitting, setSubmitting] = (0, import_react6.useState)(false);
2105
+ const [error, setError] = (0, import_react6.useState)(null);
2106
+ const [memberQuery, setMemberQuery] = (0, import_react6.useState)("");
2107
+ const [memberRef, setMemberRef] = (0, import_react6.useState)("");
2108
+ const [memberBudget, setMemberBudget] = (0, import_react6.useState)("");
2109
+ const [memberBusy, setMemberBusy] = (0, import_react6.useState)(false);
2110
+ const [memberError, setMemberError] = (0, import_react6.useState)(null);
2111
+ const [kbIds, setKbIds] = (0, import_react6.useState)([]);
2112
+ const [kbBusy, setKbBusy] = (0, import_react6.useState)(false);
2113
+ const [kbError, setKbError] = (0, import_react6.useState)(null);
2114
+ const [mcpIds, setMcpIds] = (0, import_react6.useState)([]);
2115
+ const [mcpBusy, setMcpBusy] = (0, import_react6.useState)(false);
2116
+ const [mcpError, setMcpError] = (0, import_react6.useState)(null);
2120
2117
  const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
2121
2118
  const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
2122
2119
  const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
2123
2120
  const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
2124
- const members = (0, import_react5.useMemo)(() => team?.members_with_roles ?? [], [team]);
2121
+ const members = (0, import_react6.useMemo)(() => team?.members_with_roles ?? [], [team]);
2125
2122
  const showMembers = mode === "edit" && !!canManageMembers;
2126
2123
  const catalogApi = (0, import_core_plugin_api2.useApi)(import_plugin_catalog_react.catalogApiRef);
2127
2124
  const { value: catalogUsers, loading: usersLoading } = (0, import_react_use.useAsync)(async () => {
@@ -2136,7 +2133,7 @@ var init_ManageTeamDialog = __esm({
2136
2133
  return [];
2137
2134
  }
2138
2135
  }, [open, showMembers, catalogApi]);
2139
- const userOptions = (0, import_react5.useMemo)(() => {
2136
+ const userOptions = (0, import_react6.useMemo)(() => {
2140
2137
  const existingEmails = new Set(
2141
2138
  members.map((m) => m.user_email?.toLowerCase()).filter((e) => !!e)
2142
2139
  );
@@ -2151,7 +2148,7 @@ var init_ManageTeamDialog = __esm({
2151
2148
  };
2152
2149
  }).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
2153
2150
  }, [catalogUsers, members]);
2154
- (0, import_react5.useEffect)(() => {
2151
+ (0, import_react6.useEffect)(() => {
2155
2152
  if (open) {
2156
2153
  if (mode === "edit" && team) {
2157
2154
  setAlias(team.team_alias ?? "");
@@ -2307,7 +2304,7 @@ var init_ManageTeamDialog = __esm({
2307
2304
  const showMcpServers = mode === "edit" && !!canManageMcpServers;
2308
2305
  const mcpOptions = (mcpServers ?? []).map((s) => s.id);
2309
2306
  const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
2310
- return /* @__PURE__ */ import_react5.default.createElement(import_Dialog3.default, { open, onClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react5.default.createElement(import_DialogTitle3.default, null, mode === "create" ? "Create Team" : "Edit Team"), /* @__PURE__ */ import_react5.default.createElement(import_DialogContent3.default, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 } }, error && /* @__PURE__ */ import_react5.default.createElement(import_Alert2.default, { severity: "error" }, error), /* @__PURE__ */ import_react5.default.createElement(
2307
+ return /* @__PURE__ */ import_react6.default.createElement(import_Dialog3.default, { open, onClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react6.default.createElement(import_DialogTitle3.default, null, mode === "create" ? "Create Team" : "Edit Team"), /* @__PURE__ */ import_react6.default.createElement(import_DialogContent3.default, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 } }, error && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error" }, error), /* @__PURE__ */ import_react6.default.createElement(
2311
2308
  import_TextField3.default,
2312
2309
  {
2313
2310
  label: "Team Alias",
@@ -2316,15 +2313,15 @@ var init_ManageTeamDialog = __esm({
2316
2313
  disabled: submitting,
2317
2314
  fullWidth: true
2318
2315
  }
2319
- ), /* @__PURE__ */ import_react5.default.createElement(
2320
- import_Autocomplete3.default,
2316
+ ), /* @__PURE__ */ import_react6.default.createElement(
2317
+ import_Autocomplete2.default,
2321
2318
  {
2322
2319
  multiple: true,
2323
2320
  options: modelNames,
2324
2321
  value: models,
2325
2322
  onChange: (_, newValue) => setModels(newValue),
2326
2323
  disabled: submitting,
2327
- renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
2324
+ renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(
2328
2325
  import_TextField3.default,
2329
2326
  {
2330
2327
  ...params,
@@ -2334,7 +2331,7 @@ var init_ManageTeamDialog = __esm({
2334
2331
  }
2335
2332
  )
2336
2333
  }
2337
- ), /* @__PURE__ */ import_react5.default.createElement(
2334
+ ), /* @__PURE__ */ import_react6.default.createElement(
2338
2335
  import_TextField3.default,
2339
2336
  {
2340
2337
  label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
@@ -2346,13 +2343,13 @@ var init_ManageTeamDialog = __esm({
2346
2343
  helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
2347
2344
  fullWidth: true
2348
2345
  }
2349
- ), allowUnlimitedBudget && /* @__PURE__ */ import_react5.default.createElement(
2346
+ ), allowUnlimitedBudget && /* @__PURE__ */ import_react6.default.createElement(
2350
2347
  import_FormControlLabel2.default,
2351
2348
  {
2352
- control: /* @__PURE__ */ import_react5.default.createElement(import_Checkbox2.default, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
2349
+ control: /* @__PURE__ */ import_react6.default.createElement(import_Checkbox2.default, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
2353
2350
  label: "Unlimited Budget"
2354
2351
  }
2355
- ), /* @__PURE__ */ import_react5.default.createElement(
2352
+ ), /* @__PURE__ */ import_react6.default.createElement(
2356
2353
  import_TextField3.default,
2357
2354
  {
2358
2355
  select: true,
@@ -2363,8 +2360,8 @@ var init_ManageTeamDialog = __esm({
2363
2360
  helperText: "Spend-reset period for the team budget",
2364
2361
  fullWidth: true
2365
2362
  },
2366
- durationOptions.map((o) => /* @__PURE__ */ import_react5.default.createElement(import_MenuItem2.default, { key: o.value, value: o.value }, o.label))
2367
- ), showMembers && /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ import_react5.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "No members yet.") : /* @__PURE__ */ import_react5.default.createElement(import_TableContainer2.default, { component: import_Paper3.default, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 } }, /* @__PURE__ */ import_react5.default.createElement(import_Table2.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react5.default.createElement(import_TableHead2.default, null, /* @__PURE__ */ import_react5.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, null, "User"), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right" }, "Role"), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right", sx: { width: 40 } }))), /* @__PURE__ */ import_react5.default.createElement(import_TableBody2.default, null, members.map((m) => /* @__PURE__ */ import_react5.default.createElement(import_TableRow2.default, { key: m.user_id }, /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, null, m.user_email ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react5.default.createElement(
2363
+ durationOptions.map((o) => /* @__PURE__ */ import_react6.default.createElement(import_MenuItem2.default, { key: o.value, value: o.value }, o.label))
2364
+ ), showMembers && /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "No members yet.") : /* @__PURE__ */ import_react6.default.createElement(import_TableContainer2.default, { component: import_Paper3.default, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 } }, /* @__PURE__ */ import_react6.default.createElement(import_Table2.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react6.default.createElement(import_TableHead2.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, null, "User"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right" }, "Role"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right", sx: { width: 40 } }))), /* @__PURE__ */ import_react6.default.createElement(import_TableBody2.default, null, members.map((m) => /* @__PURE__ */ import_react6.default.createElement(import_TableRow2.default, { key: m.user_id }, /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, null, m.user_email ? /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react6.default.createElement(
2368
2365
  import_Typography5.default,
2369
2366
  {
2370
2367
  variant: "caption",
@@ -2372,14 +2369,14 @@ var init_ManageTeamDialog = __esm({
2372
2369
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2373
2370
  },
2374
2371
  m.user_id
2375
- )) : /* @__PURE__ */ import_react5.default.createElement(
2372
+ )) : /* @__PURE__ */ import_react6.default.createElement(
2376
2373
  import_Typography5.default,
2377
2374
  {
2378
2375
  variant: "body2",
2379
2376
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2380
2377
  },
2381
2378
  m.user_id
2382
- )), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react5.default.createElement(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react5.default.createElement(
2379
+ )), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react6.default.createElement(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react6.default.createElement(
2383
2380
  import_IconButton3.default,
2384
2381
  {
2385
2382
  edge: "end",
@@ -2388,9 +2385,9 @@ var init_ManageTeamDialog = __esm({
2388
2385
  disabled: memberBusy,
2389
2386
  onClick: () => handleRemoveMember(m.user_id)
2390
2387
  },
2391
- /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
2392
- ))))))), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ import_react5.default.createElement(
2393
- import_Autocomplete3.default,
2388
+ /* @__PURE__ */ import_react6.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
2389
+ ))))))), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ import_react6.default.createElement(
2390
+ import_Autocomplete2.default,
2394
2391
  {
2395
2392
  freeSolo: true,
2396
2393
  autoHighlight: true,
@@ -2413,7 +2410,7 @@ var init_ManageTeamDialog = __esm({
2413
2410
  isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
2414
2411
  disabled: memberBusy,
2415
2412
  sx: { flex: 1, minWidth: 260 },
2416
- renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
2413
+ renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(
2417
2414
  import_TextField3.default,
2418
2415
  {
2419
2416
  ...params,
@@ -2423,12 +2420,12 @@ var init_ManageTeamDialog = __esm({
2423
2420
  helperText: "Pick a catalog user, or paste a user entity ref",
2424
2421
  InputProps: {
2425
2422
  ...params.InputProps,
2426
- endAdornment: /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, usersLoading ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress3.default, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
2423
+ endAdornment: /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, usersLoading ? /* @__PURE__ */ import_react6.default.createElement(import_CircularProgress3.default, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
2427
2424
  }
2428
2425
  }
2429
2426
  )
2430
2427
  }
2431
- ), /* @__PURE__ */ import_react5.default.createElement(
2428
+ ), /* @__PURE__ */ import_react6.default.createElement(
2432
2429
  import_TextField3.default,
2433
2430
  {
2434
2431
  label: "Max budget in team ($)",
@@ -2439,7 +2436,7 @@ var init_ManageTeamDialog = __esm({
2439
2436
  size: "small",
2440
2437
  sx: { width: 160 }
2441
2438
  }
2442
- ), /* @__PURE__ */ import_react5.default.createElement(
2439
+ ), /* @__PURE__ */ import_react6.default.createElement(
2443
2440
  import_Button4.default,
2444
2441
  {
2445
2442
  onClick: handleAddMember,
@@ -2448,8 +2445,8 @@ var init_ManageTeamDialog = __esm({
2448
2445
  sx: { mt: 0.5 }
2449
2446
  },
2450
2447
  "Add"
2451
- ))), showKnowledgeBases && /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Knowledge Bases"), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching a knowledge base exposes its documents to every key in this team."), kbError && /* @__PURE__ */ import_react5.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setKbError(null) }, kbError), /* @__PURE__ */ import_react5.default.createElement(
2452
- import_Autocomplete3.default,
2448
+ ))), showKnowledgeBases && /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Knowledge Bases"), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching a knowledge base exposes its documents to every key in this team."), kbError && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setKbError(null) }, kbError), /* @__PURE__ */ import_react6.default.createElement(
2449
+ import_Autocomplete2.default,
2453
2450
  {
2454
2451
  multiple: true,
2455
2452
  options: kbOptions,
@@ -2457,9 +2454,9 @@ var init_ManageTeamDialog = __esm({
2457
2454
  getOptionLabel: kbLabel,
2458
2455
  onChange: (_, v) => setKbIds(v),
2459
2456
  disabled: kbBusy,
2460
- renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(import_TextField3.default, { ...params, label: "Attached knowledge bases" })
2457
+ renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(import_TextField3.default, { ...params, label: "Attached knowledge bases" })
2461
2458
  }
2462
- ), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react5.default.createElement(
2459
+ ), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
2463
2460
  import_Button4.default,
2464
2461
  {
2465
2462
  onClick: handleSaveKnowledgeBases,
@@ -2467,8 +2464,8 @@ var init_ManageTeamDialog = __esm({
2467
2464
  variant: "outlined"
2468
2465
  },
2469
2466
  kbBusy ? "Saving\u2026" : "Save knowledge bases"
2470
- ))), showMcpServers && /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "MCP Servers"), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching an MCP server lets every key in this team invoke that server's tools through the model \u2014 including any side effects those tools have."), mcpError && /* @__PURE__ */ import_react5.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMcpError(null) }, mcpError), /* @__PURE__ */ import_react5.default.createElement(
2471
- import_Autocomplete3.default,
2467
+ ))), showMcpServers && /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "MCP Servers"), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching an MCP server lets every key in this team invoke that server's tools through the model \u2014 including any side effects those tools have."), mcpError && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMcpError(null) }, mcpError), /* @__PURE__ */ import_react6.default.createElement(
2468
+ import_Autocomplete2.default,
2472
2469
  {
2473
2470
  multiple: true,
2474
2471
  options: mcpOptions,
@@ -2476,9 +2473,9 @@ var init_ManageTeamDialog = __esm({
2476
2473
  getOptionLabel: mcpLabel,
2477
2474
  onChange: (_, v) => setMcpIds(v),
2478
2475
  disabled: mcpBusy,
2479
- renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(import_TextField3.default, { ...params, label: "Attached MCP servers" })
2476
+ renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(import_TextField3.default, { ...params, label: "Attached MCP servers" })
2480
2477
  }
2481
- ), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react5.default.createElement(
2478
+ ), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
2482
2479
  import_Button4.default,
2483
2480
  {
2484
2481
  onClick: handleSaveMcpServers,
@@ -2486,7 +2483,7 @@ var init_ManageTeamDialog = __esm({
2486
2483
  variant: "outlined"
2487
2484
  },
2488
2485
  mcpBusy ? "Saving\u2026" : "Save MCP servers"
2489
- )))), /* @__PURE__ */ import_react5.default.createElement(import_DialogActions3.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Button4.default, { onClick: onClose, disabled: submitting }, "Cancel"), /* @__PURE__ */ import_react5.default.createElement(import_Button4.default, { onClick: handleSubmit, disabled: submitting, variant: "contained" }, submitting ? "Saving..." : "Save")));
2486
+ )))), /* @__PURE__ */ import_react6.default.createElement(import_DialogActions3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Button4.default, { onClick: onClose, disabled: submitting }, "Cancel"), /* @__PURE__ */ import_react6.default.createElement(import_Button4.default, { onClick: handleSubmit, disabled: submitting, variant: "contained" }, submitting ? "Saving..." : "Save")));
2490
2487
  };
2491
2488
  }
2492
2489
  });
@@ -2497,11 +2494,11 @@ function rateTone(rate) {
2497
2494
  if (rate >= 0.9) return "warning";
2498
2495
  return "danger";
2499
2496
  }
2500
- var import_react6, import_Paper4, import_Box6, import_Typography6, import_TextField4, import_MenuItem3, import_Grid, import_Table3, import_TableBody3, import_TableCell3, import_TableContainer3, import_TableHead3, import_TableRow3, import_Skeleton3, import_recharts, TOP_N_MODELS, PERIOD_LS_KEY, PRESET_LABELS, fmtPct, ChartSkeleton, ChartOrFallback, SuccessRateCell, UsageStats;
2497
+ var import_react7, import_Paper4, import_Box6, import_Typography6, import_TextField4, import_MenuItem3, import_Grid, import_Table3, import_TableBody3, import_TableCell3, import_TableContainer3, import_TableHead3, import_TableRow3, import_Skeleton3, import_recharts, TOP_N_MODELS, PERIOD_LS_KEY, PRESET_LABELS, fmtPct, ChartSkeleton, ChartOrFallback, SuccessRateCell, UsageStats;
2501
2498
  var init_UsageStats = __esm({
2502
2499
  "src/components/UsageStats.tsx"() {
2503
2500
  "use strict";
2504
- import_react6 = __toESM(require("react"));
2501
+ import_react7 = __toESM(require("react"));
2505
2502
  import_Paper4 = __toESM(require("@mui/material/Paper"));
2506
2503
  import_Box6 = __toESM(require("@mui/material/Box"));
2507
2504
  import_Typography6 = __toESM(require("@mui/material/Typography"));
@@ -2527,16 +2524,16 @@ var init_UsageStats = __esm({
2527
2524
  "30d": "Last 30 days"
2528
2525
  };
2529
2526
  fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
2530
- ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ import_react6.default.createElement(import_Skeleton3.default, { variant: "rounded", height });
2527
+ ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ import_react7.default.createElement(import_Skeleton3.default, { variant: "rounded", height });
2531
2528
  ChartOrFallback = ({ loading, empty, height = 240, children }) => {
2532
- if (loading) return /* @__PURE__ */ import_react6.default.createElement(ChartSkeleton, { height });
2533
- if (empty) return /* @__PURE__ */ import_react6.default.createElement(EmptyState, { message: "No data for this period", height });
2534
- return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, children);
2529
+ if (loading) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { height });
2530
+ if (empty) return /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "No data for this period", height });
2531
+ return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, children);
2535
2532
  };
2536
2533
  SuccessRateCell = ({ rate, requests }) => {
2537
- if (requests === 0) return /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "body2", color: "text.secondary" }, "\u2014");
2534
+ if (requests === 0) return /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "body2", color: "text.secondary" }, "\u2014");
2538
2535
  const tone = rateTone(rate);
2539
- return /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { width: 72, flexShrink: 0 }, /* @__PURE__ */ import_react6.default.createElement(Meter, { value: rate * 100, tone, height: 4 })), /* @__PURE__ */ import_react6.default.createElement(
2536
+ return /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { width: 72, flexShrink: 0 }, /* @__PURE__ */ import_react7.default.createElement(Meter, { value: rate * 100, tone, height: 4 })), /* @__PURE__ */ import_react7.default.createElement(
2540
2537
  import_Typography6.default,
2541
2538
  {
2542
2539
  variant: "caption",
@@ -2555,8 +2552,8 @@ var init_UsageStats = __esm({
2555
2552
  userInfo
2556
2553
  }) => {
2557
2554
  const chart = useChartTheme();
2558
- const [selectedModel, setSelectedModel] = (0, import_react6.useState)("all");
2559
- const [tab, setTab] = (0, import_react6.useState)("costs");
2555
+ const [selectedModel, setSelectedModel] = (0, import_react7.useState)("all");
2556
+ const [tab, setTab] = (0, import_react7.useState)("costs");
2560
2557
  const handlePresetChange = (preset) => {
2561
2558
  const end = /* @__PURE__ */ new Date();
2562
2559
  const start = /* @__PURE__ */ new Date();
@@ -2570,7 +2567,7 @@ var init_UsageStats = __esm({
2570
2567
  }
2571
2568
  onDateRangeChange({ start, end }, preset);
2572
2569
  };
2573
- const dailyData = (0, import_react6.useMemo)(
2570
+ const dailyData = (0, import_react7.useMemo)(
2574
2571
  () => (usage?.daily_usage ?? []).map((d) => ({
2575
2572
  date: d.date,
2576
2573
  spend: d.spend,
@@ -2583,21 +2580,21 @@ var init_UsageStats = __esm({
2583
2580
  })),
2584
2581
  [usage]
2585
2582
  );
2586
- const cumulativeData = (0, import_react6.useMemo)(() => {
2583
+ const cumulativeData = (0, import_react7.useMemo)(() => {
2587
2584
  let cum = 0;
2588
2585
  return dailyData.map((d) => {
2589
2586
  cum += d.spend;
2590
2587
  return { date: d.date, cumulative: cum };
2591
2588
  });
2592
2589
  }, [dailyData]);
2593
- const successRateData = (0, import_react6.useMemo)(
2590
+ const successRateData = (0, import_react7.useMemo)(
2594
2591
  () => dailyData.filter((d) => d.apiRequests > 0).map((d) => ({
2595
2592
  date: d.date,
2596
2593
  successRate: parseFloat((d.successfulRequests / d.apiRequests * 100).toFixed(1))
2597
2594
  })),
2598
2595
  [dailyData]
2599
2596
  );
2600
- const { modelSpendByDate, topModels: topSpendModels } = (0, import_react6.useMemo)(() => {
2597
+ const { modelSpendByDate, topModels: topSpendModels } = (0, import_react7.useMemo)(() => {
2601
2598
  const rows = usage?.daily_by_model ?? [];
2602
2599
  const modelTotals = {};
2603
2600
  for (const r of rows) modelTotals[r.model] = (modelTotals[r.model] ?? 0) + r.spend;
@@ -2612,7 +2609,7 @@ var init_UsageStats = __esm({
2612
2609
  const hasOther = sorted.some((r) => r.Other > 0);
2613
2610
  return { modelSpendByDate: sorted, topModels: hasOther ? [...top, "Other"] : top };
2614
2611
  }, [usage]);
2615
- const modelRows = (0, import_react6.useMemo)(() => {
2612
+ const modelRows = (0, import_react7.useMemo)(() => {
2616
2613
  const entries = Object.entries(usage?.usage_by_model ?? {}).map(([model, d]) => ({
2617
2614
  model,
2618
2615
  spend: d.total_spend,
@@ -2626,11 +2623,11 @@ var init_UsageStats = __esm({
2626
2623
  }));
2627
2624
  return selectedModel === "all" ? entries : entries.filter((e) => e.model === selectedModel);
2628
2625
  }, [usage, selectedModel]);
2629
- const topModelSpendBars = (0, import_react6.useMemo)(
2626
+ const topModelSpendBars = (0, import_react7.useMemo)(
2630
2627
  () => [...modelRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
2631
2628
  [modelRows]
2632
2629
  );
2633
- const keyRows = (0, import_react6.useMemo)(
2630
+ const keyRows = (0, import_react7.useMemo)(
2634
2631
  () => Object.entries(usage?.usage_by_key ?? {}).map(([keyHash, d]) => ({
2635
2632
  keyHash,
2636
2633
  keyAlias: d.key_alias ?? keyHash.slice(0, 8),
@@ -2647,7 +2644,7 @@ var init_UsageStats = __esm({
2647
2644
  })),
2648
2645
  [usage]
2649
2646
  );
2650
- const topKeySpendBars = (0, import_react6.useMemo)(
2647
+ const topKeySpendBars = (0, import_react7.useMemo)(
2651
2648
  () => [...keyRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
2652
2649
  [keyRows]
2653
2650
  );
@@ -2685,23 +2682,23 @@ var init_UsageStats = __esm({
2685
2682
  ];
2686
2683
  const renderKeyRows = () => {
2687
2684
  if (loading) {
2688
- return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ import_react6.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
2685
+ return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ import_react7.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
2689
2686
  }
2690
2687
  if (keyRows.length === 0) {
2691
- return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 7 }, /* @__PURE__ */ import_react6.default.createElement(EmptyState, { message: "No key activity in this period" })));
2688
+ return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 7 }, /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "No key activity in this period" })));
2692
2689
  }
2693
- return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, { key: r.keyHash }, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ import_react6.default.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ import_react6.default.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2690
+ return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, { key: r.keyHash }, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ import_react7.default.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ import_react7.default.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2694
2691
  };
2695
2692
  const renderModelRows = () => {
2696
2693
  if (loading) {
2697
- return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ import_react6.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
2694
+ return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ import_react7.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
2698
2695
  }
2699
2696
  if (modelRows.length === 0) {
2700
- return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 8 }, /* @__PURE__ */ import_react6.default.createElement(EmptyState, { message: "No model activity in this period" })));
2697
+ return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 8 }, /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "No model activity in this period" })));
2701
2698
  }
2702
- return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, { key: r.model }, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2699
+ return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, { key: r.model }, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2703
2700
  };
2704
- const periodSelect = /* @__PURE__ */ import_react6.default.createElement(
2701
+ const periodSelect = /* @__PURE__ */ import_react7.default.createElement(
2705
2702
  import_TextField4.default,
2706
2703
  {
2707
2704
  select: true,
@@ -2711,17 +2708,17 @@ var init_UsageStats = __esm({
2711
2708
  onChange: (e) => handlePresetChange(e.target.value),
2712
2709
  sx: { minWidth: 160 }
2713
2710
  },
2714
- Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { key: p, value: p }, PRESET_LABELS[p]))
2711
+ Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { key: p, value: p }, PRESET_LABELS[p]))
2715
2712
  );
2716
- return /* @__PURE__ */ import_react6.default.createElement(
2713
+ return /* @__PURE__ */ import_react7.default.createElement(
2717
2714
  SectionCard,
2718
2715
  {
2719
2716
  title: "Usage Analytics",
2720
2717
  subtitle: `${PRESET_LABELS[currentPreset]} \xB7 ${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
2721
2718
  actions: periodSelect
2722
2719
  },
2723
- /* @__PURE__ */ import_react6.default.createElement(MetricStrip, { metrics }),
2724
- /* @__PURE__ */ import_react6.default.createElement(
2720
+ /* @__PURE__ */ import_react7.default.createElement(MetricStrip, { metrics }),
2721
+ /* @__PURE__ */ import_react7.default.createElement(
2725
2722
  import_Box6.default,
2726
2723
  {
2727
2724
  sx: {
@@ -2734,7 +2731,7 @@ var init_UsageStats = __esm({
2734
2731
  mb: 2
2735
2732
  }
2736
2733
  },
2737
- /* @__PURE__ */ import_react6.default.createElement(
2734
+ /* @__PURE__ */ import_react7.default.createElement(
2738
2735
  SegmentedControl,
2739
2736
  {
2740
2737
  value: tab,
@@ -2746,7 +2743,7 @@ var init_UsageStats = __esm({
2746
2743
  ]
2747
2744
  }
2748
2745
  ),
2749
- tab === "models" && /* @__PURE__ */ import_react6.default.createElement(
2746
+ tab === "models" && /* @__PURE__ */ import_react7.default.createElement(
2750
2747
  import_TextField4.default,
2751
2748
  {
2752
2749
  select: true,
@@ -2756,17 +2753,17 @@ var init_UsageStats = __esm({
2756
2753
  onChange: (e) => setSelectedModel(e.target.value),
2757
2754
  sx: { minWidth: 220 }
2758
2755
  },
2759
- /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "all" }, "All models"),
2760
- models.map((m) => /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { key: m.model_name, value: m.model_name }, m.model_name))
2756
+ /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "all" }, "All models"),
2757
+ models.map((m) => /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { key: m.model_name, value: m.model_name }, m.model_name))
2761
2758
  )
2762
2759
  ),
2763
- tab === "costs" && /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { container: true, spacing: 2 }, /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ import_react6.default.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(
2760
+ tab === "costs" && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { container: true, spacing: 2 }, /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(
2764
2761
  import_recharts.Tooltip,
2765
2762
  {
2766
2763
  cursor: chart.cursor,
2767
- content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
2764
+ content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
2768
2765
  }
2769
- ), topSpendModels.map((m) => /* @__PURE__ */ import_react6.default.createElement(
2766
+ ), topSpendModels.map((m) => /* @__PURE__ */ import_react7.default.createElement(
2770
2767
  import_recharts.Area,
2771
2768
  {
2772
2769
  key: m,
@@ -2778,13 +2775,13 @@ var init_UsageStats = __esm({
2778
2775
  fill: chartColor(m),
2779
2776
  fillOpacity: 0.28
2780
2777
  }
2781
- ))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */ import_react6.default.createElement(SeriesLegend, { series: topSpendModels.map((m) => ({ name: m, color: chartColor(m) })) }))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Daily token usage" }, /* @__PURE__ */ import_react6.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })))))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Daily requests" }, /* @__PURE__ */ import_react6.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.BarChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 }, barCategoryGap: "30%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })))))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Daily success rate" }, /* @__PURE__ */ import_react6.default.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.LineChart, { data: successRateData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(
2778
+ ))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */ import_react7.default.createElement(SeriesLegend, { series: topSpendModels.map((m) => ({ name: m, color: chartColor(m) })) }))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily token usage" }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })))))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily requests" }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 }, barCategoryGap: "30%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })))))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily success rate" }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.LineChart, { data: successRateData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(
2782
2779
  import_recharts.Tooltip,
2783
2780
  {
2784
2781
  cursor: chart.cursor,
2785
- content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
2782
+ content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
2786
2783
  }
2787
- ), /* @__PURE__ */ import_react6.default.createElement(import_recharts.ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ import_react6.default.createElement(
2784
+ ), /* @__PURE__ */ import_react7.default.createElement(import_recharts.ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ import_react7.default.createElement(
2788
2785
  import_recharts.Line,
2789
2786
  {
2790
2787
  type: "monotone",
@@ -2795,13 +2792,13 @@ var init_UsageStats = __esm({
2795
2792
  dot: { r: 2.5, strokeWidth: 0, fill: SERIES.input },
2796
2793
  activeDot: { r: 4 }
2797
2794
  }
2798
- )))))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react6.default.createElement(
2795
+ )))))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(
2799
2796
  ChartCard,
2800
2797
  {
2801
2798
  title: maxBudget > 0 ? "Cumulative spend vs budget" : "Cumulative spend",
2802
2799
  meta: maxBudget > 0 ? `${fmtUsd(totalCumSpend)} used \xB7 ${fmtUsd(Math.max(0, maxBudget - totalCumSpend))} left` : void 0
2803
2800
  },
2804
- /* @__PURE__ */ import_react6.default.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0 }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.AreaChart, { data: cumulativeData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(
2801
+ /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.AreaChart, { data: cumulativeData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(
2805
2802
  import_recharts.YAxis,
2806
2803
  {
2807
2804
  tickFormatter: fmtUsdCompact,
@@ -2809,13 +2806,13 @@ var init_UsageStats = __esm({
2809
2806
  domain: cumulativeDomain,
2810
2807
  ...chart.axis
2811
2808
  }
2812
- ), /* @__PURE__ */ import_react6.default.createElement(
2809
+ ), /* @__PURE__ */ import_react7.default.createElement(
2813
2810
  import_recharts.Tooltip,
2814
2811
  {
2815
2812
  cursor: chart.cursor,
2816
- content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtUsd })
2813
+ content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd })
2817
2814
  }
2818
- ), maxBudget > 0 && /* @__PURE__ */ import_react6.default.createElement(
2815
+ ), maxBudget > 0 && /* @__PURE__ */ import_react7.default.createElement(
2819
2816
  import_recharts.ReferenceLine,
2820
2817
  {
2821
2818
  y: maxBudget,
@@ -2823,7 +2820,7 @@ var init_UsageStats = __esm({
2823
2820
  strokeDasharray: "5 4",
2824
2821
  label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 10, fill: SERIES.budget }
2825
2822
  }
2826
- ), /* @__PURE__ */ import_react6.default.createElement(
2823
+ ), /* @__PURE__ */ import_react7.default.createElement(
2827
2824
  import_recharts.Area,
2828
2825
  {
2829
2826
  type: "monotone",
@@ -2836,32 +2833,32 @@ var init_UsageStats = __esm({
2836
2833
  }
2837
2834
  ))))
2838
2835
  ))),
2839
- tab === "models" && /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { container: true, spacing: 2 }, /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Spend by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ import_react6.default.createElement(
2836
+ tab === "models" && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { container: true, spacing: 2 }, /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Spend by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ import_react7.default.createElement(
2840
2837
  ChartOrFallback,
2841
2838
  {
2842
2839
  loading,
2843
2840
  empty: topModelSpendBars.length === 0,
2844
2841
  height: Math.max(200, topModelSpendBars.length * 34)
2845
2842
  },
2846
- /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", radius: [0, 3, 3, 0], barSize: 14 }, topModelSpendBars.map((r) => /* @__PURE__ */ import_react6.default.createElement(import_recharts.Cell, { key: r.model, fill: chartColor(r.model) })))))
2847
- ))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Tokens by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ import_react6.default.createElement(
2843
+ /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", radius: [0, 3, 3, 0], barSize: 14 }, topModelSpendBars.map((r) => /* @__PURE__ */ import_react7.default.createElement(import_recharts.Cell, { key: r.model, fill: chartColor(r.model) })))))
2844
+ ))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Tokens by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ import_react7.default.createElement(
2848
2845
  ChartOrFallback,
2849
2846
  {
2850
2847
  loading,
2851
2848
  empty: modelRows.length === 0,
2852
2849
  height: Math.max(200, topModelSpendBars.length * 34)
2853
2850
  },
2854
- /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
2855
- ))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react6.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react6.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, "Model"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Success"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Input"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Output"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react6.default.createElement(import_TableBody3.default, null, renderModelRows()))))),
2856
- tab === "keys" && /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ import_react6.default.createElement(
2851
+ /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
2852
+ ))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react7.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, "Model"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Success"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Input"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Output"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react7.default.createElement(import_TableBody3.default, null, renderModelRows()))))),
2853
+ tab === "keys" && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ import_react7.default.createElement(
2857
2854
  ChartOrFallback,
2858
2855
  {
2859
2856
  loading,
2860
2857
  empty: topKeySpendBars.length === 0,
2861
2858
  height: Math.max(160, topKeySpendBars.length * 32)
2862
2859
  },
2863
- /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
2864
- ))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react6.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react6.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, "Key"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, "Models"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Tokens"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react6.default.createElement(import_TableBody3.default, null, renderKeyRows())))))
2860
+ /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
2861
+ ))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react7.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, "Key"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, "Models"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Tokens"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react7.default.createElement(import_TableBody3.default, null, renderKeyRows())))))
2865
2862
  );
2866
2863
  };
2867
2864
  }
@@ -3003,16 +3000,16 @@ function levelToneColor(tone, theme) {
3003
3000
  return theme.palette.primary.main;
3004
3001
  }
3005
3002
  }
3006
- var import_react7, import_Box7, import_Paper5, import_Typography7, import_styles4, import_core_components, LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
3003
+ var import_react8, import_Box7, import_Paper5, import_Typography7, import_styles4, import_core_components2, LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
3007
3004
  var init_BudgetLimitList = __esm({
3008
3005
  "src/components/BudgetLimitList.tsx"() {
3009
3006
  "use strict";
3010
- import_react7 = __toESM(require("react"));
3007
+ import_react8 = __toESM(require("react"));
3011
3008
  import_Box7 = __toESM(require("@mui/material/Box"));
3012
3009
  import_Paper5 = __toESM(require("@mui/material/Paper"));
3013
3010
  import_Typography7 = __toESM(require("@mui/material/Typography"));
3014
3011
  import_styles4 = require("@mui/material/styles");
3015
- import_core_components = require("@backstage/core-components");
3012
+ import_core_components2 = require("@backstage/core-components");
3016
3013
  init_budget();
3017
3014
  init_format();
3018
3015
  init_ui();
@@ -3020,7 +3017,7 @@ var init_BudgetLimitList = __esm({
3020
3017
  const tone = budgetTone2(limit.pct);
3021
3018
  const pct = Math.round(limit.pct);
3022
3019
  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(
3020
+ return /* @__PURE__ */ import_react8.default.createElement(import_Paper5.default, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react8.default.createElement(
3024
3021
  import_Typography7.default,
3025
3022
  {
3026
3023
  variant: "body2",
@@ -3028,7 +3025,7 @@ var init_BudgetLimitList = __esm({
3028
3025
  title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
3029
3026
  },
3030
3027
  limit.label
3031
- ), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
3028
+ ), limit.sublabel && /* @__PURE__ */ import_react8.default.createElement(
3032
3029
  import_Typography7.default,
3033
3030
  {
3034
3031
  variant: "caption",
@@ -3043,7 +3040,7 @@ var init_BudgetLimitList = __esm({
3043
3040
  }
3044
3041
  },
3045
3042
  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(
3043
+ )), limit.hidden ? /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { mt: 1 } }, /* @__PURE__ */ import_react8.default.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ import_react8.default.createElement(
3047
3044
  import_Box7.default,
3048
3045
  {
3049
3046
  sx: {
@@ -3054,14 +3051,14 @@ var init_BudgetLimitList = __esm({
3054
3051
  mt: 0.75
3055
3052
  }
3056
3053
  },
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")
3054
+ /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, "Hidden by admin") : /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
3055
+ /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3059
3056
  ));
3060
3057
  };
3061
3058
  TaggedLimit = ({ limit }) => {
3062
3059
  const theme = (0, import_styles4.useTheme)();
3063
3060
  const tone = budgetTone2(limit.pct);
3064
- return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
3061
+ return /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react8.default.createElement(
3065
3062
  import_Typography7.default,
3066
3063
  {
3067
3064
  sx: {
@@ -3074,7 +3071,7 @@ var init_BudgetLimitList = __esm({
3074
3071
  }
3075
3072
  },
3076
3073
  limit.kind
3077
- ), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
3074
+ ), /* @__PURE__ */ import_react8.default.createElement(LimitCard, { limit }));
3078
3075
  };
3079
3076
  MoreKeysNote = ({
3080
3077
  count,
@@ -3083,12 +3080,12 @@ var init_BudgetLimitList = __esm({
3083
3080
  }) => {
3084
3081
  const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
3085
3082
  if (!href) {
3086
- return /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, text);
3083
+ return /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, text);
3087
3084
  }
3088
- return /* @__PURE__ */ import_react7.default.createElement(import_core_components.Link, { to: href, variant: "caption" }, text, " \u2192");
3085
+ return /* @__PURE__ */ import_react8.default.createElement(import_core_components2.Link, { to: href, variant: "caption" }, text, " \u2192");
3089
3086
  };
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(
3087
+ BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, limits.map((limit) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: `${limit.kind}:${limit.sublabel ?? limit.label}`, limit })), hiddenKeyCount > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: hiddenKeyCount, href: keysHref }));
3088
+ LimitListPanel = ({ children }) => /* @__PURE__ */ import_react8.default.createElement(
3092
3089
  import_Box7.default,
3093
3090
  {
3094
3091
  sx: (theme) => ({
@@ -3104,11 +3101,11 @@ var init_BudgetLimitList = __esm({
3104
3101
  });
3105
3102
 
3106
3103
  // 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;
3104
+ var import_react9, 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
3105
  var init_LiteLLMBudgetWidget = __esm({
3109
3106
  "src/components/LiteLLMBudgetWidget.tsx"() {
3110
3107
  "use strict";
3111
- import_react8 = __toESM(require("react"));
3108
+ import_react9 = __toESM(require("react"));
3112
3109
  import_Paper6 = __toESM(require("@mui/material/Paper"));
3113
3110
  import_Box8 = __toESM(require("@mui/material/Box"));
3114
3111
  import_Typography8 = __toESM(require("@mui/material/Typography"));
@@ -3131,7 +3128,7 @@ var init_LiteLLMBudgetWidget = __esm({
3131
3128
  tone,
3132
3129
  isLast,
3133
3130
  children
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(
3131
+ }) => /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ import_react9.default.createElement(
3135
3132
  import_Box8.default,
3136
3133
  {
3137
3134
  sx: (theme) => ({
@@ -3149,7 +3146,7 @@ var init_LiteLLMBudgetWidget = __esm({
3149
3146
  })
3150
3147
  },
3151
3148
  rank
3152
- ), /* @__PURE__ */ import_react8.default.createElement(
3149
+ ), /* @__PURE__ */ import_react9.default.createElement(
3153
3150
  import_Box8.default,
3154
3151
  {
3155
3152
  sx: (theme) => ({
@@ -3159,7 +3156,7 @@ var init_LiteLLMBudgetWidget = __esm({
3159
3156
  bgcolor: isLast ? "transparent" : (0, import_styles5.alpha)(theme.palette.text.primary, 0.12)
3160
3157
  })
3161
3158
  }
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(
3159
+ )), /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ import_react9.default.createElement(
3163
3160
  import_Typography8.default,
3164
3161
  {
3165
3162
  sx: (theme) => ({
@@ -3171,8 +3168,8 @@ var init_LiteLLMBudgetWidget = __esm({
3171
3168
  })
3172
3169
  },
3173
3170
  name
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(
3171
+ ), /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
3172
+ EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react9.default.createElement(
3176
3173
  import_Paper6.default,
3177
3174
  {
3178
3175
  variant: "outlined",
@@ -3183,7 +3180,7 @@ var init_LiteLLMBudgetWidget = __esm({
3183
3180
  bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
3184
3181
  })
3185
3182
  },
3186
- /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3183
+ /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3187
3184
  );
3188
3185
  CLOSEST_LEVEL_LABEL = {
3189
3186
  key: "key",
@@ -3191,7 +3188,7 @@ var init_LiteLLMBudgetWidget = __esm({
3191
3188
  team: "team"
3192
3189
  };
3193
3190
  KEYS_PATH = "/litellm?tab=keys";
3194
- HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react8.default.createElement(
3191
+ HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react9.default.createElement(
3195
3192
  import_Box8.default,
3196
3193
  {
3197
3194
  sx: (theme) => ({
@@ -3202,11 +3199,11 @@ var init_LiteLLMBudgetWidget = __esm({
3202
3199
  borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
3203
3200
  })
3204
3201
  },
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.")
3202
+ /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react9.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.")
3206
3203
  );
3207
3204
  FullBody = ({ summary }) => {
3208
3205
  const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
3209
- return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(
3206
+ return /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement(
3210
3207
  LevelFrame,
3211
3208
  {
3212
3209
  rank: 1,
@@ -3214,10 +3211,10 @@ var init_LiteLLMBudgetWidget = __esm({
3214
3211
  tagline: "per-key cap \xB7 highest priority",
3215
3212
  note: hasBudgetedKeys ? void 0 : "no key budgets set"
3216
3213
  },
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(
3214
+ !hasBudgetedKeys && /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3215
+ summary.keys.map((k) => /* @__PURE__ */ import_react9.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3216
+ summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react9.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
3217
+ ), /* @__PURE__ */ import_react9.default.createElement(
3221
3218
  LevelFrame,
3222
3219
  {
3223
3220
  rank: 2,
@@ -3225,15 +3222,15 @@ var init_LiteLLMBudgetWidget = __esm({
3225
3222
  tagline: "cap on everything you spend with your own keys",
3226
3223
  note: "skipped for team-bound keys"
3227
3224
  },
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 })));
3225
+ summary.user ? /* @__PURE__ */ import_react9.default.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3226
+ ), /* @__PURE__ */ import_react9.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_react9.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react9.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react9.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_react9.default.createElement(import_Box8.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react9.default.createElement(HierarchyNote, { withReset: true })));
3230
3227
  };
3231
3228
  CompactBody = ({ summary }) => {
3232
3229
  const { count } = budgetHeadline(summary);
3233
3230
  if (count === 0) {
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.");
3231
+ return /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3235
3232
  }
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 })));
3233
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react9.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react9.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react9.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ import_react9.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react9.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3237
3234
  };
3238
3235
  LiteLLMBudgetWidget = ({
3239
3236
  title = "Budget Policy",
@@ -3244,13 +3241,13 @@ var init_LiteLLMBudgetWidget = __esm({
3244
3241
  action
3245
3242
  }) => {
3246
3243
  const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
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)(() => {
3244
+ const [loading, setLoading] = (0, import_react9.useState)(true);
3245
+ const [error, setError] = (0, import_react9.useState)(null);
3246
+ const [user, setUser] = (0, import_react9.useState)(null);
3247
+ const [teams, setTeams] = (0, import_react9.useState)([]);
3248
+ const [keys, setKeys] = (0, import_react9.useState)([]);
3249
+ const [expanded, setExpanded] = (0, import_react9.useState)(defaultExpanded);
3250
+ (0, import_react9.useEffect)(() => {
3254
3251
  let cancelled = false;
3255
3252
  setLoading(true);
3256
3253
  setError(null);
@@ -3274,18 +3271,18 @@ var init_LiteLLMBudgetWidget = __esm({
3274
3271
  cancelled = true;
3275
3272
  };
3276
3273
  }, [api]);
3277
- const summary = (0, import_react8.useMemo)(
3274
+ const summary = (0, import_react9.useMemo)(
3278
3275
  () => buildBudgetSummary(user, teams, keys, maxKeys),
3279
3276
  [user, teams, keys, maxKeys]
3280
3277
  );
3281
- const headline = (0, import_react8.useMemo)(() => budgetHeadline(summary), [summary]);
3278
+ const headline = (0, import_react9.useMemo)(() => budgetHeadline(summary), [summary]);
3282
3279
  const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
3283
3280
  const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
3284
3281
  headline.closest.pct
3285
3282
  )}%`;
3286
3283
  const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
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(
3284
+ const body = /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, loading && /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ import_react9.default.createElement(import_CircularProgress4.default, { size: 32 })), !loading && error && /* @__PURE__ */ import_react9.default.createElement(import_Alert3.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ import_react9.default.createElement(CompactBody, { summary }) : /* @__PURE__ */ import_react9.default.createElement(FullBody, { summary })));
3285
+ return /* @__PURE__ */ import_react9.default.createElement(import_Paper6.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react9.default.createElement(
3289
3286
  import_Box8.default,
3290
3287
  {
3291
3288
  onClick: () => setExpanded((e) => !e),
@@ -3298,14 +3295,14 @@ var init_LiteLLMBudgetWidget = __esm({
3298
3295
  mb: expanded ? 1.5 : 0
3299
3296
  }
3300
3297
  },
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(
3298
+ /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { mt: 0.5 } }, /* @__PURE__ */ import_react9.default.createElement(
3302
3299
  StatusPill,
3303
3300
  {
3304
3301
  label: summaryText,
3305
3302
  tone: budgetTone2(headline.closest?.pct ?? 0)
3306
3303
  }
3307
3304
  ))),
3308
- /* @__PURE__ */ import_react8.default.createElement(
3305
+ /* @__PURE__ */ import_react9.default.createElement(
3309
3306
  import_IconButton4.default,
3310
3307
  {
3311
3308
  size: "small",
@@ -3321,9 +3318,9 @@ var init_LiteLLMBudgetWidget = __esm({
3321
3318
  transition: theme.transitions.create("transform")
3322
3319
  })
3323
3320
  },
3324
- /* @__PURE__ */ import_react8.default.createElement(import_icons_material5.ExpandMore, null)
3321
+ /* @__PURE__ */ import_react9.default.createElement(import_icons_material5.ExpandMore, null)
3325
3322
  )
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(
3323
+ ) : /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ import_react9.default.createElement(import_Collapse.default, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ import_react9.default.createElement(
3327
3324
  import_Box8.default,
3328
3325
  {
3329
3326
  sx: (theme) => ({
@@ -3353,11 +3350,11 @@ function budgetStatValue(hidden, budget) {
3353
3350
  if (budget > 0) return fmtUsd2(budget);
3354
3351
  return "Unlimited";
3355
3352
  }
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;
3353
+ var import_react10, 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;
3357
3354
  var init_TeamUsage = __esm({
3358
3355
  "src/components/TeamUsage.tsx"() {
3359
3356
  "use strict";
3360
- import_react9 = __toESM(require("react"));
3357
+ import_react10 = __toESM(require("react"));
3361
3358
  import_Paper7 = __toESM(require("@mui/material/Paper"));
3362
3359
  import_Box9 = __toESM(require("@mui/material/Box"));
3363
3360
  import_Typography9 = __toESM(require("@mui/material/Typography"));
@@ -3379,7 +3376,7 @@ var init_TeamUsage = __esm({
3379
3376
  init_ui();
3380
3377
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
3381
3378
  TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
3382
- const [expanded, setExpanded] = (0, import_react9.useState)(false);
3379
+ const [expanded, setExpanded] = (0, import_react10.useState)(false);
3383
3380
  const chart = useChartTheme();
3384
3381
  const hidden = team.budget_hidden === true;
3385
3382
  const budget = hidden ? 0 : team.max_budget ?? 0;
@@ -3391,10 +3388,10 @@ var init_TeamUsage = __esm({
3391
3388
  const renderDailySpendSection = () => {
3392
3389
  if (hidden) return null;
3393
3390
  if (usageLoading) {
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 }));
3391
+ return /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react10.default.createElement(import_CircularProgress5.default, { size: 22 }));
3395
3392
  }
3396
3393
  if (dailyData.length === 0) return null;
3397
- return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react9.default.createElement(
3394
+ return /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react10.default.createElement(
3398
3395
  import_Typography9.default,
3399
3396
  {
3400
3397
  variant: "caption",
@@ -3402,13 +3399,13 @@ var init_TeamUsage = __esm({
3402
3399
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
3403
3400
  },
3404
3401
  "Daily spend"
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(
3402
+ ), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { height: 150 }, /* @__PURE__ */ import_react10.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react10.default.createElement(import_recharts2.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react10.default.createElement(import_recharts2.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react10.default.createElement(import_recharts2.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react10.default.createElement(import_recharts2.YAxis, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ import_react10.default.createElement(
3406
3403
  import_recharts2.Tooltip,
3407
3404
  {
3408
3405
  cursor: chart.cursor,
3409
- content: /* @__PURE__ */ import_react9.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3406
+ content: /* @__PURE__ */ import_react10.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3410
3407
  }
3411
- ), /* @__PURE__ */ import_react9.default.createElement(
3408
+ ), /* @__PURE__ */ import_react10.default.createElement(
3412
3409
  import_recharts2.Area,
3413
3410
  {
3414
3411
  type: "monotone",
@@ -3421,7 +3418,7 @@ var init_TeamUsage = __esm({
3421
3418
  }
3422
3419
  )))));
3423
3420
  };
3424
- const sectionLabel = (label) => /* @__PURE__ */ import_react9.default.createElement(
3421
+ const sectionLabel = (label) => /* @__PURE__ */ import_react10.default.createElement(
3425
3422
  import_Typography9.default,
3426
3423
  {
3427
3424
  variant: "caption",
@@ -3430,7 +3427,7 @@ var init_TeamUsage = __esm({
3430
3427
  },
3431
3428
  label
3432
3429
  );
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(
3430
+ return /* @__PURE__ */ import_react10.default.createElement(import_Paper7.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ import_react10.default.createElement(
3434
3431
  import_Box9.default,
3435
3432
  {
3436
3433
  sx: (theme) => ({
@@ -3445,8 +3442,8 @@ var init_TeamUsage = __esm({
3445
3442
  color: theme.palette.primary.main
3446
3443
  })
3447
3444
  },
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(
3445
+ /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.Group, { fontSize: "small" })
3446
+ ), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react10.default.createElement(
3450
3447
  import_Typography9.default,
3451
3448
  {
3452
3449
  variant: "caption",
@@ -3454,7 +3451,7 @@ var init_TeamUsage = __esm({
3454
3451
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3455
3452
  },
3456
3453
  team.team_id
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(
3454
+ )), isOver && /* @__PURE__ */ import_react10.default.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ import_react10.default.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ import_react10.default.createElement(
3458
3455
  import_Button5.default,
3459
3456
  {
3460
3457
  size: "small",
@@ -3463,7 +3460,7 @@ var init_TeamUsage = __esm({
3463
3460
  sx: { textTransform: "none" }
3464
3461
  },
3465
3462
  "Edit"
3466
- ), /* @__PURE__ */ import_react9.default.createElement(
3463
+ ), /* @__PURE__ */ import_react10.default.createElement(
3467
3464
  import_IconButton5.default,
3468
3465
  {
3469
3466
  size: "small",
@@ -3476,8 +3473,8 @@ var init_TeamUsage = __esm({
3476
3473
  transition: theme.transitions.create("transform")
3477
3474
  })
3478
3475
  },
3479
- /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.ExpandMore, null)
3480
- )), /* @__PURE__ */ import_react9.default.createElement(
3476
+ /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.ExpandMore, null)
3477
+ )), /* @__PURE__ */ import_react10.default.createElement(
3481
3478
  import_Box9.default,
3482
3479
  {
3483
3480
  sx: {
@@ -3488,20 +3485,20 @@ var init_TeamUsage = __esm({
3488
3485
  maxWidth: 640
3489
3486
  }
3490
3487
  },
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(
3488
+ /* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3489
+ /* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3490
+ /* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3491
+ /* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3492
+ /* @__PURE__ */ import_react10.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3493
+ /* @__PURE__ */ import_react10.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3494
+ ), hidden && /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ import_react10.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ import_react10.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_react10.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react10.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react10.default.createElement(import_Collapse2.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react10.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react10.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react10.default.createElement(
3498
3495
  import_TableContainer4.default,
3499
3496
  {
3500
3497
  component: import_Paper7.default,
3501
3498
  variant: "outlined",
3502
3499
  sx: { borderRadius: 1.5 }
3503
3500
  },
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(
3501
+ /* @__PURE__ */ import_react10.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react10.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, null, "User"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, { align: "right" }, "Role"))), /* @__PURE__ */ import_react10.default.createElement(import_TableBody4.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react10.default.createElement(import_TableRow4.default, { key: m.user_id }, /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, null, m.user_email ? /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react10.default.createElement(
3505
3502
  import_Typography9.default,
3506
3503
  {
3507
3504
  variant: "caption",
@@ -3509,14 +3506,14 @@ var init_TeamUsage = __esm({
3509
3506
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3510
3507
  },
3511
3508
  m.user_id
3512
- )) : /* @__PURE__ */ import_react9.default.createElement(
3509
+ )) : /* @__PURE__ */ import_react10.default.createElement(
3513
3510
  import_Typography9.default,
3514
3511
  {
3515
3512
  variant: "body2",
3516
3513
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
3517
3514
  },
3518
3515
  m.user_id
3519
- )), /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(
3516
+ )), /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(
3520
3517
  StatusPill,
3521
3518
  {
3522
3519
  label: m.role,
@@ -3524,7 +3521,7 @@ var init_TeamUsage = __esm({
3524
3521
  dot: false
3525
3522
  }
3526
3523
  ))))))
3527
- ) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3524
+ ) : /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3528
3525
  };
3529
3526
  TeamUsage = ({
3530
3527
  teams,
@@ -3536,22 +3533,22 @@ var init_TeamUsage = __esm({
3536
3533
  canCreate,
3537
3534
  onCreateTeam
3538
3535
  }) => {
3539
- const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react9.default.createElement(
3536
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react10.default.createElement(
3540
3537
  import_Button5.default,
3541
3538
  {
3542
3539
  variant: "contained",
3543
3540
  color: "primary",
3544
3541
  disableElevation: true,
3545
- startIcon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Add, null),
3542
+ startIcon: /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.Add, null),
3546
3543
  onClick: onCreateTeam
3547
3544
  },
3548
3545
  "Create Team"
3549
3546
  ) : void 0;
3550
3547
  if (loading) {
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 }));
3548
+ return /* @__PURE__ */ import_react10.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react10.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react10.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
3552
3549
  }
3553
3550
  if (!teams.length) {
3554
- return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(
3551
+ return /* @__PURE__ */ import_react10.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react10.default.createElement(
3555
3552
  EmptyState,
3556
3553
  {
3557
3554
  message: "You're not a member of any LiteLLM team yet.",
@@ -3559,14 +3556,14 @@ var init_TeamUsage = __esm({
3559
3556
  }
3560
3557
  ));
3561
3558
  }
3562
- return /* @__PURE__ */ import_react9.default.createElement(
3559
+ return /* @__PURE__ */ import_react10.default.createElement(
3563
3560
  SectionCard,
3564
3561
  {
3565
3562
  title: "Teams",
3566
3563
  subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
3567
3564
  actions: createAction
3568
3565
  },
3569
- /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react9.default.createElement(
3566
+ /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react10.default.createElement(
3570
3567
  TeamCard,
3571
3568
  {
3572
3569
  key: team.team_id,
@@ -3600,11 +3597,11 @@ function fmtTokens(n) {
3600
3597
  if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
3601
3598
  return String(n);
3602
3599
  }
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;
3600
+ var import_react11, 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;
3604
3601
  var init_ModelsTable = __esm({
3605
3602
  "src/components/ModelsTable.tsx"() {
3606
3603
  "use strict";
3607
- import_react10 = __toESM(require("react"));
3604
+ import_react11 = __toESM(require("react"));
3608
3605
  import_Box10 = __toESM(require("@mui/material/Box"));
3609
3606
  import_Table5 = __toESM(require("@mui/material/Table"));
3610
3607
  import_TableBody5 = __toESM(require("@mui/material/TableBody"));
@@ -3630,20 +3627,20 @@ var init_ModelsTable = __esm({
3630
3627
  ALL_PROXY_MODELS2 = "all-proxy-models";
3631
3628
  ModelsTable = ({ allModels, teams, loading }) => {
3632
3629
  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)(
3630
+ const [selectedTeamId, setSelectedTeamId] = (0, import_react11.useState)("");
3631
+ const [filterText, setFilterText] = (0, import_react11.useState)("");
3632
+ const [copiedSnackbar, setCopiedSnackbar] = (0, import_react11.useState)(false);
3633
+ const [sortKey, setSortKey] = (0, import_react11.useState)("model_name");
3634
+ const [sortDirection, setSortDirection] = (0, import_react11.useState)("asc");
3635
+ const selectedTeam = (0, import_react11.useMemo)(
3639
3636
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
3640
3637
  [teams, selectedTeamId]
3641
3638
  );
3642
- const teamModels = (0, import_react10.useMemo)(() => {
3639
+ const teamModels = (0, import_react11.useMemo)(() => {
3643
3640
  if (!selectedTeam) return [];
3644
3641
  return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
3645
3642
  }, [allModels, selectedTeam]);
3646
- const filteredModels = (0, import_react10.useMemo)(() => {
3643
+ const filteredModels = (0, import_react11.useMemo)(() => {
3647
3644
  const q = filterText.trim().toLowerCase();
3648
3645
  let models = teamModels;
3649
3646
  if (q) {
@@ -3690,10 +3687,10 @@ var init_ModelsTable = __esm({
3690
3687
  return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
3691
3688
  });
3692
3689
  }, [teamModels, filterText, sortKey, sortDirection]);
3693
- const handleCopyModelId = (0, import_react10.useCallback)((modelId) => {
3690
+ const handleCopyModelId = (0, import_react11.useCallback)((modelId) => {
3694
3691
  navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
3695
3692
  }, []);
3696
- const handleSort = (0, import_react10.useCallback)((key) => {
3693
+ const handleSort = (0, import_react11.useCallback)((key) => {
3697
3694
  if (sortKey === key) {
3698
3695
  setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
3699
3696
  } else {
@@ -3715,13 +3712,13 @@ var init_ModelsTable = __esm({
3715
3712
  return theme.palette.info.main;
3716
3713
  }
3717
3714
  };
3718
- return /* @__PURE__ */ import_react10.default.createElement(
3715
+ return /* @__PURE__ */ import_react11.default.createElement(
3719
3716
  SectionCard,
3720
3717
  {
3721
3718
  title: "Available Models",
3722
3719
  subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
3723
3720
  },
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(
3721
+ /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ import_react11.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react11.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react11.default.createElement(
3725
3722
  import_Select.default,
3726
3723
  {
3727
3724
  labelId: "team-select-label",
@@ -3729,8 +3726,8 @@ var init_ModelsTable = __esm({
3729
3726
  label: "Team",
3730
3727
  onChange: (e) => setSelectedTeamId(e.target.value)
3731
3728
  },
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(
3729
+ teams.map((t) => /* @__PURE__ */ import_react11.default.createElement(import_MenuItem4.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3730
+ )), /* @__PURE__ */ import_react11.default.createElement(
3734
3731
  import_TextField5.default,
3735
3732
  {
3736
3733
  label: "Filter Models",
@@ -3740,33 +3737,33 @@ var init_ModelsTable = __esm({
3740
3737
  disabled: !selectedTeamId,
3741
3738
  sx: { minWidth: 240, mb: 2 },
3742
3739
  InputProps: {
3743
- startAdornment: /* @__PURE__ */ import_react10.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react10.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
3740
+ startAdornment: /* @__PURE__ */ import_react11.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react11.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
3744
3741
  }
3745
3742
  }
3746
3743
  )),
3747
- loading && /* @__PURE__ */ import_react10.default.createElement(EmptyState, { message: "Loading models\u2026" }),
3748
- !loading && !selectedTeamId && /* @__PURE__ */ import_react10.default.createElement(
3744
+ loading && /* @__PURE__ */ import_react11.default.createElement(EmptyState, { message: "Loading models\u2026" }),
3745
+ !loading && !selectedTeamId && /* @__PURE__ */ import_react11.default.createElement(
3749
3746
  EmptyState,
3750
3747
  {
3751
3748
  message: "Select a team to see available models",
3752
3749
  hint: "Each team may have a different set of models assigned"
3753
3750
  }
3754
3751
  ),
3755
- !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
3752
+ !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react11.default.createElement(
3756
3753
  EmptyState,
3757
3754
  {
3758
3755
  message: "No models available for this team",
3759
3756
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
3760
3757
  }
3761
3758
  ),
3762
- !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
3759
+ !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react11.default.createElement(
3763
3760
  EmptyState,
3764
3761
  {
3765
3762
  message: "No models match your search",
3766
3763
  hint: "Try a different search term"
3767
3764
  }
3768
3765
  ),
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(
3766
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ import_react11.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react11.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react11.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_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react11.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_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react11.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_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react11.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_react11.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react11.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react11.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react11.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react11.default.createElement(
3770
3767
  "span",
3771
3768
  {
3772
3769
  role: "button",
@@ -3776,8 +3773,8 @@ var init_ModelsTable = __esm({
3776
3773
  onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
3777
3774
  style: { cursor: "pointer", display: "inline-flex" }
3778
3775
  },
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(
3776
+ /* @__PURE__ */ import_react11.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3777
+ )), /* @__PURE__ */ import_react11.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react11.default.createElement(
3781
3778
  import_Box10.default,
3782
3779
  {
3783
3780
  component: "span",
@@ -3788,8 +3785,8 @@ var init_ModelsTable = __esm({
3788
3785
  sx: quietIconButtonSx("accent"),
3789
3786
  style: { cursor: "pointer", display: "inline-flex" }
3790
3787
  },
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(
3788
+ /* @__PURE__ */ import_react11.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
3789
+ )))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react11.default.createElement(
3793
3790
  import_Box10.default,
3794
3791
  {
3795
3792
  component: "span",
@@ -3806,8 +3803,8 @@ var init_ModelsTable = __esm({
3806
3803
  }
3807
3804
  },
3808
3805
  m.mode
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(
3806
+ )), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react11.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react11.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3807
+ /* @__PURE__ */ import_react11.default.createElement(
3811
3808
  import_Snackbar.default,
3812
3809
  {
3813
3810
  open: copiedSnackbar,
@@ -3815,7 +3812,7 @@ var init_ModelsTable = __esm({
3815
3812
  onClose: () => setCopiedSnackbar(false),
3816
3813
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
3817
3814
  },
3818
- /* @__PURE__ */ import_react10.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3815
+ /* @__PURE__ */ import_react11.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3819
3816
  )
3820
3817
  );
3821
3818
  };
@@ -3843,10 +3840,10 @@ function formatDateTime(iso) {
3843
3840
  }
3844
3841
  function renderAuditLogBody(loading, entries) {
3845
3842
  if (loading) {
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 })));
3843
+ return /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react12.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
3847
3844
  }
3848
3845
  if (entries.length === 0) {
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(
3846
+ return /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react12.default.createElement(
3850
3847
  EmptyState,
3851
3848
  {
3852
3849
  message: "No audit events found",
@@ -3854,13 +3851,13 @@ function renderAuditLogBody(loading, entries) {
3854
3851
  }
3855
3852
  )));
3856
3853
  }
3857
- return entries.map((entry) => /* @__PURE__ */ import_react11.default.createElement(DetailRow, { key: entry.id, entry }));
3854
+ return entries.map((entry) => /* @__PURE__ */ import_react12.default.createElement(DetailRow, { key: entry.id, entry }));
3858
3855
  }
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;
3856
+ var import_react12, 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;
3860
3857
  var init_AuditLog = __esm({
3861
3858
  "src/components/AuditLog.tsx"() {
3862
3859
  "use strict";
3863
- import_react11 = __toESM(require("react"));
3860
+ import_react12 = __toESM(require("react"));
3864
3861
  import_Box11 = __toESM(require("@mui/material/Box"));
3865
3862
  import_Typography11 = __toESM(require("@mui/material/Typography"));
3866
3863
  import_Table6 = __toESM(require("@mui/material/Table"));
@@ -3895,9 +3892,9 @@ var init_AuditLog = __esm({
3895
3892
  LiteLLM_UserTable: "User"
3896
3893
  };
3897
3894
  DetailRow = ({ entry }) => {
3898
- const [open, setOpen] = (0, import_react11.useState)(false);
3895
+ const [open, setOpen] = (0, import_react12.useState)(false);
3899
3896
  const hasDetail = entry.before_value || entry.updated_values;
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(
3897
+ return /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react12.default.createElement(
3901
3898
  import_IconButton6.default,
3902
3899
  {
3903
3900
  size: "small",
@@ -3910,8 +3907,8 @@ var init_AuditLog = __esm({
3910
3907
  transition: theme.transitions.create("transform")
3911
3908
  })
3912
3909
  },
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(
3910
+ /* @__PURE__ */ import_react12.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
3911
+ )), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react12.default.createElement(import_Typography11.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, entry.action && /* @__PURE__ */ import_react12.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_Typography11.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react12.default.createElement(
3915
3912
  import_Typography11.default,
3916
3913
  {
3917
3914
  variant: "body2",
@@ -3921,7 +3918,7 @@ var init_AuditLog = __esm({
3921
3918
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3922
3919
  },
3923
3920
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
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(
3921
+ )), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react12.default.createElement(import_Collapse3.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react12.default.createElement(
3925
3922
  import_Box11.default,
3926
3923
  {
3927
3924
  display: "flex",
@@ -3934,7 +3931,7 @@ var init_AuditLog = __esm({
3934
3931
  bgcolor: (0, import_styles8.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3935
3932
  })
3936
3933
  },
3937
- entry.before_value && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react11.default.createElement(
3934
+ entry.before_value && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react12.default.createElement(
3938
3935
  import_Typography11.default,
3939
3936
  {
3940
3937
  variant: "caption",
@@ -3944,8 +3941,8 @@ var init_AuditLog = __esm({
3944
3941
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3945
3942
  },
3946
3943
  "Before"
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(
3944
+ ), /* @__PURE__ */ import_react12.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))),
3945
+ entry.updated_values && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react12.default.createElement(
3949
3946
  import_Typography11.default,
3950
3947
  {
3951
3948
  variant: "caption",
@@ -3955,14 +3952,14 @@ var init_AuditLog = __esm({
3955
3952
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3956
3953
  },
3957
3954
  "After"
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)))
3955
+ ), /* @__PURE__ */ import_react12.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)))
3959
3956
  )))));
3960
3957
  };
3961
3958
  AuditLog = ({ api }) => {
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)(
3959
+ const [page, setPage] = (0, import_react12.useState)(0);
3960
+ const [pageSize, setPageSize] = (0, import_react12.useState)(25);
3961
+ const [filters, setFilters] = (0, import_react12.useState)({});
3962
+ const fetchParams = (0, import_react12.useMemo)(
3966
3963
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3967
3964
  [page, pageSize, filters]
3968
3965
  );
@@ -3972,13 +3969,13 @@ var init_AuditLog = __esm({
3972
3969
  );
3973
3970
  const entries = value?.audit_logs ?? [];
3974
3971
  const total = value?.total ?? 0;
3975
- return /* @__PURE__ */ import_react11.default.createElement(
3972
+ return /* @__PURE__ */ import_react12.default.createElement(
3976
3973
  SectionCard,
3977
3974
  {
3978
3975
  title: "Audit Log",
3979
3976
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3980
3977
  flush: true,
3981
- actions: /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(
3978
+ actions: /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(
3982
3979
  import_TextField6.default,
3983
3980
  {
3984
3981
  size: "small",
@@ -3991,12 +3988,12 @@ var init_AuditLog = __esm({
3991
3988
  },
3992
3989
  sx: { minWidth: 150 }
3993
3990
  },
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(
3991
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
3992
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
3993
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
3994
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
3995
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
3996
+ ), /* @__PURE__ */ import_react12.default.createElement(
4000
3997
  import_TextField6.default,
4001
3998
  {
4002
3999
  size: "small",
@@ -4009,13 +4006,13 @@ var init_AuditLog = __esm({
4009
4006
  },
4010
4007
  sx: { minWidth: 150 }
4011
4008
  },
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(
4009
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
4010
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
4011
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
4012
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
4013
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
4014
+ /* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
4015
+ ), /* @__PURE__ */ import_react12.default.createElement(
4019
4016
  import_TextField6.default,
4020
4017
  {
4021
4018
  size: "small",
@@ -4029,9 +4026,9 @@ var init_AuditLog = __esm({
4029
4026
  }
4030
4027
  ))
4031
4028
  },
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(
4029
+ error && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react12.default.createElement(import_Alert5.default, { severity: "error" }, error.message)),
4030
+ /* @__PURE__ */ import_react12.default.createElement(import_TableContainer6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_Table6.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react12.default.createElement(import_TableHead6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Time"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Action"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Table"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Object ID"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Changed By"))), /* @__PURE__ */ import_react12.default.createElement(import_TableBody6.default, null, renderAuditLogBody(loading, entries)))),
4031
+ /* @__PURE__ */ import_react12.default.createElement(
4035
4032
  import_TablePagination.default,
4036
4033
  {
4037
4034
  component: "div",
@@ -4100,11 +4097,11 @@ function initDateRange() {
4100
4097
  else start.setDate(start.getDate() - 7);
4101
4098
  return { start, end };
4102
4099
  }
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;
4100
+ var import_react13, 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;
4104
4101
  var init_LiteLLMPage = __esm({
4105
4102
  "src/components/LiteLLMPage.tsx"() {
4106
4103
  "use strict";
4107
- import_react12 = __toESM(require("react"));
4104
+ import_react13 = __toESM(require("react"));
4108
4105
  import_Box12 = __toESM(require("@mui/material/Box"));
4109
4106
  import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
4110
4107
  import_Alert6 = __toESM(require("@mui/material/Alert"));
@@ -4120,7 +4117,7 @@ var init_LiteLLMPage = __esm({
4120
4117
  import_plugin_permission_react = require("@backstage/plugin-permission-react");
4121
4118
  init_DashboardHeader();
4122
4119
  init_KeysTable();
4123
- init_GenerateKeyDialog();
4120
+ init_KeyFormDialog();
4124
4121
  init_ManageTeamDialog();
4125
4122
  init_UsageStats();
4126
4123
  init_LiteLLMBudgetWidget();
@@ -4134,8 +4131,8 @@ var init_LiteLLMPage = __esm({
4134
4131
  isPageTab = (v) => !!v && PAGE_TABS.includes(v);
4135
4132
  LiteLLMPage = () => {
4136
4133
  const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
4137
- const [dateRange, setDateRange] = (0, import_react12.useState)(initDateRange);
4138
- const [currentPreset, setCurrentPreset] = (0, import_react12.useState)(() => {
4134
+ const [dateRange, setDateRange] = (0, import_react13.useState)(initDateRange);
4135
+ const [currentPreset, setCurrentPreset] = (0, import_react13.useState)(() => {
4139
4136
  try {
4140
4137
  return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
4141
4138
  } catch {
@@ -4143,14 +4140,14 @@ var init_LiteLLMPage = __esm({
4143
4140
  }
4144
4141
  });
4145
4142
  const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
4146
- const [activeTab, setActiveTab] = (0, import_react12.useState)(() => {
4143
+ const [activeTab, setActiveTab] = (0, import_react13.useState)(() => {
4147
4144
  const t = searchParams.get("tab");
4148
4145
  return isPageTab(t) ? t : "overview";
4149
4146
  });
4150
- const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react12.useState)(
4147
+ const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react13.useState)(
4151
4148
  () => searchParams.get("generate") === "1"
4152
4149
  );
4153
- (0, import_react12.useEffect)(() => {
4150
+ (0, import_react13.useEffect)(() => {
4154
4151
  if (searchParams.get("generate") !== "1") return;
4155
4152
  setGenerateDialogOpen(true);
4156
4153
  setSearchParams(
@@ -4162,7 +4159,7 @@ var init_LiteLLMPage = __esm({
4162
4159
  { replace: true }
4163
4160
  );
4164
4161
  }, [searchParams, setSearchParams]);
4165
- const selectTab = (0, import_react12.useCallback)(
4162
+ const selectTab = (0, import_react13.useCallback)(
4166
4163
  (tab) => {
4167
4164
  setActiveTab(tab);
4168
4165
  setSearchParams(
@@ -4176,10 +4173,12 @@ var init_LiteLLMPage = __esm({
4176
4173
  },
4177
4174
  [setSearchParams]
4178
4175
  );
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)({});
4176
+ const [snackbar, setSnackbar] = (0, import_react13.useState)(null);
4177
+ const [manageTeam, setManageTeam] = (0, import_react13.useState)(null);
4178
+ const [keyToEdit, setKeyToEdit] = (0, import_react13.useState)(null);
4179
+ const keyFormOpen = generateDialogOpen || !!keyToEdit;
4180
+ const [teamUsageCache, setTeamUsageCache] = (0, import_react13.useState)({});
4181
+ const [teamUsageLoading, setTeamUsageLoading] = (0, import_react13.useState)({});
4183
4182
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
4184
4183
  () => api.getUserInfo(),
4185
4184
  [api]
@@ -4223,7 +4222,7 @@ var init_LiteLLMPage = __esm({
4223
4222
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
4224
4223
  [api, objectPermsEnabled]
4225
4224
  );
4226
- const teams = (0, import_react12.useMemo)(() => {
4225
+ const teams = (0, import_react13.useMemo)(() => {
4227
4226
  if (!allTeams?.length) return [];
4228
4227
  if (!userInfo) return allTeams;
4229
4228
  const userId = userInfo.user_id;
@@ -4240,7 +4239,7 @@ var init_LiteLLMPage = __esm({
4240
4239
  const endDate = dateRange.end.toISOString().split("T")[0];
4241
4240
  return api.getUsage(startDate, endDate);
4242
4241
  }, [api, dateRange]);
4243
- const handleDateRangeChange = (0, import_react12.useCallback)((range, preset) => {
4242
+ const handleDateRangeChange = (0, import_react13.useCallback)((range, preset) => {
4244
4243
  setDateRange(range);
4245
4244
  if (preset) {
4246
4245
  setCurrentPreset(preset);
@@ -4248,7 +4247,7 @@ var init_LiteLLMPage = __esm({
4248
4247
  setTeamUsageCache({});
4249
4248
  setTeamUsageLoading({});
4250
4249
  }, [setDateRange, setCurrentPreset]);
4251
- const loadTeamUsage = (0, import_react12.useCallback)(async (teamId) => {
4250
+ const loadTeamUsage = (0, import_react13.useCallback)(async (teamId) => {
4252
4251
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
4253
4252
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
4254
4253
  try {
@@ -4262,7 +4261,7 @@ var init_LiteLLMPage = __esm({
4262
4261
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
4263
4262
  }
4264
4263
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
4265
- const allowedModels = (0, import_react12.useMemo)(() => {
4264
+ const allowedModels = (0, import_react13.useMemo)(() => {
4266
4265
  if (!allModels?.length) return [];
4267
4266
  const userModels = userInfo?.models;
4268
4267
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -4275,7 +4274,7 @@ var init_LiteLLMPage = __esm({
4275
4274
  ]);
4276
4275
  return allModels.filter((m) => allowed.has(m.model_name));
4277
4276
  }, [allModels, userInfo, teams]);
4278
- const handleGenerateKey = (0, import_react12.useCallback)(
4277
+ const handleGenerateKey = (0, import_react13.useCallback)(
4279
4278
  async (request) => {
4280
4279
  try {
4281
4280
  const response = await api.generateKey(request);
@@ -4289,7 +4288,7 @@ var init_LiteLLMPage = __esm({
4289
4288
  },
4290
4289
  [api, refreshKeys]
4291
4290
  );
4292
- const handleUpdateKey = (0, import_react12.useCallback)(
4291
+ const handleUpdateKey = (0, import_react13.useCallback)(
4293
4292
  async (keyId, request) => {
4294
4293
  try {
4295
4294
  await api.updateKey(keyId, request);
@@ -4302,7 +4301,7 @@ var init_LiteLLMPage = __esm({
4302
4301
  },
4303
4302
  [api, refreshKeys]
4304
4303
  );
4305
- const handleBlockKey = (0, import_react12.useCallback)(
4304
+ const handleBlockKey = (0, import_react13.useCallback)(
4306
4305
  async (keyId) => {
4307
4306
  try {
4308
4307
  await api.blockKey(keyId);
@@ -4314,7 +4313,7 @@ var init_LiteLLMPage = __esm({
4314
4313
  },
4315
4314
  [api, refreshKeys]
4316
4315
  );
4317
- const handleUnblockKey = (0, import_react12.useCallback)(
4316
+ const handleUnblockKey = (0, import_react13.useCallback)(
4318
4317
  async (keyId) => {
4319
4318
  try {
4320
4319
  await api.unblockKey(keyId);
@@ -4326,7 +4325,7 @@ var init_LiteLLMPage = __esm({
4326
4325
  },
4327
4326
  [api, refreshKeys]
4328
4327
  );
4329
- const handleResetKeySpend = (0, import_react12.useCallback)(
4328
+ const handleResetKeySpend = (0, import_react13.useCallback)(
4330
4329
  async (keyId) => {
4331
4330
  try {
4332
4331
  await api.resetKeySpend(keyId);
@@ -4338,7 +4337,7 @@ var init_LiteLLMPage = __esm({
4338
4337
  },
4339
4338
  [api, refreshKeys]
4340
4339
  );
4341
- const handleDeleteKey = (0, import_react12.useCallback)(
4340
+ const handleDeleteKey = (0, import_react13.useCallback)(
4342
4341
  async (keyId) => {
4343
4342
  try {
4344
4343
  await api.deleteKey(keyId);
@@ -4355,7 +4354,7 @@ var init_LiteLLMPage = __esm({
4355
4354
  },
4356
4355
  [api, refreshKeys]
4357
4356
  );
4358
- const handlePruneExpiredKeys = (0, import_react12.useCallback)(
4357
+ const handlePruneExpiredKeys = (0, import_react13.useCallback)(
4359
4358
  async () => {
4360
4359
  try {
4361
4360
  const result = await api.pruneExpiredKeys();
@@ -4370,14 +4369,14 @@ var init_LiteLLMPage = __esm({
4370
4369
  );
4371
4370
  const isInitialLoading = userLoading && !userInfo;
4372
4371
  if (isInitialLoading) {
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));
4372
+ return /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react13.default.createElement(import_CircularProgress6.default, null));
4374
4373
  }
4375
4374
  if (userError || !userInfo) {
4376
4375
  const isProvisioningEnabled = userError?.body?.provisioning === true;
4377
4376
  const hint = userError?.body?.hint;
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.")));
4377
+ return /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { p: 3 }, /* @__PURE__ */ import_react13.default.createElement(import_Paper8.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react13.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.")));
4379
4378
  }
4380
- const pageTabs = /* @__PURE__ */ import_react12.default.createElement(
4379
+ const pageTabs = /* @__PURE__ */ import_react13.default.createElement(
4381
4380
  import_Tabs2.default,
4382
4381
  {
4383
4382
  value: activeTab,
@@ -4390,13 +4389,13 @@ var init_LiteLLMPage = __esm({
4390
4389
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
4391
4390
  }
4392
4391
  },
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" })
4392
+ /* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
4393
+ /* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
4394
+ /* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
4395
+ /* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
4396
+ userInfo.can_view_audit && /* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
4398
4397
  );
4399
- return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react12.default.createElement(
4398
+ return /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react13.default.createElement(
4400
4399
  DashboardHeader,
4401
4400
  {
4402
4401
  userInfo,
@@ -4406,7 +4405,7 @@ var init_LiteLLMPage = __esm({
4406
4405
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
4407
4406
  tabs: pageTabs
4408
4407
  }
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(
4408
+ ), activeTab === "overview" && /* @__PURE__ */ import_react13.default.createElement(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ import_react13.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ import_react13.default.createElement(
4410
4409
  UsageStats,
4411
4410
  {
4412
4411
  usage: usage ?? null,
@@ -4417,17 +4416,15 @@ var init_LiteLLMPage = __esm({
4417
4416
  loading: usageLoading,
4418
4417
  userInfo
4419
4418
  }
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(
4419
+ )), /* @__PURE__ */ import_react13.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ import_react13.default.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ import_react13.default.createElement(
4421
4420
  KeysTable,
4422
4421
  {
4423
4422
  keys: keys ?? [],
4424
- models: allowedModels,
4425
4423
  loading: keysLoading || modelsLoading,
4426
4424
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
4427
- onUpdateKey: handleUpdateKey,
4425
+ onEditKey: setKeyToEdit,
4428
4426
  onBlockKey: handleBlockKey,
4429
4427
  onUnblockKey: handleUnblockKey,
4430
- onResetKeySpend: handleResetKeySpend,
4431
4428
  onDeleteKey: handleDeleteKey,
4432
4429
  onPruneExpiredKeys: handlePruneExpiredKeys
4433
4430
  }
@@ -4436,7 +4433,7 @@ var init_LiteLLMPage = __esm({
4436
4433
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
4437
4434
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
4438
4435
  const visibleTeams = Array.from(teamsById.values());
4439
- return /* @__PURE__ */ import_react12.default.createElement(
4436
+ return /* @__PURE__ */ import_react13.default.createElement(
4440
4437
  TeamUsage,
4441
4438
  {
4442
4439
  teams: visibleTeams,
@@ -4452,27 +4449,34 @@ var init_LiteLLMPage = __esm({
4452
4449
  onCreateTeam: () => setManageTeam({ mode: "create" })
4453
4450
  }
4454
4451
  );
4455
- })(), activeTab === "models" && /* @__PURE__ */ import_react12.default.createElement(
4452
+ })(), activeTab === "models" && /* @__PURE__ */ import_react13.default.createElement(
4456
4453
  ModelsTable,
4457
4454
  {
4458
4455
  allModels: allModels ?? [],
4459
4456
  teams: teams ?? [],
4460
4457
  loading: modelsLoading
4461
4458
  }
4462
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react12.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react12.default.createElement(
4463
- GenerateKeyDialog,
4459
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react13.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react13.default.createElement(
4460
+ KeyFormDialog,
4464
4461
  {
4465
- open: generateDialogOpen,
4466
- onClose: () => setGenerateDialogOpen(false),
4462
+ open: keyFormOpen,
4463
+ onClose: () => {
4464
+ setGenerateDialogOpen(false);
4465
+ setKeyToEdit(null);
4466
+ },
4467
+ mode: keyToEdit ? "edit" : "create",
4468
+ keyToEdit,
4467
4469
  keys: keys ?? [],
4468
4470
  models: allowedModels,
4469
4471
  teams: teams ?? [],
4470
4472
  username: userInfo.user_id,
4471
4473
  keyGenerationSettings: liteLlmConfig?.keyGeneration,
4472
- onGenerateKey: handleGenerateKey,
4474
+ onCreateKey: handleGenerateKey,
4475
+ onUpdateKey: handleUpdateKey,
4476
+ onResetKeySpend: handleResetKeySpend,
4473
4477
  onGetConfig: () => api.getConfig()
4474
4478
  }
4475
- ), /* @__PURE__ */ import_react12.default.createElement(
4479
+ ), /* @__PURE__ */ import_react13.default.createElement(
4476
4480
  ManageTeamDialog,
4477
4481
  {
4478
4482
  open: !!manageTeam,
@@ -4534,7 +4538,7 @@ var init_LiteLLMPage = __esm({
4534
4538
  refreshTeams();
4535
4539
  }
4536
4540
  }
4537
- ), /* @__PURE__ */ import_react12.default.createElement(
4541
+ ), /* @__PURE__ */ import_react13.default.createElement(
4538
4542
  import_Snackbar2.default,
4539
4543
  {
4540
4544
  open: !!snackbar,
@@ -4542,7 +4546,7 @@ var init_LiteLLMPage = __esm({
4542
4546
  onClose: () => setSnackbar(null),
4543
4547
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
4544
4548
  },
4545
- snackbar ? /* @__PURE__ */ import_react12.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4549
+ snackbar ? /* @__PURE__ */ import_react13.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4546
4550
  ));
4547
4551
  };
4548
4552
  }
@@ -4552,7 +4556,9 @@ var init_LiteLLMPage = __esm({
4552
4556
  var index_exports = {};
4553
4557
  __export(index_exports, {
4554
4558
  DashboardHeader: () => DashboardHeader,
4559
+ GenerateKeyButton: () => GenerateKeyButton,
4555
4560
  GenerateKeyDialog: () => GenerateKeyDialog,
4561
+ KeyFormDialog: () => KeyFormDialog,
4556
4562
  KeysTable: () => KeysTable,
4557
4563
  LiteLLMBudgetGauges: () => LiteLLMBudgetGauges,
4558
4564
  LiteLLMBudgetWidget: () => LiteLLMBudgetWidget,
@@ -4573,7 +4579,7 @@ __export(index_exports, {
4573
4579
  module.exports = __toCommonJS(index_exports);
4574
4580
 
4575
4581
  // src/plugin.tsx
4576
- var import_react13 = __toESM(require("react"));
4582
+ var import_react14 = __toESM(require("react"));
4577
4583
  var import_icons_material9 = require("@mui/icons-material");
4578
4584
  var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
4579
4585
  init_api();
@@ -4588,10 +4594,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
4588
4594
  params: {
4589
4595
  path: "/litellm",
4590
4596
  title: "LiteLLM",
4591
- icon: /* @__PURE__ */ import_react13.default.createElement(import_icons_material9.TrendingUp, null),
4597
+ icon: /* @__PURE__ */ import_react14.default.createElement(import_icons_material9.TrendingUp, null),
4592
4598
  loader: async () => {
4593
4599
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
4594
- return /* @__PURE__ */ import_react13.default.createElement(LiteLLMPage2, null);
4600
+ return /* @__PURE__ */ import_react14.default.createElement(LiteLLMPage2, null);
4595
4601
  }
4596
4602
  }
4597
4603
  });
@@ -4608,7 +4614,7 @@ init_UsageStats();
4608
4614
  init_TeamUsage();
4609
4615
 
4610
4616
  // src/components/LiteLLMHomeWidget.tsx
4611
- var import_react14 = __toESM(require("react"));
4617
+ var import_react15 = __toESM(require("react"));
4612
4618
  var import_Paper9 = __toESM(require("@mui/material/Paper"));
4613
4619
  var import_Box13 = __toESM(require("@mui/material/Box"));
4614
4620
  var import_Typography13 = __toESM(require("@mui/material/Typography"));
@@ -4634,19 +4640,19 @@ function presetToDateRange(preset) {
4634
4640
  }
4635
4641
  return { start, end };
4636
4642
  }
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));
4643
+ var Kpi = ({ label, value }) => /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, null, /* @__PURE__ */ import_react15.default.createElement(import_Typography13.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react15.default.createElement(import_Typography13.default, { variant: "subtitle1", fontWeight: 600 }, value));
4638
4644
  var LiteLLMHomeWidget = ({
4639
4645
  defaultPeriod = "7d",
4640
4646
  title = "LiteLLM Usage"
4641
4647
  }) => {
4642
4648
  const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
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)(() => {
4649
+ const [period, setPeriod] = (0, import_react15.useState)(defaultPeriod);
4650
+ const [loading, setLoading] = (0, import_react15.useState)(true);
4651
+ const [usageError, setUsageError] = (0, import_react15.useState)(null);
4652
+ const [keysError, setKeysError] = (0, import_react15.useState)(null);
4653
+ const [usage, setUsage] = (0, import_react15.useState)(null);
4654
+ const [keys, setKeys] = (0, import_react15.useState)([]);
4655
+ (0, import_react15.useEffect)(() => {
4650
4656
  let cancelled = false;
4651
4657
  setLoading(true);
4652
4658
  setUsageError(null);
@@ -4683,17 +4689,17 @@ var LiteLLMHomeWidget = ({
4683
4689
  spend: d.spend
4684
4690
  }));
4685
4691
  const hasSparkline = dailyData.length > 0;
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(
4692
+ return /* @__PURE__ */ import_react15.default.createElement(import_Paper9.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react15.default.createElement(import_Typography13.default, { variant: "h6" }, title), /* @__PURE__ */ import_react15.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react15.default.createElement(
4687
4693
  import_Select2.default,
4688
4694
  {
4689
4695
  value: period,
4690
4696
  onChange: (e) => setPeriod(e.target.value),
4691
4697
  displayEmpty: true
4692
4698
  },
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(
4699
+ /* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
4700
+ /* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
4701
+ /* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
4702
+ ))), loading && /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react15.default.createElement(import_CircularProgress7.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react15.default.createElement(import_Alert7.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react15.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_react15.default.createElement(import_Grid3.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, { height: 120 }, /* @__PURE__ */ import_react15.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react15.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react15.default.createElement(
4697
4703
  import_recharts3.Area,
4698
4704
  {
4699
4705
  type: "monotone",
@@ -4711,7 +4717,7 @@ var LiteLLMHomeWidget = ({
4711
4717
  init_LiteLLMBudgetWidget();
4712
4718
 
4713
4719
  // src/components/LiteLLMBudgetGauges.tsx
4714
- var import_react15 = __toESM(require("react"));
4720
+ var import_react16 = __toESM(require("react"));
4715
4721
  var import_Paper10 = __toESM(require("@mui/material/Paper"));
4716
4722
  var import_Box14 = __toESM(require("@mui/material/Box"));
4717
4723
  var import_Button6 = __toESM(require("@mui/material/Button"));
@@ -4720,11 +4726,13 @@ var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress")
4720
4726
  var import_Alert8 = __toESM(require("@mui/material/Alert"));
4721
4727
  var import_Collapse4 = __toESM(require("@mui/material/Collapse"));
4722
4728
  var import_icons_material10 = require("@mui/icons-material");
4729
+ var import_recharts4 = require("recharts");
4723
4730
  var import_core_plugin_api6 = require("@backstage/core-plugin-api");
4724
- var import_core_components2 = require("@backstage/core-components");
4731
+ var import_core_components3 = require("@backstage/core-components");
4725
4732
  init_api();
4726
4733
  init_format();
4727
4734
  init_ui();
4735
+ init_GenerateKeyButton();
4728
4736
  init_BudgetLimitList();
4729
4737
  init_budget();
4730
4738
  var MODULE_PATH = "/litellm";
@@ -4735,7 +4743,7 @@ var LEVEL = {
4735
4743
  };
4736
4744
  var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
4737
4745
  var CTA_LABELS = {
4738
- "new-key": "New key",
4746
+ "new-key": "Generate New Key",
4739
4747
  module: "Open module",
4740
4748
  "all-limits": "All limits"
4741
4749
  };
@@ -4761,7 +4769,7 @@ var LevelGauge = ({
4761
4769
  const limit = gauge.limit;
4762
4770
  const tone = limit ? budgetTone2(limit.pct) : "neutral";
4763
4771
  const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
4764
- return /* @__PURE__ */ import_react15.default.createElement(
4772
+ return /* @__PURE__ */ import_react16.default.createElement(
4765
4773
  import_Box14.default,
4766
4774
  {
4767
4775
  sx: {
@@ -4772,7 +4780,7 @@ var LevelGauge = ({
4772
4780
  flex: 1
4773
4781
  }
4774
4782
  },
4775
- /* @__PURE__ */ import_react15.default.createElement(
4783
+ /* @__PURE__ */ import_react16.default.createElement(
4776
4784
  Gauge,
4777
4785
  {
4778
4786
  value: limit?.pct ?? 0,
@@ -4781,7 +4789,7 @@ var LevelGauge = ({
4781
4789
  label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
4782
4790
  }
4783
4791
  ),
4784
- /* @__PURE__ */ import_react15.default.createElement(
4792
+ /* @__PURE__ */ import_react16.default.createElement(
4785
4793
  import_Typography14.default,
4786
4794
  {
4787
4795
  sx: (theme) => ({
@@ -4795,7 +4803,7 @@ var LevelGauge = ({
4795
4803
  },
4796
4804
  name
4797
4805
  ),
4798
- limit ? /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
4806
+ limit ? /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(
4799
4807
  import_Typography14.default,
4800
4808
  {
4801
4809
  variant: "caption",
@@ -4811,7 +4819,7 @@ var LevelGauge = ({
4811
4819
  }
4812
4820
  },
4813
4821
  limit.label
4814
- ), /* @__PURE__ */ import_react15.default.createElement(
4822
+ ), /* @__PURE__ */ import_react16.default.createElement(
4815
4823
  import_Typography14.default,
4816
4824
  {
4817
4825
  variant: "caption",
@@ -4823,10 +4831,10 @@ var LevelGauge = ({
4823
4831
  }
4824
4832
  },
4825
4833
  spendCaption(limit)
4826
- ), extra > 0 ? /* @__PURE__ */ import_react15.default.createElement(
4834
+ ), extra > 0 ? /* @__PURE__ */ import_react16.default.createElement(
4827
4835
  import_Typography14.default,
4828
4836
  {
4829
- component: import_core_components2.Link,
4837
+ component: import_core_components3.Link,
4830
4838
  to: keysHref,
4831
4839
  variant: "caption",
4832
4840
  sx: {
@@ -4839,7 +4847,7 @@ var LevelGauge = ({
4839
4847
  "+",
4840
4848
  extra,
4841
4849
  " 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(
4850
+ ) : /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ import_react16.default.createElement(
4843
4851
  import_Typography14.default,
4844
4852
  {
4845
4853
  variant: "caption",
@@ -4850,6 +4858,125 @@ var LevelGauge = ({
4850
4858
  )
4851
4859
  );
4852
4860
  };
4861
+ function monthToDateRange(now = /* @__PURE__ */ new Date()) {
4862
+ const toDay = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
4863
+ d.getDate()
4864
+ ).padStart(2, "0")}`;
4865
+ const start = new Date(now.getFullYear(), now.getMonth(), 1);
4866
+ return { startDate: toDay(start), endDate: toDay(now) };
4867
+ }
4868
+ var UsageMiniChart = ({ data, totalTokens, loading, height }) => {
4869
+ const renderPlot = () => {
4870
+ if (loading) {
4871
+ return /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%" }, /* @__PURE__ */ import_react16.default.createElement(import_CircularProgress8.default, { size: 20 }));
4872
+ }
4873
+ if (data.length === 0) {
4874
+ return /* @__PURE__ */ import_react16.default.createElement(
4875
+ import_Box14.default,
4876
+ {
4877
+ display: "flex",
4878
+ justifyContent: "center",
4879
+ alignItems: "center",
4880
+ height: "100%",
4881
+ sx: (theme) => ({ color: theme.palette.text.secondary, fontSize: 18 })
4882
+ },
4883
+ "\u2014"
4884
+ );
4885
+ }
4886
+ return /* @__PURE__ */ import_react16.default.createElement(import_recharts4.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react16.default.createElement(import_recharts4.AreaChart, { data, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react16.default.createElement(import_recharts4.Tooltip, { content: /* @__PURE__ */ import_react16.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react16.default.createElement(
4887
+ import_recharts4.Area,
4888
+ {
4889
+ type: "monotone",
4890
+ dataKey: "input",
4891
+ name: "Input",
4892
+ stackId: "tok",
4893
+ stroke: SERIES.input,
4894
+ strokeWidth: 1.5,
4895
+ fill: SERIES.input,
4896
+ fillOpacity: 0.25,
4897
+ dot: false,
4898
+ isAnimationActive: false
4899
+ }
4900
+ ), /* @__PURE__ */ import_react16.default.createElement(
4901
+ import_recharts4.Area,
4902
+ {
4903
+ type: "monotone",
4904
+ dataKey: "output",
4905
+ name: "Output",
4906
+ stackId: "tok",
4907
+ stroke: SERIES.output,
4908
+ strokeWidth: 1.5,
4909
+ fill: SERIES.output,
4910
+ fillOpacity: 0.25,
4911
+ dot: false,
4912
+ isAnimationActive: false
4913
+ }
4914
+ )));
4915
+ };
4916
+ const renderCaption = () => {
4917
+ if (loading) {
4918
+ return /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { mt: 0.25, fontSize: 11 } }, "\u2026");
4919
+ }
4920
+ if (data.length === 0) {
4921
+ return /* @__PURE__ */ import_react16.default.createElement(
4922
+ import_Typography14.default,
4923
+ {
4924
+ variant: "caption",
4925
+ color: "text.secondary",
4926
+ sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
4927
+ },
4928
+ "No usage this month"
4929
+ );
4930
+ }
4931
+ return /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(
4932
+ import_Typography14.default,
4933
+ {
4934
+ variant: "caption",
4935
+ title: `${fmtInt(totalTokens)} tokens month to date`,
4936
+ sx: {
4937
+ mt: 0.25,
4938
+ fontSize: 11,
4939
+ fontWeight: 600,
4940
+ maxWidth: "100%",
4941
+ overflow: "hidden",
4942
+ textOverflow: "ellipsis",
4943
+ whiteSpace: "nowrap",
4944
+ fontVariantNumeric: "tabular-nums"
4945
+ }
4946
+ },
4947
+ fmtCompact(totalTokens),
4948
+ " tokens"
4949
+ ), /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, "month to date"));
4950
+ };
4951
+ return /* @__PURE__ */ import_react16.default.createElement(
4952
+ import_Box14.default,
4953
+ {
4954
+ sx: {
4955
+ display: "flex",
4956
+ flexDirection: "column",
4957
+ alignItems: "center",
4958
+ minWidth: 0,
4959
+ flex: 1
4960
+ }
4961
+ },
4962
+ /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { sx: { height, width: "100%" } }, renderPlot()),
4963
+ /* @__PURE__ */ import_react16.default.createElement(
4964
+ import_Typography14.default,
4965
+ {
4966
+ sx: (theme) => ({
4967
+ mt: 0.75,
4968
+ fontSize: 10.5,
4969
+ fontWeight: 700,
4970
+ letterSpacing: "0.08em",
4971
+ textTransform: "uppercase",
4972
+ color: theme.palette.text.secondary
4973
+ })
4974
+ },
4975
+ "Usage"
4976
+ ),
4977
+ renderCaption()
4978
+ );
4979
+ };
4853
4980
  var LiteLLMBudgetGauges = ({
4854
4981
  title = "Budget",
4855
4982
  size = 72,
@@ -4864,60 +4991,83 @@ var LiteLLMBudgetGauges = ({
4864
4991
  action
4865
4992
  }) => {
4866
4993
  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)([]);
4994
+ const [loading, setLoading] = (0, import_react16.useState)(true);
4995
+ const [error, setError] = (0, import_react16.useState)(null);
4996
+ const [user, setUser] = (0, import_react16.useState)(null);
4997
+ const [teams, setTeams] = (0, import_react16.useState)([]);
4998
+ const [keys, setKeys] = (0, import_react16.useState)([]);
4999
+ const [usageLoading, setUsageLoading] = (0, import_react16.useState)(true);
5000
+ const [dailyTokens, setDailyTokens] = (0, import_react16.useState)([]);
5001
+ const [mtdTokens, setMtdTokens] = (0, import_react16.useState)(0);
4872
5002
  const isControlled = expanded !== void 0;
4873
- const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react15.useState)(defaultExpanded);
5003
+ const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react16.useState)(defaultExpanded);
4874
5004
  const isExpanded = isControlled ? expanded : uncontrolledExpanded;
4875
- const toggleExpanded = (0, import_react15.useCallback)(() => {
5005
+ const toggleExpanded = (0, import_react16.useCallback)(() => {
4876
5006
  const next = !isExpanded;
4877
5007
  if (!isControlled) setUncontrolledExpanded(next);
4878
5008
  onExpandedChange?.(next);
4879
5009
  }, [isControlled, isExpanded, onExpandedChange]);
4880
5010
  const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
4881
- (0, import_react15.useEffect)(() => {
5011
+ (0, import_react16.useEffect)(() => {
4882
5012
  let cancelled = false;
4883
5013
  setLoading(true);
5014
+ setUsageLoading(true);
4884
5015
  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);
5016
+ const { startDate, endDate } = monthToDateRange();
5017
+ Promise.allSettled([
5018
+ api.getUserInfo(),
5019
+ api.getTeams(),
5020
+ api.listKeys(),
5021
+ api.getUsage(startDate, endDate)
5022
+ ]).then(([userResult, teamsResult, keysResult, usageResult]) => {
5023
+ if (cancelled) return;
5024
+ if (userResult.status === "fulfilled") {
5025
+ setUser(userResult.value);
5026
+ } else {
5027
+ setUser(null);
5028
+ setError(
5029
+ userResult.reason?.message ?? "Failed to load your LiteLLM profile"
5030
+ );
4899
5031
  }
4900
- );
5032
+ setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
5033
+ setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
5034
+ setLoading(false);
5035
+ if (usageResult.status === "fulfilled") {
5036
+ const daily = usageResult.value.daily_usage ?? [];
5037
+ setDailyTokens(
5038
+ daily.map((d) => ({
5039
+ date: d.date,
5040
+ input: d.prompt_tokens,
5041
+ output: d.completion_tokens
5042
+ }))
5043
+ );
5044
+ setMtdTokens(usageResult.value.total_tokens ?? 0);
5045
+ } else {
5046
+ setDailyTokens([]);
5047
+ setMtdTokens(0);
5048
+ }
5049
+ setUsageLoading(false);
5050
+ });
4901
5051
  return () => {
4902
5052
  cancelled = true;
4903
5053
  };
4904
5054
  }, [api]);
4905
- const summary = (0, import_react15.useMemo)(
5055
+ const summary = (0, import_react16.useMemo)(
4906
5056
  () => buildBudgetSummary(user, teams, keys, keys.length),
4907
5057
  [user, teams, keys]
4908
5058
  );
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)(
5059
+ const gauges = (0, import_react16.useMemo)(() => buildBudgetGauges(summary), [summary]);
5060
+ const headline = (0, import_react16.useMemo)(() => budgetHeadline(summary), [summary]);
5061
+ const expandedSummary = (0, import_react16.useMemo)(
4912
5062
  () => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
4913
5063
  [user, teams, keys, maxExpandedKeys]
4914
5064
  );
4915
- const expandedLimits = (0, import_react15.useMemo)(
5065
+ const expandedLimits = (0, import_react16.useMemo)(
4916
5066
  () => allBudgetLimits(expandedSummary),
4917
5067
  [expandedSummary]
4918
5068
  );
4919
5069
  const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
4920
- const resolvedCtas = (0, import_react15.useMemo)(() => {
5070
+ const resolvedCtas = (0, import_react16.useMemo)(() => {
4921
5071
  const list = ctas ?? DEFAULT_CTAS;
4922
5072
  return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
4923
5073
  }, [ctas, headline.count]);
@@ -4926,28 +5076,18 @@ var LiteLLMBudgetGauges = ({
4926
5076
  const key = `${cta.kind}-${index}`;
4927
5077
  switch (cta.kind) {
4928
5078
  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
- );
5079
+ return onCreateKey ? /* @__PURE__ */ import_react16.default.createElement(GenerateKeyButton, { key, size: "small", label, onClick: onCreateKey }) : /* @__PURE__ */ import_react16.default.createElement(GenerateKeyButton, { key, size: "small", label, to: `${moduleHref}?generate=1` });
4940
5080
  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);
5081
+ return /* @__PURE__ */ import_react16.default.createElement(import_Button6.default, { key, size: "small", variant: "outlined", component: import_core_components3.Link, to: moduleHref }, label);
4942
5082
  case "all-limits":
4943
- return /* @__PURE__ */ import_react15.default.createElement(
5083
+ return /* @__PURE__ */ import_react16.default.createElement(
4944
5084
  import_Button6.default,
4945
5085
  {
4946
5086
  key,
4947
5087
  size: "small",
4948
5088
  variant: "text",
4949
5089
  onClick: toggleExpanded,
4950
- endIcon: /* @__PURE__ */ import_react15.default.createElement(
5090
+ endIcon: /* @__PURE__ */ import_react16.default.createElement(
4951
5091
  import_icons_material10.ExpandMore,
4952
5092
  {
4953
5093
  sx: {
@@ -4965,14 +5105,22 @@ var LiteLLMBudgetGauges = ({
4965
5105
  }
4966
5106
  };
4967
5107
  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(
5108
+ return /* @__PURE__ */ import_react16.default.createElement(import_Paper10.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ import_react16.default.createElement(
4969
5109
  StatusPill,
4970
5110
  {
4971
5111
  label: summaryText,
4972
5112
  tone: budgetTone2(headline.closest?.pct ?? 0),
4973
5113
  dot: headline.count > 0
4974
5114
  }
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(
5115
+ )), loading && /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ import_react16.default.createElement(import_CircularProgress8.default, { size: 28 })), !loading && error && /* @__PURE__ */ import_react16.default.createElement(import_Alert8.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ import_react16.default.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref: keysTabHref })), /* @__PURE__ */ import_react16.default.createElement(
5116
+ UsageMiniChart,
5117
+ {
5118
+ data: dailyTokens,
5119
+ totalTokens: mtdTokens,
5120
+ loading: usageLoading,
5121
+ height: size
5122
+ }
5123
+ )), /* @__PURE__ */ import_react16.default.createElement(import_Collapse4.default, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ import_react16.default.createElement(
4976
5124
  import_Box14.default,
4977
5125
  {
4978
5126
  sx: {
@@ -4983,7 +5131,7 @@ var LiteLLMBudgetGauges = ({
4983
5131
  overflowY: "auto"
4984
5132
  }
4985
5133
  },
4986
- /* @__PURE__ */ import_react15.default.createElement(LimitListPanel, null, /* @__PURE__ */ import_react15.default.createElement(
5134
+ /* @__PURE__ */ import_react16.default.createElement(LimitListPanel, null, /* @__PURE__ */ import_react16.default.createElement(
4987
5135
  BudgetLimitList,
4988
5136
  {
4989
5137
  limits: expandedLimits,
@@ -4991,7 +5139,7 @@ var LiteLLMBudgetGauges = ({
4991
5139
  keysHref: keysTabHref
4992
5140
  }
4993
5141
  ))
4994
- ))), hasFooter && /* @__PURE__ */ import_react15.default.createElement(
5142
+ ))), hasFooter && /* @__PURE__ */ import_react16.default.createElement(
4995
5143
  import_Box14.default,
4996
5144
  {
4997
5145
  sx: (theme) => ({
@@ -5010,7 +5158,31 @@ var LiteLLMBudgetGauges = ({
5010
5158
  };
5011
5159
 
5012
5160
  // src/index.ts
5013
- init_GenerateKeyDialog();
5161
+ init_KeyFormDialog();
5162
+ init_GenerateKeyButton();
5163
+
5164
+ // src/components/GenerateKeyDialog.tsx
5165
+ var import_react17 = __toESM(require("react"));
5166
+ init_KeyFormDialog();
5167
+ var GenerateKeyDialog = (props) => /* @__PURE__ */ import_react17.default.createElement(
5168
+ KeyFormDialog,
5169
+ {
5170
+ open: props.open,
5171
+ onClose: props.onClose,
5172
+ mode: "create",
5173
+ keys: props.keys,
5174
+ models: props.models,
5175
+ teams: props.teams,
5176
+ username: props.username,
5177
+ keyGenerationSettings: props.keyGenerationSettings,
5178
+ onCreateKey: props.onGenerateKey,
5179
+ onUpdateKey: async () => {
5180
+ },
5181
+ onGetConfig: props.onGetConfig
5182
+ }
5183
+ );
5184
+
5185
+ // src/index.ts
5014
5186
  init_ManageTeamDialog();
5015
5187
  init_api();
5016
5188
  init_permissions();