@acarmisc/backstage-plugin-litellm 0.24.0 → 0.26.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
@@ -1051,26 +1051,6 @@ function BudgetCell({ spend, maxBudget }) {
1051
1051
  const pct = Math.min(100, spend / maxBudget * 100);
1052
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
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;
1073
- }
1074
1054
  function compareKeys(a, b, sortKey) {
1075
1055
  switch (sortKey) {
1076
1056
  case "alias":
@@ -1089,7 +1069,7 @@ function compareKeys(a, b, sortKey) {
1089
1069
  return 0;
1090
1070
  }
1091
1071
  }
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;
1072
+ 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_Skeleton2, import_InputAdornment, import_styles3, import_icons_material2, shortKeyId, formatDate, KeysTable;
1093
1073
  var init_KeysTable = __esm({
1094
1074
  "src/components/KeysTable.tsx"() {
1095
1075
  "use strict";
@@ -1111,17 +1091,12 @@ var init_KeysTable = __esm({
1111
1091
  import_DialogActions = __toESM(require("@mui/material/DialogActions"));
1112
1092
  import_TextField = __toESM(require("@mui/material/TextField"));
1113
1093
  import_CircularProgress = __toESM(require("@mui/material/CircularProgress"));
1114
- import_Autocomplete = __toESM(require("@mui/material/Autocomplete"));
1115
1094
  import_Skeleton2 = __toESM(require("@mui/material/Skeleton"));
1116
1095
  import_InputAdornment = __toESM(require("@mui/material/InputAdornment"));
1117
1096
  import_styles3 = require("@mui/material/styles");
1118
1097
  import_icons_material2 = require("@mui/icons-material");
1119
1098
  init_api();
1120
1099
  init_ui();
1121
- maskKey = (key) => {
1122
- if (key.length <= 8) return "***";
1123
- return `${key.slice(0, 4)}...${key.slice(-4)}`;
1124
- };
1125
1100
  shortKeyId = (token) => {
1126
1101
  if (!token) return "-";
1127
1102
  if (token.length <= 16) return token;
@@ -1134,33 +1109,19 @@ var init_KeysTable = __esm({
1134
1109
  return dateStr;
1135
1110
  }
1136
1111
  };
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
1112
  KeysTable = ({
1145
1113
  keys,
1146
- models,
1147
1114
  loading,
1148
1115
  onGenerateKeyClick,
1149
- onUpdateKey,
1116
+ onEditKey,
1150
1117
  onBlockKey,
1151
1118
  onUnblockKey,
1152
- onResetKeySpend,
1153
1119
  onDeleteKey,
1154
1120
  onPruneExpiredKeys
1155
1121
  }) => {
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
1122
  const [blockingKeyId, setBlockingKeyId] = (0, import_react3.useState)(null);
1160
1123
  const [deleteConfirmId, setDeleteConfirmId] = (0, import_react3.useState)(null);
1161
1124
  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
1125
  const [filterText, setFilterText] = (0, import_react3.useState)("");
1165
1126
  const [sortKey, setSortKey] = (0, import_react3.useState)("created");
1166
1127
  const [sortDirection, setSortDirection] = (0, import_react3.useState)("desc");
@@ -1185,27 +1146,8 @@ var init_KeysTable = __esm({
1185
1146
  return sortDirection === "asc" ? comparison : -comparison;
1186
1147
  });
1187
1148
  }, [keys, filterText, sortKey, sortDirection]);
1188
- const editSelectedModels = models.filter((m) => (editForm.models || []).includes(m.model_name));
1189
1149
  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
- }
1150
+ onEditKey(k);
1209
1151
  };
1210
1152
  const handleToggleBlock = async (key) => {
1211
1153
  const keyId = key.token ?? key.key;
@@ -1220,17 +1162,6 @@ var init_KeysTable = __esm({
1220
1162
  setBlockingKeyId(null);
1221
1163
  }
1222
1164
  };
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
1165
  const handleConfirmDelete = async () => {
1235
1166
  if (!deleteConfirmId) return;
1236
1167
  setDeleteSubmitting(true);
@@ -1261,12 +1192,6 @@ var init_KeysTable = __esm({
1261
1192
  setPruneConfirmCount(null);
1262
1193
  }
1263
1194
  };
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
1195
  const renderTableBody = () => {
1271
1196
  if (loading) {
1272
1197
  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 })));
@@ -1438,75 +1363,7 @@ var init_KeysTable = __esm({
1438
1363
  ))
1439
1364
  },
1440
1365
  /* @__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(
1366
+ ), /* @__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(
1510
1367
  import_Button2.default,
1511
1368
  {
1512
1369
  onClick: handleConfirmDelete,
@@ -1543,7 +1400,7 @@ var init_format = __esm({
1543
1400
  }
1544
1401
  });
1545
1402
 
1546
- // src/components/GenerateKeyDialog.tsx
1403
+ // src/components/KeyFormDialog.tsx
1547
1404
  function trimSlash(url) {
1548
1405
  return url.replace(/\/+$/, "");
1549
1406
  }
@@ -1636,17 +1493,17 @@ function teamHelperText(teamError, teamRequired) {
1636
1493
  if (teamRequired) return "Bind this key to a team for scoped access";
1637
1494
  return "Optional: bind this key to a specific team for scoped access";
1638
1495
  }
1639
- function budgetHelperText(budgetInvalid, budgetEstimate) {
1640
- if (budgetInvalid) return 'Enter a positive budget or tick "Unlimited"';
1496
+ function budgetHelperText(budgetInvalid, budgetEstimate, unlimited) {
1497
+ if (budgetInvalid && !unlimited) return 'Enter a positive budget or tick "Unlimited"';
1641
1498
  if (budgetEstimate !== null) return `\u2248 ${fmtInt(budgetEstimate)} tokens at the selected model's rate`;
1642
1499
  return void 0;
1643
1500
  }
1644
- function fmtCost2(perToken) {
1501
+ function fmtCost(perToken) {
1645
1502
  if (!perToken) return null;
1646
1503
  const per1k = perToken * 1e3;
1647
1504
  return per1k < 0.01 ? `$${(perToken * 1e6).toFixed(2)}/M` : `$${per1k.toFixed(3)}/1K`;
1648
1505
  }
1649
- function formatContextWindow2(maxInput, maxOutput) {
1506
+ function formatContextWindow(maxInput, maxOutput) {
1650
1507
  if (!maxInput && !maxOutput) return null;
1651
1508
  const fmt = (n) => {
1652
1509
  if (!n) return null;
@@ -1661,9 +1518,9 @@ function formatContextWindow2(maxInput, maxOutput) {
1661
1518
  if (outPart) return `ctx ${outPart} out`;
1662
1519
  return null;
1663
1520
  }
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"() {
1521
+ 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_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;
1522
+ var init_KeyFormDialog = __esm({
1523
+ "src/components/KeyFormDialog.tsx"() {
1667
1524
  "use strict";
1668
1525
  import_react4 = __toESM(require("react"));
1669
1526
  import_Box4 = __toESM(require("@mui/material/Box"));
@@ -1677,7 +1534,7 @@ var init_GenerateKeyDialog = __esm({
1677
1534
  import_Button3 = __toESM(require("@mui/material/Button"));
1678
1535
  import_IconButton2 = __toESM(require("@mui/material/IconButton"));
1679
1536
  import_CircularProgress2 = __toESM(require("@mui/material/CircularProgress"));
1680
- import_Autocomplete2 = __toESM(require("@mui/material/Autocomplete"));
1537
+ import_Autocomplete = __toESM(require("@mui/material/Autocomplete"));
1681
1538
  import_Checkbox = __toESM(require("@mui/material/Checkbox"));
1682
1539
  import_FormControlLabel = __toESM(require("@mui/material/FormControlLabel"));
1683
1540
  import_Alert = __toESM(require("@mui/material/Alert"));
@@ -1693,8 +1550,8 @@ var init_GenerateKeyDialog = __esm({
1693
1550
  const hash = Math.random().toString(36).slice(2, 8);
1694
1551
  return `${base}-${hash}`;
1695
1552
  };
1696
- emptyForm = () => ({
1697
- alias: "",
1553
+ createForm = (username) => ({
1554
+ alias: generateDefaultAlias(username),
1698
1555
  models: [],
1699
1556
  duration: "30d",
1700
1557
  max_budget: 100,
@@ -1703,6 +1560,13 @@ var init_GenerateKeyDialog = __esm({
1703
1560
  team_id: void 0,
1704
1561
  key_type: "llm_api"
1705
1562
  });
1563
+ editForm = (k) => ({
1564
+ key_alias: k.key_alias ?? "",
1565
+ models: k.models ?? [],
1566
+ max_budget: k.max_budget,
1567
+ tpm_limit: k.tpm_limit,
1568
+ rpm_limit: k.rpm_limit
1569
+ });
1706
1570
  ALL_PROXY_MODELS = "all-proxy-models";
1707
1571
  SNIPPET_FILE_HINTS = {
1708
1572
  opencode: "Add to ~/.config/opencode/opencode.json",
@@ -1749,46 +1613,79 @@ var init_GenerateKeyDialog = __esm({
1749
1613
  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.")
1750
1614
  ));
1751
1615
  };
1752
- GenerateKeyDialog = ({
1616
+ KeyFormDialog = ({
1753
1617
  open,
1754
1618
  onClose,
1619
+ mode,
1620
+ keyToEdit = null,
1755
1621
  keys,
1756
1622
  models,
1757
1623
  teams,
1758
1624
  username,
1759
1625
  keyGenerationSettings,
1760
- onGenerateKey,
1626
+ onCreateKey,
1627
+ onUpdateKey,
1628
+ onResetKeySpend,
1761
1629
  onGetConfig
1762
1630
  }) => {
1763
- const [formData, setFormData] = (0, import_react4.useState)(emptyForm());
1631
+ const isCreate = mode === "create";
1632
+ const [generateForm, setGenerateForm] = (0, import_react4.useState)(createForm(username));
1633
+ const [editFormState, setEditFormState] = (0, import_react4.useState)({});
1764
1634
  const [unlimitedBudget, setUnlimitedBudget] = (0, import_react4.useState)(false);
1765
1635
  const [submitting, setSubmitting] = (0, import_react4.useState)(false);
1766
1636
  const [newKeyValue, setNewKeyValue] = (0, import_react4.useState)(null);
1767
1637
  const [newKeySnippets, setNewKeySnippets] = (0, import_react4.useState)(null);
1768
1638
  const [newKeyModel, setNewKeyModel] = (0, import_react4.useState)("");
1769
1639
  const [generateError, setGenerateError] = (0, import_react4.useState)(null);
1640
+ const [editError, setEditError] = (0, import_react4.useState)(null);
1641
+ const [resetSpendConfirm, setResetSpendConfirm] = (0, import_react4.useState)(false);
1642
+ const [resetSpendSubmitting, setResetSpendSubmitting] = (0, import_react4.useState)(false);
1770
1643
  (0, import_react4.useEffect)(() => {
1771
1644
  if (open) {
1772
- setFormData({ ...emptyForm(), alias: generateDefaultAlias(username) });
1773
- setUnlimitedBudget(false);
1645
+ if (isCreate) {
1646
+ setGenerateForm(createForm(username));
1647
+ } else if (keyToEdit) {
1648
+ setEditFormState(editForm(keyToEdit));
1649
+ setUnlimitedBudget(
1650
+ keyGenerationSettings?.allowUnlimitedBudget === true && (keyToEdit.max_budget === null || keyToEdit.max_budget === void 0)
1651
+ );
1652
+ } else {
1653
+ setEditFormState({});
1654
+ setUnlimitedBudget(false);
1655
+ }
1774
1656
  setNewKeyValue(null);
1775
1657
  setNewKeySnippets(null);
1776
1658
  setNewKeyModel("");
1777
1659
  setGenerateError(null);
1660
+ setEditError(null);
1661
+ setResetSpendConfirm(false);
1778
1662
  }
1779
- }, [open, username]);
1663
+ }, [open, isCreate, keyToEdit, username, keyGenerationSettings?.allowUnlimitedBudget]);
1780
1664
  const allowUnlimitedBudget = keyGenerationSettings?.allowUnlimitedBudget ?? false;
1781
1665
  const teamRequired = keyGenerationSettings?.teamRequired ?? true;
1782
- const selectedTeam = teams.find((t) => t.team_id === formData.team_id) ?? null;
1666
+ const formData = isCreate ? generateForm : editFormState;
1667
+ const setFormData = (patch) => {
1668
+ if (isCreate) {
1669
+ setGenerateForm((prev) => ({ ...prev, ...patch }));
1670
+ } else {
1671
+ setEditFormState((prev) => ({ ...prev, ...patch }));
1672
+ }
1673
+ };
1674
+ const currentAlias = (isCreate ? generateForm.alias : editFormState.key_alias) ?? "";
1675
+ const setAliasField = (value) => {
1676
+ if (isCreate) setFormData({ alias: value });
1677
+ else setFormData({ key_alias: value });
1678
+ };
1679
+ const selectedTeam = teams.find((t) => t.team_id === (isCreate ? generateForm.team_id : void 0)) ?? null;
1783
1680
  const availableModels = (0, import_react4.useMemo)(() => {
1784
1681
  return models.filter((m) => isModelAllowedByTeam(m, selectedTeam?.models));
1785
1682
  }, [models, selectedTeam]);
1786
1683
  const selectedModels = availableModels.filter((m) => (formData.models || []).includes(m.model_name));
1787
- const aliasError = !(formData.alias || "").trim();
1788
- const teamError = teamRequired && !formData.team_id;
1684
+ const aliasError = !currentAlias.trim();
1685
+ const teamError = isCreate && teamRequired && !generateForm.team_id;
1789
1686
  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);
1687
+ const canSubmit = !aliasError && !teamError && !budgetInvalid && !submitting;
1688
+ const aliasDuplicate = !!currentAlias && keys.some((k) => k.key_alias === currentAlias && k !== keyToEdit);
1792
1689
  const budgetEstimate = (0, import_react4.useMemo)(() => {
1793
1690
  if (unlimitedBudget || budgetInvalid) return null;
1794
1691
  const inputCosts = selectedModels.map((m) => m.input_cost_per_token).filter((c) => typeof c === "number" && c > 0);
@@ -1801,11 +1698,11 @@ var init_GenerateKeyDialog = __esm({
1801
1698
  setGenerateError(null);
1802
1699
  try {
1803
1700
  const request = {
1804
- ...formData,
1805
- max_budget: unlimitedBudget ? null : formData.max_budget
1701
+ ...generateForm,
1702
+ max_budget: unlimitedBudget ? null : generateForm.max_budget
1806
1703
  };
1807
- const response = await onGenerateKey(request);
1808
- const model = formData.models?.[0] ?? availableModels[0]?.model_name ?? "";
1704
+ const response = await onCreateKey(request);
1705
+ const model = generateForm.models?.[0] ?? availableModels[0]?.model_name ?? "";
1809
1706
  setNewKeyValue(response.key);
1810
1707
  setNewKeyModel(model);
1811
1708
  setNewKeySnippets(null);
@@ -1814,7 +1711,7 @@ var init_GenerateKeyDialog = __esm({
1814
1711
  setNewKeySnippets(buildSnippets(config.baseUrl, response.key, model));
1815
1712
  } catch {
1816
1713
  }
1817
- setFormData(emptyForm());
1714
+ setGenerateForm(createForm(username));
1818
1715
  setUnlimitedBudget(false);
1819
1716
  } catch (error) {
1820
1717
  setGenerateError(error?.message ?? "Failed to generate key");
@@ -1822,27 +1719,58 @@ var init_GenerateKeyDialog = __esm({
1822
1719
  setSubmitting(false);
1823
1720
  }
1824
1721
  };
1722
+ const handleUpdate = async () => {
1723
+ if (!keyToEdit) return;
1724
+ setSubmitting(true);
1725
+ setEditError(null);
1726
+ try {
1727
+ const request = {
1728
+ ...editFormState,
1729
+ max_budget: unlimitedBudget ? null : editFormState.max_budget
1730
+ };
1731
+ await onUpdateKey(keyToEdit.token ?? keyToEdit.key, request);
1732
+ onClose();
1733
+ } catch (error) {
1734
+ setEditError(error?.message ?? "Failed to update key");
1735
+ } finally {
1736
+ setSubmitting(false);
1737
+ }
1738
+ };
1739
+ const handleResetSpend = async () => {
1740
+ if (!keyToEdit || !onResetKeySpend) return;
1741
+ setResetSpendSubmitting(true);
1742
+ try {
1743
+ await onResetKeySpend(keyToEdit.token ?? keyToEdit.key);
1744
+ setResetSpendConfirm(false);
1745
+ onClose();
1746
+ } finally {
1747
+ setResetSpendSubmitting(false);
1748
+ }
1749
+ };
1825
1750
  const handleClose = () => {
1826
1751
  onClose();
1827
1752
  setGenerateError(null);
1753
+ setEditError(null);
1828
1754
  setNewKeyValue(null);
1829
1755
  setNewKeySnippets(null);
1830
- setFormData(emptyForm());
1756
+ setGenerateForm(createForm(username));
1757
+ setEditFormState({});
1831
1758
  setUnlimitedBudget(false);
1759
+ setResetSpendConfirm(false);
1832
1760
  };
1833
1761
  const copyToClipboard = (text) => {
1834
1762
  navigator.clipboard.writeText(text);
1835
1763
  };
1836
1764
  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);
1765
+ const inCost = fmtCost(m.input_cost_per_token);
1766
+ const outCost = fmtCost(m.output_cost_per_token);
1767
+ const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
1840
1768
  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"));
1841
1769
  };
1842
1770
  const renderTeamField = () => {
1843
1771
  if (teams.length > 0) {
1844
1772
  return /* @__PURE__ */ import_react4.default.createElement(
1845
- import_Autocomplete2.default,
1773
+ import_Autocomplete.default,
1846
1774
  {
1847
1775
  options: teams,
1848
1776
  getOptionLabel: (t) => t.team_alias || t.team_id,
@@ -1852,28 +1780,61 @@ var init_GenerateKeyDialog = __esm({
1852
1780
  const model = models.find((m) => m.model_name === name);
1853
1781
  return model ? isModelAllowedByTeam(model, team?.models) : false;
1854
1782
  });
1855
- setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
1783
+ setFormData({ team_id: team?.team_id, models: restrictedModels });
1856
1784
  },
1785
+ disabled: !isCreate,
1857
1786
  renderInput: (params) => /* @__PURE__ */ import_react4.default.createElement(
1858
1787
  import_TextField2.default,
1859
1788
  {
1860
1789
  ...params,
1861
1790
  label: "Team",
1862
1791
  error: teamError,
1863
- helperText: teamHelperText(teamError, teamRequired),
1864
- required: teamRequired,
1792
+ helperText: isCreate ? teamHelperText(teamError, teamRequired) : "Team binding is fixed after creation \u2014 delete and recreate the key to change it",
1793
+ required: isCreate && teamRequired,
1865
1794
  fullWidth: true
1866
1795
  }
1867
1796
  )
1868
1797
  }
1869
1798
  );
1870
1799
  }
1871
- if (teamRequired) {
1800
+ if (teamRequired && isCreate) {
1872
1801
  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.");
1873
1802
  }
1874
1803
  return null;
1875
1804
  };
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(
1805
+ const editKeyId = keyToEdit?.token ?? keyToEdit?.key ?? "";
1806
+ const renderDangerZone = () => {
1807
+ if (isCreate || !onResetKeySpend) return null;
1808
+ return /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ import_react4.default.createElement(
1809
+ import_Button3.default,
1810
+ {
1811
+ size: "small",
1812
+ color: "warning",
1813
+ variant: "outlined",
1814
+ onClick: () => setResetSpendConfirm(true)
1815
+ },
1816
+ "Reset Spend to $0"
1817
+ ) : /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ import_react4.default.createElement(
1818
+ import_Button3.default,
1819
+ {
1820
+ size: "small",
1821
+ color: "warning",
1822
+ variant: "contained",
1823
+ disabled: resetSpendSubmitting,
1824
+ onClick: handleResetSpend
1825
+ },
1826
+ resetSpendSubmitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress2.default, { size: 16 }) : "Confirm"
1827
+ ), /* @__PURE__ */ import_react4.default.createElement(import_Button3.default, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel")));
1828
+ };
1829
+ const maskKey = (key) => {
1830
+ if (key.length <= 8) return "***";
1831
+ return `${key.slice(0, 4)}...${key.slice(-4)}`;
1832
+ };
1833
+ let dialogTitle = "Edit Key";
1834
+ if (newKeyValue) dialogTitle = "Key Generated";
1835
+ else if (isCreate) dialogTitle = "Generate New Key";
1836
+ const submitLabel = isCreate ? "Generate" : "Save";
1837
+ 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, dialogTitle), /* @__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(
1877
1838
  import_Box4.default,
1878
1839
  {
1879
1840
  display: "flex",
@@ -1922,14 +1883,15 @@ var init_GenerateKeyDialog = __esm({
1922
1883
  newKeySnippets.publicEndpoint
1923
1884
  ),
1924
1885
  /* @__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(
1886
+ )), 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 }, !isCreate && /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ import_react4.default.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editKeyId))), generateError && /* @__PURE__ */ import_react4.default.createElement(import_Alert.default, { severity: "error", onClose: () => setGenerateError(null) }, generateError), editError && /* @__PURE__ */ import_react4.default.createElement(import_Alert.default, { severity: "error", onClose: () => setEditError(null) }, editError), /* @__PURE__ */ import_react4.default.createElement(
1926
1887
  import_TextField2.default,
1927
1888
  {
1928
1889
  label: "Alias",
1929
- value: formData.alias || "",
1890
+ value: currentAlias,
1930
1891
  onChange: (e) => {
1931
- setFormData({ ...formData, alias: e.target.value });
1892
+ setAliasField(e.target.value);
1932
1893
  setGenerateError(null);
1894
+ setEditError(null);
1933
1895
  },
1934
1896
  error: aliasError,
1935
1897
  color: aliasDuplicate ? "warning" : void 0,
@@ -1937,13 +1899,13 @@ var init_GenerateKeyDialog = __esm({
1937
1899
  required: true,
1938
1900
  fullWidth: true
1939
1901
  }
1940
- ), /* @__PURE__ */ import_react4.default.createElement(
1902
+ ), !isCreate ? null : /* @__PURE__ */ import_react4.default.createElement(
1941
1903
  import_TextField2.default,
1942
1904
  {
1943
1905
  select: true,
1944
1906
  label: "Duration",
1945
- value: formData.duration || "30d",
1946
- onChange: (e) => setFormData({ ...formData, duration: e.target.value }),
1907
+ value: generateForm.duration || "30d",
1908
+ onChange: (e) => setFormData({ duration: e.target.value }),
1947
1909
  fullWidth: true
1948
1910
  },
1949
1911
  /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "1d" }, "1 Day"),
@@ -1952,14 +1914,14 @@ var init_GenerateKeyDialog = __esm({
1952
1914
  /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "90d" }, "90 Days"),
1953
1915
  /* @__PURE__ */ import_react4.default.createElement(import_MenuItem.default, { value: "1y" }, "1 Year")
1954
1916
  ), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ import_react4.default.createElement(
1955
- import_Autocomplete2.default,
1917
+ import_Autocomplete.default,
1956
1918
  {
1957
1919
  multiple: true,
1958
1920
  options: availableModels,
1959
1921
  groupBy: (m) => m.mode || "other",
1960
1922
  getOptionLabel: (m) => m.model_name,
1961
1923
  value: selectedModels,
1962
- onChange: (_e, selected) => setFormData({ ...formData, models: selected.map((m) => m.model_name) }),
1924
+ onChange: (_e, selected) => setFormData({ models: selected.map((m) => m.model_name) }),
1963
1925
  renderOption: (props, m) => /* @__PURE__ */ import_react4.default.createElement("li", { ...props }, modelOption(m)),
1964
1926
  renderInput: (params) => /* @__PURE__ */ import_react4.default.createElement(
1965
1927
  import_TextField2.default,
@@ -1981,9 +1943,9 @@ var init_GenerateKeyDialog = __esm({
1981
1943
  onChange: (e) => {
1982
1944
  setUnlimitedBudget(e.target.checked);
1983
1945
  if (e.target.checked) {
1984
- setFormData({ ...formData, max_budget: null });
1946
+ setFormData({ max_budget: null });
1985
1947
  } else {
1986
- setFormData({ ...formData, max_budget: 100 });
1948
+ setFormData({ max_budget: editFormState.max_budget ?? generateForm.max_budget ?? 100 });
1987
1949
  }
1988
1950
  }
1989
1951
  }
@@ -1996,9 +1958,9 @@ var init_GenerateKeyDialog = __esm({
1996
1958
  label: "Max Budget (USD)",
1997
1959
  type: "number",
1998
1960
  value: unlimitedBudget ? "" : formData.max_budget ?? "",
1999
- onChange: (e) => setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
1961
+ onChange: (e) => setFormData({ max_budget: e.target.value ? Number(e.target.value) : void 0 }),
2000
1962
  error: budgetInvalid,
2001
- helperText: budgetHelperText(budgetInvalid, budgetEstimate),
1963
+ helperText: budgetHelperText(budgetInvalid, budgetEstimate, unlimitedBudget),
2002
1964
  disabled: unlimitedBudget,
2003
1965
  required: true,
2004
1966
  fullWidth: true
@@ -2009,7 +1971,7 @@ var init_GenerateKeyDialog = __esm({
2009
1971
  label: "TPM Limit",
2010
1972
  type: "number",
2011
1973
  value: formData.tpm_limit ?? "",
2012
- onChange: (e) => setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
1974
+ onChange: (e) => setFormData({ tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
2013
1975
  helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
2014
1976
  fullWidth: true
2015
1977
  }
@@ -2019,26 +1981,26 @@ var init_GenerateKeyDialog = __esm({
2019
1981
  label: "RPM Limit",
2020
1982
  type: "number",
2021
1983
  value: formData.rpm_limit ?? "",
2022
- onChange: (e) => setFormData({ ...formData, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
1984
+ onChange: (e) => setFormData({ rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
2023
1985
  helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
2024
1986
  fullWidth: true
2025
1987
  }
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(
1988
+ ))), renderDangerZone())), /* @__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(
2027
1989
  import_Button3.default,
2028
1990
  {
2029
- onClick: handleGenerate,
1991
+ onClick: isCreate ? handleGenerate : handleUpdate,
2030
1992
  variant: "contained",
2031
1993
  color: "primary",
2032
- disabled: !canGenerate
1994
+ disabled: !canSubmit
2033
1995
  },
2034
- submitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress2.default, { size: 24 }) : "Generate"
1996
+ submitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress2.default, { size: 24 }) : submitLabel
2035
1997
  ))));
2036
1998
  };
2037
1999
  }
2038
2000
  });
2039
2001
 
2040
2002
  // 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;
2003
+ var import_react5, 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
2004
  var init_ManageTeamDialog = __esm({
2043
2005
  "src/components/ManageTeamDialog.tsx"() {
2044
2006
  "use strict";
@@ -2052,7 +2014,7 @@ var init_ManageTeamDialog = __esm({
2052
2014
  import_Alert2 = __toESM(require("@mui/material/Alert"));
2053
2015
  import_Checkbox2 = __toESM(require("@mui/material/Checkbox"));
2054
2016
  import_FormControlLabel2 = __toESM(require("@mui/material/FormControlLabel"));
2055
- import_Autocomplete3 = __toESM(require("@mui/material/Autocomplete"));
2017
+ import_Autocomplete2 = __toESM(require("@mui/material/Autocomplete"));
2056
2018
  import_CircularProgress3 = __toESM(require("@mui/material/CircularProgress"));
2057
2019
  import_MenuItem2 = __toESM(require("@mui/material/MenuItem"));
2058
2020
  import_Box5 = __toESM(require("@mui/material/Box"));
@@ -2317,7 +2279,7 @@ var init_ManageTeamDialog = __esm({
2317
2279
  fullWidth: true
2318
2280
  }
2319
2281
  ), /* @__PURE__ */ import_react5.default.createElement(
2320
- import_Autocomplete3.default,
2282
+ import_Autocomplete2.default,
2321
2283
  {
2322
2284
  multiple: true,
2323
2285
  options: modelNames,
@@ -2390,7 +2352,7 @@ var init_ManageTeamDialog = __esm({
2390
2352
  },
2391
2353
  /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
2392
2354
  ))))))), /* @__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,
2355
+ import_Autocomplete2.default,
2394
2356
  {
2395
2357
  freeSolo: true,
2396
2358
  autoHighlight: true,
@@ -2449,7 +2411,7 @@ var init_ManageTeamDialog = __esm({
2449
2411
  },
2450
2412
  "Add"
2451
2413
  ))), 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,
2414
+ import_Autocomplete2.default,
2453
2415
  {
2454
2416
  multiple: true,
2455
2417
  options: kbOptions,
@@ -2468,7 +2430,7 @@ var init_ManageTeamDialog = __esm({
2468
2430
  },
2469
2431
  kbBusy ? "Saving\u2026" : "Save knowledge bases"
2470
2432
  ))), 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,
2433
+ import_Autocomplete2.default,
2472
2434
  {
2473
2435
  multiple: true,
2474
2436
  options: mcpOptions,
@@ -2931,6 +2893,13 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
2931
2893
  teams: teamLimits
2932
2894
  };
2933
2895
  }
2896
+ function allBudgetLimits(summary) {
2897
+ return [
2898
+ ...summary.keys,
2899
+ ...summary.user ? [summary.user] : [],
2900
+ ...summary.teams
2901
+ ];
2902
+ }
2934
2903
  function buildBudgetGauges(summary) {
2935
2904
  const byLevel = {
2936
2905
  key: summary.keys,
@@ -2981,7 +2950,7 @@ var init_budget = __esm({
2981
2950
  }
2982
2951
  });
2983
2952
 
2984
- // src/components/LiteLLMBudgetWidget.tsx
2953
+ // src/components/BudgetLimitList.tsx
2985
2954
  function levelToneColor(tone, theme) {
2986
2955
  switch (tone) {
2987
2956
  case "success":
@@ -2996,25 +2965,125 @@ function levelToneColor(tone, theme) {
2996
2965
  return theme.palette.primary.main;
2997
2966
  }
2998
2967
  }
2999
- var import_react7, import_Paper5, import_Box7, import_Typography7, import_CircularProgress4, import_Alert3, import_Collapse, import_IconButton4, import_icons_material5, import_styles4, import_core_plugin_api3, import_core_components, LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, MoreKeysNote, HierarchyNote, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
3000
- var init_LiteLLMBudgetWidget = __esm({
3001
- "src/components/LiteLLMBudgetWidget.tsx"() {
2968
+ var import_react7, import_Box7, import_Paper5, import_Typography7, import_styles4, import_core_components, LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
2969
+ var init_BudgetLimitList = __esm({
2970
+ "src/components/BudgetLimitList.tsx"() {
3002
2971
  "use strict";
3003
2972
  import_react7 = __toESM(require("react"));
3004
- import_Paper5 = __toESM(require("@mui/material/Paper"));
3005
2973
  import_Box7 = __toESM(require("@mui/material/Box"));
2974
+ import_Paper5 = __toESM(require("@mui/material/Paper"));
3006
2975
  import_Typography7 = __toESM(require("@mui/material/Typography"));
2976
+ import_styles4 = require("@mui/material/styles");
2977
+ import_core_components = require("@backstage/core-components");
2978
+ init_budget();
2979
+ init_format();
2980
+ init_ui();
2981
+ LimitCard = ({ limit }) => {
2982
+ const tone = budgetTone2(limit.pct);
2983
+ const pct = Math.round(limit.pct);
2984
+ const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
2985
+ 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(
2986
+ import_Typography7.default,
2987
+ {
2988
+ variant: "body2",
2989
+ sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
2990
+ title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
2991
+ },
2992
+ limit.label
2993
+ ), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
2994
+ import_Typography7.default,
2995
+ {
2996
+ variant: "caption",
2997
+ color: "text.secondary",
2998
+ sx: {
2999
+ fontSize: 10.5,
3000
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3001
+ display: "block",
3002
+ overflow: "hidden",
3003
+ textOverflow: "ellipsis",
3004
+ whiteSpace: "nowrap"
3005
+ }
3006
+ },
3007
+ limit.sublabel
3008
+ )), 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(
3009
+ import_Box7.default,
3010
+ {
3011
+ sx: {
3012
+ display: "flex",
3013
+ justifyContent: "space-between",
3014
+ alignItems: "center",
3015
+ gap: 1,
3016
+ mt: 0.75
3017
+ }
3018
+ },
3019
+ /* @__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`)),
3020
+ /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3021
+ ));
3022
+ };
3023
+ TaggedLimit = ({ limit }) => {
3024
+ const theme = (0, import_styles4.useTheme)();
3025
+ const tone = budgetTone2(limit.pct);
3026
+ return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
3027
+ import_Typography7.default,
3028
+ {
3029
+ sx: {
3030
+ fontSize: 11,
3031
+ fontWeight: 700,
3032
+ letterSpacing: "0.08em",
3033
+ textTransform: "uppercase",
3034
+ color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
3035
+ mb: 0.5
3036
+ }
3037
+ },
3038
+ limit.kind
3039
+ ), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
3040
+ };
3041
+ MoreKeysNote = ({
3042
+ count,
3043
+ href,
3044
+ label
3045
+ }) => {
3046
+ const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
3047
+ if (!href) {
3048
+ return /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, text);
3049
+ }
3050
+ return /* @__PURE__ */ import_react7.default.createElement(import_core_components.Link, { to: href, variant: "caption" }, text, " \u2192");
3051
+ };
3052
+ 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 }));
3053
+ LimitListPanel = ({ children }) => /* @__PURE__ */ import_react7.default.createElement(
3054
+ import_Box7.default,
3055
+ {
3056
+ sx: (theme) => ({
3057
+ px: 1.5,
3058
+ py: 1.5,
3059
+ borderRadius: 1.5,
3060
+ bgcolor: (0, import_styles4.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.02)
3061
+ })
3062
+ },
3063
+ children
3064
+ );
3065
+ }
3066
+ });
3067
+
3068
+ // src/components/LiteLLMBudgetWidget.tsx
3069
+ 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;
3070
+ var init_LiteLLMBudgetWidget = __esm({
3071
+ "src/components/LiteLLMBudgetWidget.tsx"() {
3072
+ "use strict";
3073
+ import_react8 = __toESM(require("react"));
3074
+ import_Paper6 = __toESM(require("@mui/material/Paper"));
3075
+ import_Box8 = __toESM(require("@mui/material/Box"));
3076
+ import_Typography8 = __toESM(require("@mui/material/Typography"));
3007
3077
  import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
3008
3078
  import_Alert3 = __toESM(require("@mui/material/Alert"));
3009
3079
  import_Collapse = __toESM(require("@mui/material/Collapse"));
3010
3080
  import_IconButton4 = __toESM(require("@mui/material/IconButton"));
3011
3081
  import_icons_material5 = require("@mui/icons-material");
3012
- import_styles4 = require("@mui/material/styles");
3082
+ import_styles5 = require("@mui/material/styles");
3013
3083
  import_core_plugin_api3 = require("@backstage/core-plugin-api");
3014
- import_core_components = require("@backstage/core-components");
3015
3084
  init_api();
3016
- init_format();
3017
3085
  init_ui();
3086
+ init_BudgetLimitList();
3018
3087
  init_budget();
3019
3088
  LevelFrame = ({
3020
3089
  rank,
@@ -3024,8 +3093,8 @@ var init_LiteLLMBudgetWidget = __esm({
3024
3093
  tone,
3025
3094
  isLast,
3026
3095
  children
3027
- }) => /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ import_react7.default.createElement(
3028
- import_Box7.default,
3096
+ }) => /* @__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(
3097
+ import_Box8.default,
3029
3098
  {
3030
3099
  sx: (theme) => ({
3031
3100
  width: 26,
@@ -3034,7 +3103,7 @@ var init_LiteLLMBudgetWidget = __esm({
3034
3103
  display: "flex",
3035
3104
  alignItems: "center",
3036
3105
  justifyContent: "center",
3037
- bgcolor: (0, import_styles4.alpha)(levelToneColor(tone, theme), 0.14),
3106
+ bgcolor: (0, import_styles5.alpha)(levelToneColor(tone, theme), 0.14),
3038
3107
  color: levelToneColor(tone, theme),
3039
3108
  fontSize: 12,
3040
3109
  fontWeight: 700,
@@ -3042,18 +3111,18 @@ var init_LiteLLMBudgetWidget = __esm({
3042
3111
  })
3043
3112
  },
3044
3113
  rank
3045
- ), /* @__PURE__ */ import_react7.default.createElement(
3046
- import_Box7.default,
3114
+ ), /* @__PURE__ */ import_react8.default.createElement(
3115
+ import_Box8.default,
3047
3116
  {
3048
3117
  sx: (theme) => ({
3049
3118
  flex: 1,
3050
3119
  width: 2,
3051
3120
  minHeight: 14,
3052
- bgcolor: isLast ? "transparent" : (0, import_styles4.alpha)(theme.palette.text.primary, 0.12)
3121
+ bgcolor: isLast ? "transparent" : (0, import_styles5.alpha)(theme.palette.text.primary, 0.12)
3053
3122
  })
3054
3123
  }
3055
- )), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ import_react7.default.createElement(
3056
- import_Typography7.default,
3124
+ )), /* @__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(
3125
+ import_Typography8.default,
3057
3126
  {
3058
3127
  sx: (theme) => ({
3059
3128
  fontSize: 12.5,
@@ -3064,102 +3133,42 @@ var init_LiteLLMBudgetWidget = __esm({
3064
3133
  })
3065
3134
  },
3066
3135
  name
3067
- ), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ import_react7.default.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
3068
- EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react7.default.createElement(
3069
- import_Paper5.default,
3136
+ ), /* @__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)));
3137
+ EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react8.default.createElement(
3138
+ import_Paper6.default,
3070
3139
  {
3071
3140
  variant: "outlined",
3072
3141
  sx: (theme) => ({
3073
3142
  px: 1.5,
3074
3143
  py: 1.25,
3075
3144
  borderRadius: 1.5,
3076
- bgcolor: (0, import_styles4.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
3145
+ bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
3077
3146
  })
3078
3147
  },
3079
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3148
+ /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
3080
3149
  );
3081
- LimitCard = ({ limit }) => {
3082
- const tone = budgetTone2(limit.pct);
3083
- const pct = Math.round(limit.pct);
3084
- const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
3085
- return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react7.default.createElement(
3086
- import_Typography7.default,
3087
- {
3088
- variant: "body2",
3089
- sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
3090
- title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
3091
- },
3092
- limit.label
3093
- ), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
3094
- import_Typography7.default,
3095
- {
3096
- variant: "caption",
3097
- color: "text.secondary",
3098
- sx: {
3099
- fontSize: 10.5,
3100
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3101
- display: "block",
3102
- overflow: "hidden",
3103
- textOverflow: "ellipsis",
3104
- whiteSpace: "nowrap"
3105
- }
3106
- },
3107
- limit.sublabel
3108
- )), limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 1 } }, /* @__PURE__ */ import_react7.default.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ import_react7.default.createElement(
3109
- import_Box7.default,
3110
- {
3111
- sx: {
3112
- display: "flex",
3113
- justifyContent: "space-between",
3114
- alignItems: "center",
3115
- gap: 1,
3116
- mt: 0.75
3117
- }
3118
- },
3119
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, "Hidden by admin") : /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
3120
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
3121
- ));
3122
- };
3123
3150
  CLOSEST_LEVEL_LABEL = {
3124
3151
  key: "key",
3125
3152
  user: "personal",
3126
3153
  team: "team"
3127
3154
  };
3128
3155
  KEYS_PATH = "/litellm?tab=keys";
3129
- MoreKeysNote = ({ count }) => /* @__PURE__ */ import_react7.default.createElement(import_core_components.Link, { to: KEYS_PATH, variant: "caption" }, "+", count, " more budgeted key", count > 1 ? "s" : "", " further from the cap \u2192");
3130
- HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react7.default.createElement(
3131
- import_Box7.default,
3156
+ HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react8.default.createElement(
3157
+ import_Box8.default,
3132
3158
  {
3133
3159
  sx: (theme) => ({
3134
3160
  px: 1.5,
3135
3161
  py: 1.25,
3136
3162
  borderRadius: 1.5,
3137
3163
  border: "1px dashed",
3138
- borderColor: (0, import_styles4.alpha)(theme.palette.text.primary, 0.18)
3164
+ borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
3139
3165
  })
3140
3166
  },
3141
- /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react7.default.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
3167
+ /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react8.default.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
3142
3168
  );
3143
- TaggedLimit = ({ limit }) => {
3144
- const tone = budgetTone2(limit.pct);
3145
- return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
3146
- import_Typography7.default,
3147
- {
3148
- sx: (theme) => ({
3149
- fontSize: 11,
3150
- fontWeight: 700,
3151
- letterSpacing: "0.08em",
3152
- textTransform: "uppercase",
3153
- color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
3154
- mb: 0.5
3155
- })
3156
- },
3157
- limit.kind
3158
- ), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
3159
- };
3160
3169
  FullBody = ({ summary }) => {
3161
3170
  const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
3162
- return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(
3171
+ return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(
3163
3172
  LevelFrame,
3164
3173
  {
3165
3174
  rank: 1,
@@ -3167,10 +3176,10 @@ var init_LiteLLMBudgetWidget = __esm({
3167
3176
  tagline: "per-key cap \xB7 highest priority",
3168
3177
  note: hasBudgetedKeys ? void 0 : "no key budgets set"
3169
3178
  },
3170
- !hasBudgetedKeys && /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3171
- summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3172
- summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys })
3173
- ), /* @__PURE__ */ import_react7.default.createElement(
3179
+ !hasBudgetedKeys && /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3180
+ summary.keys.map((k) => /* @__PURE__ */ import_react8.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3181
+ summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
3182
+ ), /* @__PURE__ */ import_react8.default.createElement(
3174
3183
  LevelFrame,
3175
3184
  {
3176
3185
  rank: 2,
@@ -3178,15 +3187,15 @@ var init_LiteLLMBudgetWidget = __esm({
3178
3187
  tagline: "cap on everything you spend with your own keys",
3179
3188
  note: "skipped for team-bound keys"
3180
3189
  },
3181
- summary.user ? /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3182
- ), /* @__PURE__ */ import_react7.default.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ import_react7.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react7.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react7.default.createElement(HierarchyNote, { withReset: true })));
3190
+ 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.")
3191
+ ), /* @__PURE__ */ import_react8.default.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ import_react8.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react8.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react8.default.createElement(HierarchyNote, { withReset: true })));
3183
3192
  };
3184
3193
  CompactBody = ({ summary }) => {
3185
3194
  const { count } = budgetHeadline(summary);
3186
3195
  if (count === 0) {
3187
- return /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3196
+ return /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3188
3197
  }
3189
- return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react7.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys }), summary.user && /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3198
+ return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react8.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3190
3199
  };
3191
3200
  LiteLLMBudgetWidget = ({
3192
3201
  title = "Budget Policy",
@@ -3197,13 +3206,13 @@ var init_LiteLLMBudgetWidget = __esm({
3197
3206
  action
3198
3207
  }) => {
3199
3208
  const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
3200
- const [loading, setLoading] = (0, import_react7.useState)(true);
3201
- const [error, setError] = (0, import_react7.useState)(null);
3202
- const [user, setUser] = (0, import_react7.useState)(null);
3203
- const [teams, setTeams] = (0, import_react7.useState)([]);
3204
- const [keys, setKeys] = (0, import_react7.useState)([]);
3205
- const [expanded, setExpanded] = (0, import_react7.useState)(defaultExpanded);
3206
- (0, import_react7.useEffect)(() => {
3209
+ const [loading, setLoading] = (0, import_react8.useState)(true);
3210
+ const [error, setError] = (0, import_react8.useState)(null);
3211
+ const [user, setUser] = (0, import_react8.useState)(null);
3212
+ const [teams, setTeams] = (0, import_react8.useState)([]);
3213
+ const [keys, setKeys] = (0, import_react8.useState)([]);
3214
+ const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
3215
+ (0, import_react8.useEffect)(() => {
3207
3216
  let cancelled = false;
3208
3217
  setLoading(true);
3209
3218
  setError(null);
@@ -3227,19 +3236,19 @@ var init_LiteLLMBudgetWidget = __esm({
3227
3236
  cancelled = true;
3228
3237
  };
3229
3238
  }, [api]);
3230
- const summary = (0, import_react7.useMemo)(
3239
+ const summary = (0, import_react8.useMemo)(
3231
3240
  () => buildBudgetSummary(user, teams, keys, maxKeys),
3232
3241
  [user, teams, keys, maxKeys]
3233
3242
  );
3234
- const headline = (0, import_react7.useMemo)(() => budgetHeadline(summary), [summary]);
3243
+ const headline = (0, import_react8.useMemo)(() => budgetHeadline(summary), [summary]);
3235
3244
  const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
3236
3245
  const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
3237
3246
  headline.closest.pct
3238
3247
  )}%`;
3239
3248
  const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
3240
- const body = /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, loading && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ import_react7.default.createElement(import_CircularProgress4.default, { size: 32 })), !loading && error && /* @__PURE__ */ import_react7.default.createElement(import_Alert3.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ import_react7.default.createElement(CompactBody, { summary }) : /* @__PURE__ */ import_react7.default.createElement(FullBody, { summary })));
3241
- return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react7.default.createElement(
3242
- import_Box7.default,
3249
+ 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 })));
3250
+ return /* @__PURE__ */ import_react8.default.createElement(import_Paper6.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react8.default.createElement(
3251
+ import_Box8.default,
3243
3252
  {
3244
3253
  onClick: () => setExpanded((e) => !e),
3245
3254
  sx: {
@@ -3251,14 +3260,14 @@ var init_LiteLLMBudgetWidget = __esm({
3251
3260
  mb: expanded ? 1.5 : 0
3252
3261
  }
3253
3262
  },
3254
- /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 0.5 } }, /* @__PURE__ */ import_react7.default.createElement(
3263
+ /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { mt: 0.5 } }, /* @__PURE__ */ import_react8.default.createElement(
3255
3264
  StatusPill,
3256
3265
  {
3257
3266
  label: summaryText,
3258
3267
  tone: budgetTone2(headline.closest?.pct ?? 0)
3259
3268
  }
3260
3269
  ))),
3261
- /* @__PURE__ */ import_react7.default.createElement(
3270
+ /* @__PURE__ */ import_react8.default.createElement(
3262
3271
  import_IconButton4.default,
3263
3272
  {
3264
3273
  size: "small",
@@ -3274,10 +3283,10 @@ var init_LiteLLMBudgetWidget = __esm({
3274
3283
  transition: theme.transitions.create("transform")
3275
3284
  })
3276
3285
  },
3277
- /* @__PURE__ */ import_react7.default.createElement(import_icons_material5.ExpandMore, null)
3286
+ /* @__PURE__ */ import_react8.default.createElement(import_icons_material5.ExpandMore, null)
3278
3287
  )
3279
- ) : /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ import_react7.default.createElement(import_Collapse.default, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ import_react7.default.createElement(
3280
- import_Box7.default,
3288
+ ) : /* @__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(
3289
+ import_Box8.default,
3281
3290
  {
3282
3291
  sx: (theme) => ({
3283
3292
  mt: 2,
@@ -3306,14 +3315,14 @@ function budgetStatValue(hidden, budget) {
3306
3315
  if (budget > 0) return fmtUsd2(budget);
3307
3316
  return "Unlimited";
3308
3317
  }
3309
- var import_react8, import_Paper6, import_Box8, import_Typography8, import_Skeleton4, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton5, import_Button5, import_CircularProgress5, import_styles5, import_icons_material6, import_recharts2, fmtUsd2, TeamCard, TeamUsage;
3318
+ var import_react9, import_Paper7, import_Box9, import_Typography9, import_Skeleton4, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton5, import_Button5, import_CircularProgress5, import_styles6, import_icons_material6, import_recharts2, fmtUsd2, TeamCard, TeamUsage;
3310
3319
  var init_TeamUsage = __esm({
3311
3320
  "src/components/TeamUsage.tsx"() {
3312
3321
  "use strict";
3313
- import_react8 = __toESM(require("react"));
3314
- import_Paper6 = __toESM(require("@mui/material/Paper"));
3315
- import_Box8 = __toESM(require("@mui/material/Box"));
3316
- import_Typography8 = __toESM(require("@mui/material/Typography"));
3322
+ import_react9 = __toESM(require("react"));
3323
+ import_Paper7 = __toESM(require("@mui/material/Paper"));
3324
+ import_Box9 = __toESM(require("@mui/material/Box"));
3325
+ import_Typography9 = __toESM(require("@mui/material/Typography"));
3317
3326
  import_Skeleton4 = __toESM(require("@mui/material/Skeleton"));
3318
3327
  import_Divider3 = __toESM(require("@mui/material/Divider"));
3319
3328
  import_Table4 = __toESM(require("@mui/material/Table"));
@@ -3326,13 +3335,13 @@ var init_TeamUsage = __esm({
3326
3335
  import_IconButton5 = __toESM(require("@mui/material/IconButton"));
3327
3336
  import_Button5 = __toESM(require("@mui/material/Button"));
3328
3337
  import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
3329
- import_styles5 = require("@mui/material/styles");
3338
+ import_styles6 = require("@mui/material/styles");
3330
3339
  import_icons_material6 = require("@mui/icons-material");
3331
3340
  import_recharts2 = require("recharts");
3332
3341
  init_ui();
3333
3342
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
3334
3343
  TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
3335
- const [expanded, setExpanded] = (0, import_react8.useState)(false);
3344
+ const [expanded, setExpanded] = (0, import_react9.useState)(false);
3336
3345
  const chart = useChartTheme();
3337
3346
  const hidden = team.budget_hidden === true;
3338
3347
  const budget = hidden ? 0 : team.max_budget ?? 0;
@@ -3344,24 +3353,24 @@ var init_TeamUsage = __esm({
3344
3353
  const renderDailySpendSection = () => {
3345
3354
  if (hidden) return null;
3346
3355
  if (usageLoading) {
3347
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react8.default.createElement(import_CircularProgress5.default, { size: 22 }));
3356
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react9.default.createElement(import_CircularProgress5.default, { size: 22 }));
3348
3357
  }
3349
3358
  if (dailyData.length === 0) return null;
3350
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mb: 2.5 }, /* @__PURE__ */ import_react8.default.createElement(
3351
- import_Typography8.default,
3359
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react9.default.createElement(
3360
+ import_Typography9.default,
3352
3361
  {
3353
3362
  variant: "caption",
3354
3363
  color: "text.secondary",
3355
3364
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
3356
3365
  },
3357
3366
  "Daily spend"
3358
- ), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { height: 150 }, /* @__PURE__ */ import_react8.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react8.default.createElement(import_recharts2.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react8.default.createElement(import_recharts2.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react8.default.createElement(import_recharts2.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react8.default.createElement(import_recharts2.YAxis, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ import_react8.default.createElement(
3367
+ ), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { height: 150 }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react9.default.createElement(import_recharts2.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react9.default.createElement(import_recharts2.YAxis, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ import_react9.default.createElement(
3359
3368
  import_recharts2.Tooltip,
3360
3369
  {
3361
3370
  cursor: chart.cursor,
3362
- content: /* @__PURE__ */ import_react8.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3371
+ content: /* @__PURE__ */ import_react9.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
3363
3372
  }
3364
- ), /* @__PURE__ */ import_react8.default.createElement(
3373
+ ), /* @__PURE__ */ import_react9.default.createElement(
3365
3374
  import_recharts2.Area,
3366
3375
  {
3367
3376
  type: "monotone",
@@ -3374,8 +3383,8 @@ var init_TeamUsage = __esm({
3374
3383
  }
3375
3384
  )))));
3376
3385
  };
3377
- const sectionLabel = (label) => /* @__PURE__ */ import_react8.default.createElement(
3378
- import_Typography8.default,
3386
+ const sectionLabel = (label) => /* @__PURE__ */ import_react9.default.createElement(
3387
+ import_Typography9.default,
3379
3388
  {
3380
3389
  variant: "caption",
3381
3390
  color: "text.secondary",
@@ -3383,8 +3392,8 @@ var init_TeamUsage = __esm({
3383
3392
  },
3384
3393
  label
3385
3394
  );
3386
- return /* @__PURE__ */ import_react8.default.createElement(import_Paper6.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ import_react8.default.createElement(
3387
- import_Box8.default,
3395
+ 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(
3396
+ import_Box9.default,
3388
3397
  {
3389
3398
  sx: (theme) => ({
3390
3399
  width: 36,
@@ -3394,20 +3403,20 @@ var init_TeamUsage = __esm({
3394
3403
  display: "flex",
3395
3404
  alignItems: "center",
3396
3405
  justifyContent: "center",
3397
- bgcolor: (0, import_styles5.alpha)(theme.palette.primary.main, 0.1),
3406
+ bgcolor: (0, import_styles6.alpha)(theme.palette.primary.main, 0.1),
3398
3407
  color: theme.palette.primary.main
3399
3408
  })
3400
3409
  },
3401
- /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.Group, { fontSize: "small" })
3402
- ), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react8.default.createElement(
3403
- import_Typography8.default,
3410
+ /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Group, { fontSize: "small" })
3411
+ ), /* @__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(
3412
+ import_Typography9.default,
3404
3413
  {
3405
3414
  variant: "caption",
3406
3415
  color: "text.secondary",
3407
3416
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3408
3417
  },
3409
3418
  team.team_id
3410
- )), isOver && /* @__PURE__ */ import_react8.default.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ import_react8.default.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ import_react8.default.createElement(
3419
+ )), isOver && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ import_react9.default.createElement(
3411
3420
  import_Button5.default,
3412
3421
  {
3413
3422
  size: "small",
@@ -3416,7 +3425,7 @@ var init_TeamUsage = __esm({
3416
3425
  sx: { textTransform: "none" }
3417
3426
  },
3418
3427
  "Edit"
3419
- ), /* @__PURE__ */ import_react8.default.createElement(
3428
+ ), /* @__PURE__ */ import_react9.default.createElement(
3420
3429
  import_IconButton5.default,
3421
3430
  {
3422
3431
  size: "small",
@@ -3429,9 +3438,9 @@ var init_TeamUsage = __esm({
3429
3438
  transition: theme.transitions.create("transform")
3430
3439
  })
3431
3440
  },
3432
- /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.ExpandMore, null)
3433
- )), /* @__PURE__ */ import_react8.default.createElement(
3434
- import_Box8.default,
3441
+ /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.ExpandMore, null)
3442
+ )), /* @__PURE__ */ import_react9.default.createElement(
3443
+ import_Box9.default,
3435
3444
  {
3436
3445
  sx: {
3437
3446
  display: "grid",
@@ -3441,35 +3450,35 @@ var init_TeamUsage = __esm({
3441
3450
  maxWidth: 640
3442
3451
  }
3443
3452
  },
3444
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3445
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3446
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3447
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3448
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3449
- /* @__PURE__ */ import_react8.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3450
- ), hidden && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ import_react8.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.75 } }, "Team budget is hidden \u2014 showing usage level only", team.budget_duration ? ` \xB7 resets every ${team.budget_duration}` : "", ".")), !hidden && budget > 0 && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react8.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react8.default.createElement(import_Collapse2.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react8.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react8.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react8.default.createElement(
3453
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
3454
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
3455
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
3456
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
3457
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
3458
+ /* @__PURE__ */ import_react9.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
3459
+ ), hidden && /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ import_react9.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.75 } }, "Team budget is hidden \u2014 showing usage level only", team.budget_duration ? ` \xB7 resets every ${team.budget_duration}` : "", ".")), !hidden && budget > 0 && /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react9.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react9.default.createElement(import_Collapse2.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react9.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react9.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react9.default.createElement(
3451
3460
  import_TableContainer4.default,
3452
3461
  {
3453
- component: import_Paper6.default,
3462
+ component: import_Paper7.default,
3454
3463
  variant: "outlined",
3455
3464
  sx: { borderRadius: 1.5 }
3456
3465
  },
3457
- /* @__PURE__ */ import_react8.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react8.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, null, "User"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, { align: "right" }, "Role"))), /* @__PURE__ */ import_react8.default.createElement(import_TableBody4.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react8.default.createElement(import_TableRow4.default, { key: m.user_id }, /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, null, m.user_email ? /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react8.default.createElement(
3458
- import_Typography8.default,
3466
+ /* @__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(
3467
+ import_Typography9.default,
3459
3468
  {
3460
3469
  variant: "caption",
3461
3470
  color: "text.secondary",
3462
3471
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
3463
3472
  },
3464
3473
  m.user_id
3465
- )) : /* @__PURE__ */ import_react8.default.createElement(
3466
- import_Typography8.default,
3474
+ )) : /* @__PURE__ */ import_react9.default.createElement(
3475
+ import_Typography9.default,
3467
3476
  {
3468
3477
  variant: "body2",
3469
3478
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
3470
3479
  },
3471
3480
  m.user_id
3472
- )), /* @__PURE__ */ import_react8.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react8.default.createElement(
3481
+ )), /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(
3473
3482
  StatusPill,
3474
3483
  {
3475
3484
  label: m.role,
@@ -3477,7 +3486,7 @@ var init_TeamUsage = __esm({
3477
3486
  dot: false
3478
3487
  }
3479
3488
  ))))))
3480
- ) : /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3489
+ ) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
3481
3490
  };
3482
3491
  TeamUsage = ({
3483
3492
  teams,
@@ -3489,22 +3498,22 @@ var init_TeamUsage = __esm({
3489
3498
  canCreate,
3490
3499
  onCreateTeam
3491
3500
  }) => {
3492
- const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react8.default.createElement(
3501
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react9.default.createElement(
3493
3502
  import_Button5.default,
3494
3503
  {
3495
3504
  variant: "contained",
3496
3505
  color: "primary",
3497
3506
  disableElevation: true,
3498
- startIcon: /* @__PURE__ */ import_react8.default.createElement(import_icons_material6.Add, null),
3507
+ startIcon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Add, null),
3499
3508
  onClick: onCreateTeam
3500
3509
  },
3501
3510
  "Create Team"
3502
3511
  ) : void 0;
3503
3512
  if (loading) {
3504
- return /* @__PURE__ */ import_react8.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react8.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react8.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
3513
+ return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react9.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
3505
3514
  }
3506
3515
  if (!teams.length) {
3507
- return /* @__PURE__ */ import_react8.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react8.default.createElement(
3516
+ return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(
3508
3517
  EmptyState,
3509
3518
  {
3510
3519
  message: "You're not a member of any LiteLLM team yet.",
@@ -3512,14 +3521,14 @@ var init_TeamUsage = __esm({
3512
3521
  }
3513
3522
  ));
3514
3523
  }
3515
- return /* @__PURE__ */ import_react8.default.createElement(
3524
+ return /* @__PURE__ */ import_react9.default.createElement(
3516
3525
  SectionCard,
3517
3526
  {
3518
3527
  title: "Teams",
3519
3528
  subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
3520
3529
  actions: createAction
3521
3530
  },
3522
- /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react8.default.createElement(
3531
+ /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react9.default.createElement(
3523
3532
  TeamCard,
3524
3533
  {
3525
3534
  key: team.team_id,
@@ -3553,12 +3562,12 @@ function fmtTokens(n) {
3553
3562
  if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
3554
3563
  return String(n);
3555
3564
  }
3556
- var import_react9, import_Box9, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography9, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles6, ALL_PROXY_MODELS2, ModelsTable;
3565
+ var import_react10, import_Box10, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography10, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles7, ALL_PROXY_MODELS2, ModelsTable;
3557
3566
  var init_ModelsTable = __esm({
3558
3567
  "src/components/ModelsTable.tsx"() {
3559
3568
  "use strict";
3560
- import_react9 = __toESM(require("react"));
3561
- import_Box9 = __toESM(require("@mui/material/Box"));
3569
+ import_react10 = __toESM(require("react"));
3570
+ import_Box10 = __toESM(require("@mui/material/Box"));
3562
3571
  import_Table5 = __toESM(require("@mui/material/Table"));
3563
3572
  import_TableBody5 = __toESM(require("@mui/material/TableBody"));
3564
3573
  import_TableCell5 = __toESM(require("@mui/material/TableCell"));
@@ -3566,7 +3575,7 @@ var init_ModelsTable = __esm({
3566
3575
  import_TableHead5 = __toESM(require("@mui/material/TableHead"));
3567
3576
  import_TableRow5 = __toESM(require("@mui/material/TableRow"));
3568
3577
  import_TableSortLabel2 = __toESM(require("@mui/material/TableSortLabel"));
3569
- import_Typography9 = __toESM(require("@mui/material/Typography"));
3578
+ import_Typography10 = __toESM(require("@mui/material/Typography"));
3570
3579
  import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
3571
3580
  import_Select = __toESM(require("@mui/material/Select"));
3572
3581
  import_FormControl = __toESM(require("@mui/material/FormControl"));
@@ -3579,24 +3588,24 @@ var init_ModelsTable = __esm({
3579
3588
  import_Snackbar = __toESM(require("@mui/material/Snackbar"));
3580
3589
  import_Alert4 = __toESM(require("@mui/material/Alert"));
3581
3590
  init_ui();
3582
- import_styles6 = require("@mui/material/styles");
3591
+ import_styles7 = require("@mui/material/styles");
3583
3592
  ALL_PROXY_MODELS2 = "all-proxy-models";
3584
3593
  ModelsTable = ({ allModels, teams, loading }) => {
3585
- const theme = (0, import_styles6.useTheme)();
3586
- const [selectedTeamId, setSelectedTeamId] = (0, import_react9.useState)("");
3587
- const [filterText, setFilterText] = (0, import_react9.useState)("");
3588
- const [copiedSnackbar, setCopiedSnackbar] = (0, import_react9.useState)(false);
3589
- const [sortKey, setSortKey] = (0, import_react9.useState)("model_name");
3590
- const [sortDirection, setSortDirection] = (0, import_react9.useState)("asc");
3591
- const selectedTeam = (0, import_react9.useMemo)(
3594
+ const theme = (0, import_styles7.useTheme)();
3595
+ const [selectedTeamId, setSelectedTeamId] = (0, import_react10.useState)("");
3596
+ const [filterText, setFilterText] = (0, import_react10.useState)("");
3597
+ const [copiedSnackbar, setCopiedSnackbar] = (0, import_react10.useState)(false);
3598
+ const [sortKey, setSortKey] = (0, import_react10.useState)("model_name");
3599
+ const [sortDirection, setSortDirection] = (0, import_react10.useState)("asc");
3600
+ const selectedTeam = (0, import_react10.useMemo)(
3592
3601
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
3593
3602
  [teams, selectedTeamId]
3594
3603
  );
3595
- const teamModels = (0, import_react9.useMemo)(() => {
3604
+ const teamModels = (0, import_react10.useMemo)(() => {
3596
3605
  if (!selectedTeam) return [];
3597
3606
  return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
3598
3607
  }, [allModels, selectedTeam]);
3599
- const filteredModels = (0, import_react9.useMemo)(() => {
3608
+ const filteredModels = (0, import_react10.useMemo)(() => {
3600
3609
  const q = filterText.trim().toLowerCase();
3601
3610
  let models = teamModels;
3602
3611
  if (q) {
@@ -3643,10 +3652,10 @@ var init_ModelsTable = __esm({
3643
3652
  return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
3644
3653
  });
3645
3654
  }, [teamModels, filterText, sortKey, sortDirection]);
3646
- const handleCopyModelId = (0, import_react9.useCallback)((modelId) => {
3655
+ const handleCopyModelId = (0, import_react10.useCallback)((modelId) => {
3647
3656
  navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
3648
3657
  }, []);
3649
- const handleSort = (0, import_react9.useCallback)((key) => {
3658
+ const handleSort = (0, import_react10.useCallback)((key) => {
3650
3659
  if (sortKey === key) {
3651
3660
  setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
3652
3661
  } else {
@@ -3668,13 +3677,13 @@ var init_ModelsTable = __esm({
3668
3677
  return theme.palette.info.main;
3669
3678
  }
3670
3679
  };
3671
- return /* @__PURE__ */ import_react9.default.createElement(
3680
+ return /* @__PURE__ */ import_react10.default.createElement(
3672
3681
  SectionCard,
3673
3682
  {
3674
3683
  title: "Available Models",
3675
3684
  subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
3676
3685
  },
3677
- /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ import_react9.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react9.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react9.default.createElement(
3686
+ /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ import_react10.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react10.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react10.default.createElement(
3678
3687
  import_Select.default,
3679
3688
  {
3680
3689
  labelId: "team-select-label",
@@ -3682,8 +3691,8 @@ var init_ModelsTable = __esm({
3682
3691
  label: "Team",
3683
3692
  onChange: (e) => setSelectedTeamId(e.target.value)
3684
3693
  },
3685
- teams.map((t) => /* @__PURE__ */ import_react9.default.createElement(import_MenuItem4.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3686
- )), /* @__PURE__ */ import_react9.default.createElement(
3694
+ 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))
3695
+ )), /* @__PURE__ */ import_react10.default.createElement(
3687
3696
  import_TextField5.default,
3688
3697
  {
3689
3698
  label: "Filter Models",
@@ -3693,33 +3702,33 @@ var init_ModelsTable = __esm({
3693
3702
  disabled: !selectedTeamId,
3694
3703
  sx: { minWidth: 240, mb: 2 },
3695
3704
  InputProps: {
3696
- startAdornment: /* @__PURE__ */ import_react9.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react9.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
3705
+ startAdornment: /* @__PURE__ */ import_react10.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react10.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
3697
3706
  }
3698
3707
  }
3699
3708
  )),
3700
- loading && /* @__PURE__ */ import_react9.default.createElement(EmptyState, { message: "Loading models\u2026" }),
3701
- !loading && !selectedTeamId && /* @__PURE__ */ import_react9.default.createElement(
3709
+ loading && /* @__PURE__ */ import_react10.default.createElement(EmptyState, { message: "Loading models\u2026" }),
3710
+ !loading && !selectedTeamId && /* @__PURE__ */ import_react10.default.createElement(
3702
3711
  EmptyState,
3703
3712
  {
3704
3713
  message: "Select a team to see available models",
3705
3714
  hint: "Each team may have a different set of models assigned"
3706
3715
  }
3707
3716
  ),
3708
- !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react9.default.createElement(
3717
+ !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
3709
3718
  EmptyState,
3710
3719
  {
3711
3720
  message: "No models available for this team",
3712
3721
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
3713
3722
  }
3714
3723
  ),
3715
- !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react9.default.createElement(
3724
+ !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
3716
3725
  EmptyState,
3717
3726
  {
3718
3727
  message: "No models match your search",
3719
3728
  hint: "Try a different search term"
3720
3729
  }
3721
3730
  ),
3722
- !loading && filteredModels.length > 0 && /* @__PURE__ */ import_react9.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react9.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react9.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react9.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react9.default.createElement(
3731
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ import_react10.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react10.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react10.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react10.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react10.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react10.default.createElement(
3723
3732
  "span",
3724
3733
  {
3725
3734
  role: "button",
@@ -3729,9 +3738,9 @@ var init_ModelsTable = __esm({
3729
3738
  onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
3730
3739
  style: { cursor: "pointer", display: "inline-flex" }
3731
3740
  },
3732
- /* @__PURE__ */ import_react9.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3733
- )), /* @__PURE__ */ import_react9.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react9.default.createElement(
3734
- import_Box9.default,
3741
+ /* @__PURE__ */ import_react10.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3742
+ )), /* @__PURE__ */ import_react10.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react10.default.createElement(
3743
+ import_Box10.default,
3735
3744
  {
3736
3745
  component: "span",
3737
3746
  role: "button",
@@ -3741,9 +3750,9 @@ var init_ModelsTable = __esm({
3741
3750
  sx: quietIconButtonSx("accent"),
3742
3751
  style: { cursor: "pointer", display: "inline-flex" }
3743
3752
  },
3744
- /* @__PURE__ */ import_react9.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
3745
- )))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(
3746
- import_Box9.default,
3753
+ /* @__PURE__ */ import_react10.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
3754
+ )))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(
3755
+ import_Box10.default,
3747
3756
  {
3748
3757
  component: "span",
3749
3758
  sx: {
@@ -3754,13 +3763,13 @@ var init_ModelsTable = __esm({
3754
3763
  fontSize: 11,
3755
3764
  fontWeight: 600,
3756
3765
  textTransform: "capitalize",
3757
- bgcolor: (0, import_styles6.alpha)(modeColor(m.mode), 0.1),
3766
+ bgcolor: (0, import_styles7.alpha)(modeColor(m.mode), 0.1),
3758
3767
  color: modeColor(m.mode)
3759
3768
  }
3760
3769
  },
3761
3770
  m.mode
3762
- )), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react9.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react9.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3763
- /* @__PURE__ */ import_react9.default.createElement(
3771
+ )), /* @__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" })))))))),
3772
+ /* @__PURE__ */ import_react10.default.createElement(
3764
3773
  import_Snackbar.default,
3765
3774
  {
3766
3775
  open: copiedSnackbar,
@@ -3768,7 +3777,7 @@ var init_ModelsTable = __esm({
3768
3777
  onClose: () => setCopiedSnackbar(false),
3769
3778
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
3770
3779
  },
3771
- /* @__PURE__ */ import_react9.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3780
+ /* @__PURE__ */ import_react10.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3772
3781
  )
3773
3782
  );
3774
3783
  };
@@ -3796,10 +3805,10 @@ function formatDateTime(iso) {
3796
3805
  }
3797
3806
  function renderAuditLogBody(loading, entries) {
3798
3807
  if (loading) {
3799
- return /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react10.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
3808
+ return /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react11.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
3800
3809
  }
3801
3810
  if (entries.length === 0) {
3802
- return /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react10.default.createElement(
3811
+ return /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react11.default.createElement(
3803
3812
  EmptyState,
3804
3813
  {
3805
3814
  message: "No audit events found",
@@ -3807,15 +3816,15 @@ function renderAuditLogBody(loading, entries) {
3807
3816
  }
3808
3817
  )));
3809
3818
  }
3810
- return entries.map((entry) => /* @__PURE__ */ import_react10.default.createElement(DetailRow, { key: entry.id, entry }));
3819
+ return entries.map((entry) => /* @__PURE__ */ import_react11.default.createElement(DetailRow, { key: entry.id, entry }));
3811
3820
  }
3812
- var import_react10, import_Box10, import_Typography10, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_TablePagination, import_TextField6, import_MenuItem5, import_Skeleton5, import_Collapse3, import_IconButton6, import_Alert5, import_styles7, import_icons_material8, import_react_use2, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
3821
+ var import_react11, import_Box11, import_Typography11, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_TablePagination, import_TextField6, import_MenuItem5, import_Skeleton5, import_Collapse3, import_IconButton6, import_Alert5, import_styles8, import_icons_material8, import_react_use2, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
3813
3822
  var init_AuditLog = __esm({
3814
3823
  "src/components/AuditLog.tsx"() {
3815
3824
  "use strict";
3816
- import_react10 = __toESM(require("react"));
3817
- import_Box10 = __toESM(require("@mui/material/Box"));
3818
- import_Typography10 = __toESM(require("@mui/material/Typography"));
3825
+ import_react11 = __toESM(require("react"));
3826
+ import_Box11 = __toESM(require("@mui/material/Box"));
3827
+ import_Typography11 = __toESM(require("@mui/material/Typography"));
3819
3828
  import_Table6 = __toESM(require("@mui/material/Table"));
3820
3829
  import_TableBody6 = __toESM(require("@mui/material/TableBody"));
3821
3830
  import_TableCell6 = __toESM(require("@mui/material/TableCell"));
@@ -3829,7 +3838,7 @@ var init_AuditLog = __esm({
3829
3838
  import_Collapse3 = __toESM(require("@mui/material/Collapse"));
3830
3839
  import_IconButton6 = __toESM(require("@mui/material/IconButton"));
3831
3840
  import_Alert5 = __toESM(require("@mui/material/Alert"));
3832
- import_styles7 = require("@mui/material/styles");
3841
+ import_styles8 = require("@mui/material/styles");
3833
3842
  import_icons_material8 = require("@mui/icons-material");
3834
3843
  import_react_use2 = require("react-use");
3835
3844
  init_ui();
@@ -3848,9 +3857,9 @@ var init_AuditLog = __esm({
3848
3857
  LiteLLM_UserTable: "User"
3849
3858
  };
3850
3859
  DetailRow = ({ entry }) => {
3851
- const [open, setOpen] = (0, import_react10.useState)(false);
3860
+ const [open, setOpen] = (0, import_react11.useState)(false);
3852
3861
  const hasDetail = entry.before_value || entry.updated_values;
3853
- return /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react10.default.createElement(
3862
+ return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react11.default.createElement(
3854
3863
  import_IconButton6.default,
3855
3864
  {
3856
3865
  size: "small",
@@ -3863,9 +3872,9 @@ var init_AuditLog = __esm({
3863
3872
  transition: theme.transitions.create("transform")
3864
3873
  })
3865
3874
  },
3866
- /* @__PURE__ */ import_react10.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
3867
- )), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, entry.action && /* @__PURE__ */ import_react10.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react10.default.createElement(
3868
- import_Typography10.default,
3875
+ /* @__PURE__ */ import_react11.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
3876
+ )), /* @__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(
3877
+ import_Typography11.default,
3869
3878
  {
3870
3879
  variant: "body2",
3871
3880
  component: "code",
@@ -3874,8 +3883,8 @@ var init_AuditLog = __esm({
3874
3883
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3875
3884
  },
3876
3885
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
3877
- )), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react10.default.createElement(import_Collapse3.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react10.default.createElement(
3878
- import_Box10.default,
3886
+ )), /* @__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(
3887
+ import_Box11.default,
3879
3888
  {
3880
3889
  display: "flex",
3881
3890
  gap: 2,
@@ -3884,11 +3893,11 @@ var init_AuditLog = __esm({
3884
3893
  p: 2,
3885
3894
  mb: 1.5,
3886
3895
  borderRadius: 1.5,
3887
- bgcolor: (0, import_styles7.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3896
+ bgcolor: (0, import_styles8.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
3888
3897
  })
3889
3898
  },
3890
- entry.before_value && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react10.default.createElement(
3891
- import_Typography10.default,
3899
+ entry.before_value && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react11.default.createElement(
3900
+ import_Typography11.default,
3892
3901
  {
3893
3902
  variant: "caption",
3894
3903
  color: "text.secondary",
@@ -3897,9 +3906,9 @@ var init_AuditLog = __esm({
3897
3906
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3898
3907
  },
3899
3908
  "Before"
3900
- ), /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
3901
- entry.updated_values && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react10.default.createElement(
3902
- import_Typography10.default,
3909
+ ), /* @__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))),
3910
+ entry.updated_values && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react11.default.createElement(
3911
+ import_Typography11.default,
3903
3912
  {
3904
3913
  variant: "caption",
3905
3914
  color: "text.secondary",
@@ -3908,14 +3917,14 @@ var init_AuditLog = __esm({
3908
3917
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
3909
3918
  },
3910
3919
  "After"
3911
- ), /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
3920
+ ), /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
3912
3921
  )))));
3913
3922
  };
3914
3923
  AuditLog = ({ api }) => {
3915
- const [page, setPage] = (0, import_react10.useState)(0);
3916
- const [pageSize, setPageSize] = (0, import_react10.useState)(25);
3917
- const [filters, setFilters] = (0, import_react10.useState)({});
3918
- const fetchParams = (0, import_react10.useMemo)(
3924
+ const [page, setPage] = (0, import_react11.useState)(0);
3925
+ const [pageSize, setPageSize] = (0, import_react11.useState)(25);
3926
+ const [filters, setFilters] = (0, import_react11.useState)({});
3927
+ const fetchParams = (0, import_react11.useMemo)(
3919
3928
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3920
3929
  [page, pageSize, filters]
3921
3930
  );
@@ -3925,13 +3934,13 @@ var init_AuditLog = __esm({
3925
3934
  );
3926
3935
  const entries = value?.audit_logs ?? [];
3927
3936
  const total = value?.total ?? 0;
3928
- return /* @__PURE__ */ import_react10.default.createElement(
3937
+ return /* @__PURE__ */ import_react11.default.createElement(
3929
3938
  SectionCard,
3930
3939
  {
3931
3940
  title: "Audit Log",
3932
3941
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3933
3942
  flush: true,
3934
- actions: /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(
3943
+ actions: /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(
3935
3944
  import_TextField6.default,
3936
3945
  {
3937
3946
  size: "small",
@@ -3944,12 +3953,12 @@ var init_AuditLog = __esm({
3944
3953
  },
3945
3954
  sx: { minWidth: 150 }
3946
3955
  },
3947
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
3948
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
3949
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
3950
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
3951
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
3952
- ), /* @__PURE__ */ import_react10.default.createElement(
3956
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
3957
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
3958
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
3959
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
3960
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
3961
+ ), /* @__PURE__ */ import_react11.default.createElement(
3953
3962
  import_TextField6.default,
3954
3963
  {
3955
3964
  size: "small",
@@ -3962,13 +3971,13 @@ var init_AuditLog = __esm({
3962
3971
  },
3963
3972
  sx: { minWidth: 150 }
3964
3973
  },
3965
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
3966
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
3967
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
3968
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
3969
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3970
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
3971
- ), /* @__PURE__ */ import_react10.default.createElement(
3974
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
3975
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
3976
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
3977
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
3978
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3979
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
3980
+ ), /* @__PURE__ */ import_react11.default.createElement(
3972
3981
  import_TextField6.default,
3973
3982
  {
3974
3983
  size: "small",
@@ -3982,9 +3991,9 @@ var init_AuditLog = __esm({
3982
3991
  }
3983
3992
  ))
3984
3993
  },
3985
- error && /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react10.default.createElement(import_Alert5.default, { severity: "error" }, error.message)),
3986
- /* @__PURE__ */ import_react10.default.createElement(import_TableContainer6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Table6.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react10.default.createElement(import_TableHead6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Time"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Action"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Table"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Object ID"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell6.default, null, "Changed By"))), /* @__PURE__ */ import_react10.default.createElement(import_TableBody6.default, null, renderAuditLogBody(loading, entries)))),
3987
- /* @__PURE__ */ import_react10.default.createElement(
3994
+ 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)),
3995
+ /* @__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)))),
3996
+ /* @__PURE__ */ import_react11.default.createElement(
3988
3997
  import_TablePagination.default,
3989
3998
  {
3990
3999
  component: "div",
@@ -4053,17 +4062,17 @@ function initDateRange() {
4053
4062
  else start.setDate(start.getDate() - 7);
4054
4063
  return { start, end };
4055
4064
  }
4056
- var import_react11, import_Box11, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography11, import_Paper7, import_Tabs2, import_Tab2, import_Grid2, import_react_router_dom, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
4065
+ var import_react12, import_Box12, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography12, import_Paper8, import_Tabs2, import_Tab2, import_Grid2, import_react_router_dom, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
4057
4066
  var init_LiteLLMPage = __esm({
4058
4067
  "src/components/LiteLLMPage.tsx"() {
4059
4068
  "use strict";
4060
- import_react11 = __toESM(require("react"));
4061
- import_Box11 = __toESM(require("@mui/material/Box"));
4069
+ import_react12 = __toESM(require("react"));
4070
+ import_Box12 = __toESM(require("@mui/material/Box"));
4062
4071
  import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
4063
4072
  import_Alert6 = __toESM(require("@mui/material/Alert"));
4064
4073
  import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
4065
- import_Typography11 = __toESM(require("@mui/material/Typography"));
4066
- import_Paper7 = __toESM(require("@mui/material/Paper"));
4074
+ import_Typography12 = __toESM(require("@mui/material/Typography"));
4075
+ import_Paper8 = __toESM(require("@mui/material/Paper"));
4067
4076
  import_Tabs2 = __toESM(require("@mui/material/Tabs"));
4068
4077
  import_Tab2 = __toESM(require("@mui/material/Tab"));
4069
4078
  import_Grid2 = __toESM(require("@mui/material/Grid"));
@@ -4073,7 +4082,7 @@ var init_LiteLLMPage = __esm({
4073
4082
  import_plugin_permission_react = require("@backstage/plugin-permission-react");
4074
4083
  init_DashboardHeader();
4075
4084
  init_KeysTable();
4076
- init_GenerateKeyDialog();
4085
+ init_KeyFormDialog();
4077
4086
  init_ManageTeamDialog();
4078
4087
  init_UsageStats();
4079
4088
  init_LiteLLMBudgetWidget();
@@ -4087,8 +4096,8 @@ var init_LiteLLMPage = __esm({
4087
4096
  isPageTab = (v) => !!v && PAGE_TABS.includes(v);
4088
4097
  LiteLLMPage = () => {
4089
4098
  const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
4090
- const [dateRange, setDateRange] = (0, import_react11.useState)(initDateRange);
4091
- const [currentPreset, setCurrentPreset] = (0, import_react11.useState)(() => {
4099
+ const [dateRange, setDateRange] = (0, import_react12.useState)(initDateRange);
4100
+ const [currentPreset, setCurrentPreset] = (0, import_react12.useState)(() => {
4092
4101
  try {
4093
4102
  return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
4094
4103
  } catch {
@@ -4096,11 +4105,26 @@ var init_LiteLLMPage = __esm({
4096
4105
  }
4097
4106
  });
4098
4107
  const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
4099
- const [activeTab, setActiveTab] = (0, import_react11.useState)(() => {
4108
+ const [activeTab, setActiveTab] = (0, import_react12.useState)(() => {
4100
4109
  const t = searchParams.get("tab");
4101
4110
  return isPageTab(t) ? t : "overview";
4102
4111
  });
4103
- const selectTab = (0, import_react11.useCallback)(
4112
+ const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react12.useState)(
4113
+ () => searchParams.get("generate") === "1"
4114
+ );
4115
+ (0, import_react12.useEffect)(() => {
4116
+ if (searchParams.get("generate") !== "1") return;
4117
+ setGenerateDialogOpen(true);
4118
+ setSearchParams(
4119
+ (prev) => {
4120
+ const next = new URLSearchParams(prev);
4121
+ next.delete("generate");
4122
+ return next;
4123
+ },
4124
+ { replace: true }
4125
+ );
4126
+ }, [searchParams, setSearchParams]);
4127
+ const selectTab = (0, import_react12.useCallback)(
4104
4128
  (tab) => {
4105
4129
  setActiveTab(tab);
4106
4130
  setSearchParams(
@@ -4114,11 +4138,12 @@ var init_LiteLLMPage = __esm({
4114
4138
  },
4115
4139
  [setSearchParams]
4116
4140
  );
4117
- const [snackbar, setSnackbar] = (0, import_react11.useState)(null);
4118
- const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react11.useState)(false);
4119
- const [manageTeam, setManageTeam] = (0, import_react11.useState)(null);
4120
- const [teamUsageCache, setTeamUsageCache] = (0, import_react11.useState)({});
4121
- const [teamUsageLoading, setTeamUsageLoading] = (0, import_react11.useState)({});
4141
+ const [snackbar, setSnackbar] = (0, import_react12.useState)(null);
4142
+ const [manageTeam, setManageTeam] = (0, import_react12.useState)(null);
4143
+ const [keyToEdit, setKeyToEdit] = (0, import_react12.useState)(null);
4144
+ const keyFormOpen = generateDialogOpen || !!keyToEdit;
4145
+ const [teamUsageCache, setTeamUsageCache] = (0, import_react12.useState)({});
4146
+ const [teamUsageLoading, setTeamUsageLoading] = (0, import_react12.useState)({});
4122
4147
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
4123
4148
  () => api.getUserInfo(),
4124
4149
  [api]
@@ -4162,7 +4187,7 @@ var init_LiteLLMPage = __esm({
4162
4187
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
4163
4188
  [api, objectPermsEnabled]
4164
4189
  );
4165
- const teams = (0, import_react11.useMemo)(() => {
4190
+ const teams = (0, import_react12.useMemo)(() => {
4166
4191
  if (!allTeams?.length) return [];
4167
4192
  if (!userInfo) return allTeams;
4168
4193
  const userId = userInfo.user_id;
@@ -4179,7 +4204,7 @@ var init_LiteLLMPage = __esm({
4179
4204
  const endDate = dateRange.end.toISOString().split("T")[0];
4180
4205
  return api.getUsage(startDate, endDate);
4181
4206
  }, [api, dateRange]);
4182
- const handleDateRangeChange = (0, import_react11.useCallback)((range, preset) => {
4207
+ const handleDateRangeChange = (0, import_react12.useCallback)((range, preset) => {
4183
4208
  setDateRange(range);
4184
4209
  if (preset) {
4185
4210
  setCurrentPreset(preset);
@@ -4187,7 +4212,7 @@ var init_LiteLLMPage = __esm({
4187
4212
  setTeamUsageCache({});
4188
4213
  setTeamUsageLoading({});
4189
4214
  }, [setDateRange, setCurrentPreset]);
4190
- const loadTeamUsage = (0, import_react11.useCallback)(async (teamId) => {
4215
+ const loadTeamUsage = (0, import_react12.useCallback)(async (teamId) => {
4191
4216
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
4192
4217
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
4193
4218
  try {
@@ -4201,7 +4226,7 @@ var init_LiteLLMPage = __esm({
4201
4226
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
4202
4227
  }
4203
4228
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
4204
- const allowedModels = (0, import_react11.useMemo)(() => {
4229
+ const allowedModels = (0, import_react12.useMemo)(() => {
4205
4230
  if (!allModels?.length) return [];
4206
4231
  const userModels = userInfo?.models;
4207
4232
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -4214,7 +4239,7 @@ var init_LiteLLMPage = __esm({
4214
4239
  ]);
4215
4240
  return allModels.filter((m) => allowed.has(m.model_name));
4216
4241
  }, [allModels, userInfo, teams]);
4217
- const handleGenerateKey = (0, import_react11.useCallback)(
4242
+ const handleGenerateKey = (0, import_react12.useCallback)(
4218
4243
  async (request) => {
4219
4244
  try {
4220
4245
  const response = await api.generateKey(request);
@@ -4228,7 +4253,7 @@ var init_LiteLLMPage = __esm({
4228
4253
  },
4229
4254
  [api, refreshKeys]
4230
4255
  );
4231
- const handleUpdateKey = (0, import_react11.useCallback)(
4256
+ const handleUpdateKey = (0, import_react12.useCallback)(
4232
4257
  async (keyId, request) => {
4233
4258
  try {
4234
4259
  await api.updateKey(keyId, request);
@@ -4241,7 +4266,7 @@ var init_LiteLLMPage = __esm({
4241
4266
  },
4242
4267
  [api, refreshKeys]
4243
4268
  );
4244
- const handleBlockKey = (0, import_react11.useCallback)(
4269
+ const handleBlockKey = (0, import_react12.useCallback)(
4245
4270
  async (keyId) => {
4246
4271
  try {
4247
4272
  await api.blockKey(keyId);
@@ -4253,7 +4278,7 @@ var init_LiteLLMPage = __esm({
4253
4278
  },
4254
4279
  [api, refreshKeys]
4255
4280
  );
4256
- const handleUnblockKey = (0, import_react11.useCallback)(
4281
+ const handleUnblockKey = (0, import_react12.useCallback)(
4257
4282
  async (keyId) => {
4258
4283
  try {
4259
4284
  await api.unblockKey(keyId);
@@ -4265,7 +4290,7 @@ var init_LiteLLMPage = __esm({
4265
4290
  },
4266
4291
  [api, refreshKeys]
4267
4292
  );
4268
- const handleResetKeySpend = (0, import_react11.useCallback)(
4293
+ const handleResetKeySpend = (0, import_react12.useCallback)(
4269
4294
  async (keyId) => {
4270
4295
  try {
4271
4296
  await api.resetKeySpend(keyId);
@@ -4277,7 +4302,7 @@ var init_LiteLLMPage = __esm({
4277
4302
  },
4278
4303
  [api, refreshKeys]
4279
4304
  );
4280
- const handleDeleteKey = (0, import_react11.useCallback)(
4305
+ const handleDeleteKey = (0, import_react12.useCallback)(
4281
4306
  async (keyId) => {
4282
4307
  try {
4283
4308
  await api.deleteKey(keyId);
@@ -4294,7 +4319,7 @@ var init_LiteLLMPage = __esm({
4294
4319
  },
4295
4320
  [api, refreshKeys]
4296
4321
  );
4297
- const handlePruneExpiredKeys = (0, import_react11.useCallback)(
4322
+ const handlePruneExpiredKeys = (0, import_react12.useCallback)(
4298
4323
  async () => {
4299
4324
  try {
4300
4325
  const result = await api.pruneExpiredKeys();
@@ -4309,14 +4334,14 @@ var init_LiteLLMPage = __esm({
4309
4334
  );
4310
4335
  const isInitialLoading = userLoading && !userInfo;
4311
4336
  if (isInitialLoading) {
4312
- return /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react11.default.createElement(import_CircularProgress6.default, null));
4337
+ return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react12.default.createElement(import_CircularProgress6.default, null));
4313
4338
  }
4314
4339
  if (userError || !userInfo) {
4315
4340
  const isProvisioningEnabled = userError?.body?.provisioning === true;
4316
4341
  const hint = userError?.body?.hint;
4317
- return /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { p: 3 }, /* @__PURE__ */ import_react11.default.createElement(import_Paper7.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
4342
+ return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { p: 3 }, /* @__PURE__ */ import_react12.default.createElement(import_Paper8.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
4318
4343
  }
4319
- const pageTabs = /* @__PURE__ */ import_react11.default.createElement(
4344
+ const pageTabs = /* @__PURE__ */ import_react12.default.createElement(
4320
4345
  import_Tabs2.default,
4321
4346
  {
4322
4347
  value: activeTab,
@@ -4329,13 +4354,13 @@ var init_LiteLLMPage = __esm({
4329
4354
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
4330
4355
  }
4331
4356
  },
4332
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
4333
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
4334
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
4335
- /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
4336
- userInfo.can_view_audit && /* @__PURE__ */ import_react11.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
4357
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
4358
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
4359
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
4360
+ /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
4361
+ userInfo.can_view_audit && /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
4337
4362
  );
4338
- return /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react11.default.createElement(
4363
+ return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react12.default.createElement(
4339
4364
  DashboardHeader,
4340
4365
  {
4341
4366
  userInfo,
@@ -4345,7 +4370,7 @@ var init_LiteLLMPage = __esm({
4345
4370
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
4346
4371
  tabs: pageTabs
4347
4372
  }
4348
- ), activeTab === "overview" && /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ import_react11.default.createElement(
4373
+ ), activeTab === "overview" && /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ import_react12.default.createElement(
4349
4374
  UsageStats,
4350
4375
  {
4351
4376
  usage: usage ?? null,
@@ -4356,17 +4381,15 @@ var init_LiteLLMPage = __esm({
4356
4381
  loading: usageLoading,
4357
4382
  userInfo
4358
4383
  }
4359
- )), /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ import_react11.default.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ import_react11.default.createElement(
4384
+ )), /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ import_react12.default.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ import_react12.default.createElement(
4360
4385
  KeysTable,
4361
4386
  {
4362
4387
  keys: keys ?? [],
4363
- models: allowedModels,
4364
4388
  loading: keysLoading || modelsLoading,
4365
4389
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
4366
- onUpdateKey: handleUpdateKey,
4390
+ onEditKey: setKeyToEdit,
4367
4391
  onBlockKey: handleBlockKey,
4368
4392
  onUnblockKey: handleUnblockKey,
4369
- onResetKeySpend: handleResetKeySpend,
4370
4393
  onDeleteKey: handleDeleteKey,
4371
4394
  onPruneExpiredKeys: handlePruneExpiredKeys
4372
4395
  }
@@ -4375,7 +4398,7 @@ var init_LiteLLMPage = __esm({
4375
4398
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
4376
4399
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
4377
4400
  const visibleTeams = Array.from(teamsById.values());
4378
- return /* @__PURE__ */ import_react11.default.createElement(
4401
+ return /* @__PURE__ */ import_react12.default.createElement(
4379
4402
  TeamUsage,
4380
4403
  {
4381
4404
  teams: visibleTeams,
@@ -4391,27 +4414,34 @@ var init_LiteLLMPage = __esm({
4391
4414
  onCreateTeam: () => setManageTeam({ mode: "create" })
4392
4415
  }
4393
4416
  );
4394
- })(), activeTab === "models" && /* @__PURE__ */ import_react11.default.createElement(
4417
+ })(), activeTab === "models" && /* @__PURE__ */ import_react12.default.createElement(
4395
4418
  ModelsTable,
4396
4419
  {
4397
4420
  allModels: allModels ?? [],
4398
4421
  teams: teams ?? [],
4399
4422
  loading: modelsLoading
4400
4423
  }
4401
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react11.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react11.default.createElement(
4402
- GenerateKeyDialog,
4424
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react12.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react12.default.createElement(
4425
+ KeyFormDialog,
4403
4426
  {
4404
- open: generateDialogOpen,
4405
- onClose: () => setGenerateDialogOpen(false),
4427
+ open: keyFormOpen,
4428
+ onClose: () => {
4429
+ setGenerateDialogOpen(false);
4430
+ setKeyToEdit(null);
4431
+ },
4432
+ mode: keyToEdit ? "edit" : "create",
4433
+ keyToEdit,
4406
4434
  keys: keys ?? [],
4407
4435
  models: allowedModels,
4408
4436
  teams: teams ?? [],
4409
4437
  username: userInfo.user_id,
4410
4438
  keyGenerationSettings: liteLlmConfig?.keyGeneration,
4411
- onGenerateKey: handleGenerateKey,
4439
+ onCreateKey: handleGenerateKey,
4440
+ onUpdateKey: handleUpdateKey,
4441
+ onResetKeySpend: handleResetKeySpend,
4412
4442
  onGetConfig: () => api.getConfig()
4413
4443
  }
4414
- ), /* @__PURE__ */ import_react11.default.createElement(
4444
+ ), /* @__PURE__ */ import_react12.default.createElement(
4415
4445
  ManageTeamDialog,
4416
4446
  {
4417
4447
  open: !!manageTeam,
@@ -4473,7 +4503,7 @@ var init_LiteLLMPage = __esm({
4473
4503
  refreshTeams();
4474
4504
  }
4475
4505
  }
4476
- ), /* @__PURE__ */ import_react11.default.createElement(
4506
+ ), /* @__PURE__ */ import_react12.default.createElement(
4477
4507
  import_Snackbar2.default,
4478
4508
  {
4479
4509
  open: !!snackbar,
@@ -4481,7 +4511,7 @@ var init_LiteLLMPage = __esm({
4481
4511
  onClose: () => setSnackbar(null),
4482
4512
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
4483
4513
  },
4484
- snackbar ? /* @__PURE__ */ import_react11.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4514
+ snackbar ? /* @__PURE__ */ import_react12.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
4485
4515
  ));
4486
4516
  };
4487
4517
  }
@@ -4492,6 +4522,7 @@ var index_exports = {};
4492
4522
  __export(index_exports, {
4493
4523
  DashboardHeader: () => DashboardHeader,
4494
4524
  GenerateKeyDialog: () => GenerateKeyDialog,
4525
+ KeyFormDialog: () => KeyFormDialog,
4495
4526
  KeysTable: () => KeysTable,
4496
4527
  LiteLLMBudgetGauges: () => LiteLLMBudgetGauges,
4497
4528
  LiteLLMBudgetWidget: () => LiteLLMBudgetWidget,
@@ -4512,7 +4543,7 @@ __export(index_exports, {
4512
4543
  module.exports = __toCommonJS(index_exports);
4513
4544
 
4514
4545
  // src/plugin.tsx
4515
- var import_react12 = __toESM(require("react"));
4546
+ var import_react13 = __toESM(require("react"));
4516
4547
  var import_icons_material9 = require("@mui/icons-material");
4517
4548
  var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
4518
4549
  init_api();
@@ -4527,10 +4558,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
4527
4558
  params: {
4528
4559
  path: "/litellm",
4529
4560
  title: "LiteLLM",
4530
- icon: /* @__PURE__ */ import_react12.default.createElement(import_icons_material9.TrendingUp, null),
4561
+ icon: /* @__PURE__ */ import_react13.default.createElement(import_icons_material9.TrendingUp, null),
4531
4562
  loader: async () => {
4532
4563
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
4533
- return /* @__PURE__ */ import_react12.default.createElement(LiteLLMPage2, null);
4564
+ return /* @__PURE__ */ import_react13.default.createElement(LiteLLMPage2, null);
4534
4565
  }
4535
4566
  }
4536
4567
  });
@@ -4547,10 +4578,10 @@ init_UsageStats();
4547
4578
  init_TeamUsage();
4548
4579
 
4549
4580
  // src/components/LiteLLMHomeWidget.tsx
4550
- var import_react13 = __toESM(require("react"));
4551
- var import_Paper8 = __toESM(require("@mui/material/Paper"));
4552
- var import_Box12 = __toESM(require("@mui/material/Box"));
4553
- var import_Typography12 = __toESM(require("@mui/material/Typography"));
4581
+ var import_react14 = __toESM(require("react"));
4582
+ var import_Paper9 = __toESM(require("@mui/material/Paper"));
4583
+ var import_Box13 = __toESM(require("@mui/material/Box"));
4584
+ var import_Typography13 = __toESM(require("@mui/material/Typography"));
4554
4585
  var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
4555
4586
  var import_Select2 = __toESM(require("@mui/material/Select"));
4556
4587
  var import_MenuItem6 = __toESM(require("@mui/material/MenuItem"));
@@ -4573,19 +4604,19 @@ function presetToDateRange(preset) {
4573
4604
  }
4574
4605
  return { start, end };
4575
4606
  }
4576
- var Kpi = ({ label, value }) => /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, null, /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "subtitle1", fontWeight: 600 }, value));
4607
+ var Kpi = ({ label, value }) => /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, null, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "subtitle1", fontWeight: 600 }, value));
4577
4608
  var LiteLLMHomeWidget = ({
4578
4609
  defaultPeriod = "7d",
4579
4610
  title = "LiteLLM Usage"
4580
4611
  }) => {
4581
4612
  const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
4582
- const [period, setPeriod] = (0, import_react13.useState)(defaultPeriod);
4583
- const [loading, setLoading] = (0, import_react13.useState)(true);
4584
- const [usageError, setUsageError] = (0, import_react13.useState)(null);
4585
- const [keysError, setKeysError] = (0, import_react13.useState)(null);
4586
- const [usage, setUsage] = (0, import_react13.useState)(null);
4587
- const [keys, setKeys] = (0, import_react13.useState)([]);
4588
- (0, import_react13.useEffect)(() => {
4613
+ const [period, setPeriod] = (0, import_react14.useState)(defaultPeriod);
4614
+ const [loading, setLoading] = (0, import_react14.useState)(true);
4615
+ const [usageError, setUsageError] = (0, import_react14.useState)(null);
4616
+ const [keysError, setKeysError] = (0, import_react14.useState)(null);
4617
+ const [usage, setUsage] = (0, import_react14.useState)(null);
4618
+ const [keys, setKeys] = (0, import_react14.useState)([]);
4619
+ (0, import_react14.useEffect)(() => {
4589
4620
  let cancelled = false;
4590
4621
  setLoading(true);
4591
4622
  setUsageError(null);
@@ -4622,17 +4653,17 @@ var LiteLLMHomeWidget = ({
4622
4653
  spend: d.spend
4623
4654
  }));
4624
4655
  const hasSparkline = dailyData.length > 0;
4625
- return /* @__PURE__ */ import_react13.default.createElement(import_Paper8.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "h6" }, title), /* @__PURE__ */ import_react13.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react13.default.createElement(
4656
+ return /* @__PURE__ */ import_react14.default.createElement(import_Paper9.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "h6" }, title), /* @__PURE__ */ import_react14.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react14.default.createElement(
4626
4657
  import_Select2.default,
4627
4658
  {
4628
4659
  value: period,
4629
4660
  onChange: (e) => setPeriod(e.target.value),
4630
4661
  displayEmpty: true
4631
4662
  },
4632
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
4633
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
4634
- /* @__PURE__ */ import_react13.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
4635
- ))), loading && /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react13.default.createElement(import_CircularProgress7.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react13.default.createElement(import_Alert7.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react13.default.createElement(import_Alert7.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react13.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react13.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { height: 120 }, /* @__PURE__ */ import_react13.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react13.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react13.default.createElement(
4663
+ /* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
4664
+ /* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
4665
+ /* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
4666
+ ))), loading && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react14.default.createElement(import_CircularProgress7.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react14.default.createElement(import_Alert7.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react14.default.createElement(import_Alert7.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { height: 120 }, /* @__PURE__ */ import_react14.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react14.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react14.default.createElement(
4636
4667
  import_recharts3.Area,
4637
4668
  {
4638
4669
  type: "monotone",
@@ -4650,24 +4681,34 @@ var LiteLLMHomeWidget = ({
4650
4681
  init_LiteLLMBudgetWidget();
4651
4682
 
4652
4683
  // src/components/LiteLLMBudgetGauges.tsx
4653
- var import_react14 = __toESM(require("react"));
4654
- var import_Paper9 = __toESM(require("@mui/material/Paper"));
4655
- var import_Box13 = __toESM(require("@mui/material/Box"));
4656
- var import_Typography13 = __toESM(require("@mui/material/Typography"));
4684
+ var import_react15 = __toESM(require("react"));
4685
+ var import_Paper10 = __toESM(require("@mui/material/Paper"));
4686
+ var import_Box14 = __toESM(require("@mui/material/Box"));
4687
+ var import_Button6 = __toESM(require("@mui/material/Button"));
4688
+ var import_Typography14 = __toESM(require("@mui/material/Typography"));
4657
4689
  var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress"));
4658
4690
  var import_Alert8 = __toESM(require("@mui/material/Alert"));
4691
+ var import_Collapse4 = __toESM(require("@mui/material/Collapse"));
4692
+ var import_icons_material10 = require("@mui/icons-material");
4659
4693
  var import_core_plugin_api6 = require("@backstage/core-plugin-api");
4660
4694
  var import_core_components2 = require("@backstage/core-components");
4661
4695
  init_api();
4662
4696
  init_format();
4663
4697
  init_ui();
4698
+ init_BudgetLimitList();
4664
4699
  init_budget();
4665
- var DEFAULT_KEYS_HREF = "/litellm?tab=keys";
4700
+ var MODULE_PATH = "/litellm";
4666
4701
  var LEVEL = {
4667
4702
  key: { name: "Key", none: "No key budget" },
4668
4703
  user: { name: "User", none: "No personal budget" },
4669
4704
  team: { name: "Team", none: "No team budget" }
4670
4705
  };
4706
+ var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
4707
+ var CTA_LABELS = {
4708
+ "new-key": "New key",
4709
+ module: "Open module",
4710
+ "all-limits": "All limits"
4711
+ };
4671
4712
  function fmtUsdShort(n) {
4672
4713
  const v = n ?? 0;
4673
4714
  if (v >= 100 && Number.isInteger(v)) return `$${v}`;
@@ -4690,8 +4731,8 @@ var LevelGauge = ({
4690
4731
  const limit = gauge.limit;
4691
4732
  const tone = limit ? budgetTone2(limit.pct) : "neutral";
4692
4733
  const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
4693
- return /* @__PURE__ */ import_react14.default.createElement(
4694
- import_Box13.default,
4734
+ return /* @__PURE__ */ import_react15.default.createElement(
4735
+ import_Box14.default,
4695
4736
  {
4696
4737
  sx: {
4697
4738
  display: "flex",
@@ -4701,7 +4742,7 @@ var LevelGauge = ({
4701
4742
  flex: 1
4702
4743
  }
4703
4744
  },
4704
- /* @__PURE__ */ import_react14.default.createElement(
4745
+ /* @__PURE__ */ import_react15.default.createElement(
4705
4746
  Gauge,
4706
4747
  {
4707
4748
  value: limit?.pct ?? 0,
@@ -4710,8 +4751,8 @@ var LevelGauge = ({
4710
4751
  label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
4711
4752
  }
4712
4753
  ),
4713
- /* @__PURE__ */ import_react14.default.createElement(
4714
- import_Typography13.default,
4754
+ /* @__PURE__ */ import_react15.default.createElement(
4755
+ import_Typography14.default,
4715
4756
  {
4716
4757
  sx: (theme) => ({
4717
4758
  mt: 0.75,
@@ -4724,8 +4765,8 @@ var LevelGauge = ({
4724
4765
  },
4725
4766
  name
4726
4767
  ),
4727
- limit ? /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement(
4728
- import_Typography13.default,
4768
+ limit ? /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
4769
+ import_Typography14.default,
4729
4770
  {
4730
4771
  variant: "caption",
4731
4772
  title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
@@ -4740,8 +4781,8 @@ var LevelGauge = ({
4740
4781
  }
4741
4782
  },
4742
4783
  limit.label
4743
- ), /* @__PURE__ */ import_react14.default.createElement(
4744
- import_Typography13.default,
4784
+ ), /* @__PURE__ */ import_react15.default.createElement(
4785
+ import_Typography14.default,
4745
4786
  {
4746
4787
  variant: "caption",
4747
4788
  color: "text.secondary",
@@ -4752,8 +4793,8 @@ var LevelGauge = ({
4752
4793
  }
4753
4794
  },
4754
4795
  spendCaption(limit)
4755
- ), extra > 0 ? /* @__PURE__ */ import_react14.default.createElement(
4756
- import_Typography13.default,
4796
+ ), extra > 0 ? /* @__PURE__ */ import_react15.default.createElement(
4797
+ import_Typography14.default,
4757
4798
  {
4758
4799
  component: import_core_components2.Link,
4759
4800
  to: keysHref,
@@ -4768,8 +4809,8 @@ var LevelGauge = ({
4768
4809
  "+",
4769
4810
  extra,
4770
4811
  " more"
4771
- ) : /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ import_react14.default.createElement(
4772
- import_Typography13.default,
4812
+ ) : /* @__PURE__ */ import_react15.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ import_react15.default.createElement(
4813
+ import_Typography14.default,
4773
4814
  {
4774
4815
  variant: "caption",
4775
4816
  color: "text.secondary",
@@ -4782,16 +4823,32 @@ var LevelGauge = ({
4782
4823
  var LiteLLMBudgetGauges = ({
4783
4824
  title = "Budget",
4784
4825
  size = 72,
4785
- keysHref = DEFAULT_KEYS_HREF,
4826
+ keysHref,
4827
+ moduleHref = MODULE_PATH,
4828
+ onCreateKey,
4829
+ ctas,
4830
+ maxExpandedKeys = 8,
4831
+ expanded,
4832
+ defaultExpanded = false,
4833
+ onExpandedChange,
4786
4834
  action
4787
4835
  }) => {
4788
4836
  const api = (0, import_core_plugin_api6.useApi)(liteLlmApiRef);
4789
- const [loading, setLoading] = (0, import_react14.useState)(true);
4790
- const [error, setError] = (0, import_react14.useState)(null);
4791
- const [user, setUser] = (0, import_react14.useState)(null);
4792
- const [teams, setTeams] = (0, import_react14.useState)([]);
4793
- const [keys, setKeys] = (0, import_react14.useState)([]);
4794
- (0, import_react14.useEffect)(() => {
4837
+ const [loading, setLoading] = (0, import_react15.useState)(true);
4838
+ const [error, setError] = (0, import_react15.useState)(null);
4839
+ const [user, setUser] = (0, import_react15.useState)(null);
4840
+ const [teams, setTeams] = (0, import_react15.useState)([]);
4841
+ const [keys, setKeys] = (0, import_react15.useState)([]);
4842
+ const isControlled = expanded !== void 0;
4843
+ const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react15.useState)(defaultExpanded);
4844
+ const isExpanded = isControlled ? expanded : uncontrolledExpanded;
4845
+ const toggleExpanded = (0, import_react15.useCallback)(() => {
4846
+ const next = !isExpanded;
4847
+ if (!isControlled) setUncontrolledExpanded(next);
4848
+ onExpandedChange?.(next);
4849
+ }, [isControlled, isExpanded, onExpandedChange]);
4850
+ const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
4851
+ (0, import_react15.useEffect)(() => {
4795
4852
  let cancelled = false;
4796
4853
  setLoading(true);
4797
4854
  setError(null);
@@ -4815,35 +4872,138 @@ var LiteLLMBudgetGauges = ({
4815
4872
  cancelled = true;
4816
4873
  };
4817
4874
  }, [api]);
4818
- const summary = (0, import_react14.useMemo)(
4875
+ const summary = (0, import_react15.useMemo)(
4819
4876
  () => buildBudgetSummary(user, teams, keys, keys.length),
4820
4877
  [user, teams, keys]
4821
4878
  );
4822
- const gauges = (0, import_react14.useMemo)(() => buildBudgetGauges(summary), [summary]);
4823
- const headline = (0, import_react14.useMemo)(() => budgetHeadline(summary), [summary]);
4879
+ const gauges = (0, import_react15.useMemo)(() => buildBudgetGauges(summary), [summary]);
4880
+ const headline = (0, import_react15.useMemo)(() => budgetHeadline(summary), [summary]);
4881
+ const expandedSummary = (0, import_react15.useMemo)(
4882
+ () => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
4883
+ [user, teams, keys, maxExpandedKeys]
4884
+ );
4885
+ const expandedLimits = (0, import_react15.useMemo)(
4886
+ () => allBudgetLimits(expandedSummary),
4887
+ [expandedSummary]
4888
+ );
4824
4889
  const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
4825
- return /* @__PURE__ */ import_react14.default.createElement(import_Paper9.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ import_react14.default.createElement(
4890
+ const resolvedCtas = (0, import_react15.useMemo)(() => {
4891
+ const list = ctas ?? DEFAULT_CTAS;
4892
+ return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
4893
+ }, [ctas, headline.count]);
4894
+ const renderCta = (cta, index) => {
4895
+ const label = cta.label ?? CTA_LABELS[cta.kind];
4896
+ const key = `${cta.kind}-${index}`;
4897
+ switch (cta.kind) {
4898
+ case "new-key":
4899
+ return onCreateKey ? /* @__PURE__ */ import_react15.default.createElement(import_Button6.default, { key, size: "small", variant: "contained", onClick: onCreateKey }, label) : /* @__PURE__ */ import_react15.default.createElement(
4900
+ import_Button6.default,
4901
+ {
4902
+ key,
4903
+ size: "small",
4904
+ variant: "contained",
4905
+ component: import_core_components2.Link,
4906
+ to: `${moduleHref}?generate=1`
4907
+ },
4908
+ label
4909
+ );
4910
+ case "module":
4911
+ return /* @__PURE__ */ import_react15.default.createElement(import_Button6.default, { key, size: "small", variant: "outlined", component: import_core_components2.Link, to: moduleHref }, label);
4912
+ case "all-limits":
4913
+ return /* @__PURE__ */ import_react15.default.createElement(
4914
+ import_Button6.default,
4915
+ {
4916
+ key,
4917
+ size: "small",
4918
+ variant: "text",
4919
+ onClick: toggleExpanded,
4920
+ endIcon: /* @__PURE__ */ import_react15.default.createElement(
4921
+ import_icons_material10.ExpandMore,
4922
+ {
4923
+ sx: {
4924
+ transform: isExpanded ? "rotate(180deg)" : "none",
4925
+ transition: (theme) => theme.transitions.create("transform")
4926
+ }
4927
+ }
4928
+ )
4929
+ },
4930
+ label
4931
+ );
4932
+ /* istanbul ignore next — exhaustiveness */
4933
+ default:
4934
+ return null;
4935
+ }
4936
+ };
4937
+ const hasFooter = resolvedCtas.length > 0 || !!action;
4938
+ return /* @__PURE__ */ import_react15.default.createElement(import_Paper10.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react15.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ import_react15.default.createElement(import_Typography14.default, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ import_react15.default.createElement(
4826
4939
  StatusPill,
4827
4940
  {
4828
4941
  label: summaryText,
4829
4942
  tone: budgetTone2(headline.closest?.pct ?? 0),
4830
4943
  dot: headline.count > 0
4831
4944
  }
4832
- )), loading && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ import_react14.default.createElement(import_CircularProgress8.default, { size: 28 })), !loading && error && /* @__PURE__ */ import_react14.default.createElement(import_Alert8.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ import_react14.default.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref }))), action && /* @__PURE__ */ import_react14.default.createElement(
4833
- import_Box13.default,
4945
+ )), 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(
4946
+ import_Box14.default,
4947
+ {
4948
+ sx: {
4949
+ mt: 1.5,
4950
+ // Bound the expanded list so a user with many limits still
4951
+ // keeps the card compact and the toggle within reach.
4952
+ maxHeight: 300,
4953
+ overflowY: "auto"
4954
+ }
4955
+ },
4956
+ /* @__PURE__ */ import_react15.default.createElement(LimitListPanel, null, /* @__PURE__ */ import_react15.default.createElement(
4957
+ BudgetLimitList,
4958
+ {
4959
+ limits: expandedLimits,
4960
+ hiddenKeyCount: expandedSummary.hiddenBudgetedKeys,
4961
+ keysHref: keysTabHref
4962
+ }
4963
+ ))
4964
+ ))), hasFooter && /* @__PURE__ */ import_react15.default.createElement(
4965
+ import_Box14.default,
4834
4966
  {
4835
4967
  sx: (theme) => ({
4836
4968
  mt: 2,
4837
4969
  pt: 2,
4838
- borderTop: `1px solid ${theme.palette.divider}`
4970
+ borderTop: `1px solid ${theme.palette.divider}`,
4971
+ display: "flex",
4972
+ alignItems: "center",
4973
+ gap: 1,
4974
+ flexWrap: "wrap"
4839
4975
  })
4840
4976
  },
4977
+ resolvedCtas.map(renderCta),
4841
4978
  action
4842
4979
  ));
4843
4980
  };
4844
4981
 
4845
4982
  // src/index.ts
4846
- init_GenerateKeyDialog();
4983
+ init_KeyFormDialog();
4984
+
4985
+ // src/components/GenerateKeyDialog.tsx
4986
+ var import_react16 = __toESM(require("react"));
4987
+ init_KeyFormDialog();
4988
+ var GenerateKeyDialog = (props) => /* @__PURE__ */ import_react16.default.createElement(
4989
+ KeyFormDialog,
4990
+ {
4991
+ open: props.open,
4992
+ onClose: props.onClose,
4993
+ mode: "create",
4994
+ keys: props.keys,
4995
+ models: props.models,
4996
+ teams: props.teams,
4997
+ username: props.username,
4998
+ keyGenerationSettings: props.keyGenerationSettings,
4999
+ onCreateKey: props.onGenerateKey,
5000
+ onUpdateKey: async () => {
5001
+ },
5002
+ onGetConfig: props.onGetConfig
5003
+ }
5004
+ );
5005
+
5006
+ // src/index.ts
4847
5007
  init_ManageTeamDialog();
4848
5008
  init_api();
4849
5009
  init_permissions();