@acarmisc/backstage-plugin-litellm 0.12.3 → 0.12.4

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
@@ -175,12 +175,18 @@ var init_api = __esm({
175
175
  });
176
176
 
177
177
  // src/components/DashboardHeader.tsx
178
- var import_react, import_material, import_icons_material, DashboardHeader;
178
+ var import_react, import_Box, import_Typography, import_LinearProgress, import_Paper, import_Chip, import_Button, import_Stack, import_icons_material, DashboardHeader;
179
179
  var init_DashboardHeader = __esm({
180
180
  "src/components/DashboardHeader.tsx"() {
181
181
  "use strict";
182
182
  import_react = __toESM(require("react"));
183
- import_material = require("@mui/material");
183
+ import_Box = __toESM(require("@mui/material/Box"));
184
+ import_Typography = __toESM(require("@mui/material/Typography"));
185
+ import_LinearProgress = __toESM(require("@mui/material/LinearProgress"));
186
+ import_Paper = __toESM(require("@mui/material/Paper"));
187
+ import_Chip = __toESM(require("@mui/material/Chip"));
188
+ import_Button = __toESM(require("@mui/material/Button"));
189
+ import_Stack = __toESM(require("@mui/material/Stack"));
184
190
  import_icons_material = require("@mui/icons-material");
185
191
  init_api();
186
192
  DashboardHeader = ({
@@ -196,11 +202,11 @@ var init_DashboardHeader = __esm({
196
202
  return { total: keys.length, expired, expiringSoon };
197
203
  }, [keys]);
198
204
  if (loading) {
199
- return /* @__PURE__ */ import_react.default.createElement(import_material.Paper, { sx: { p: 2, mb: 2 } }, /* @__PURE__ */ import_react.default.createElement(import_material.LinearProgress, null));
205
+ return /* @__PURE__ */ import_react.default.createElement(import_Paper.default, { sx: { p: 2, mb: 2 } }, /* @__PURE__ */ import_react.default.createElement(import_LinearProgress.default, null));
200
206
  }
201
207
  const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;
202
- return /* @__PURE__ */ import_react.default.createElement(import_material.Paper, { sx: { p: 2, mb: 2 } }, /* @__PURE__ */ import_react.default.createElement(import_material.Box, { display: "flex", alignItems: "flex-start", gap: 2, flexWrap: "wrap" }, /* @__PURE__ */ import_react.default.createElement(import_material.Box, { flexGrow: 1 }, /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { variant: "h6" }, displayName), /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { variant: "caption", color: "text.secondary" }, userInfo.user_id), teams.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_material.Box, { display: "flex", gap: 0.75, flexWrap: "wrap", mt: 1 }, teams.map((team) => /* @__PURE__ */ import_react.default.createElement(
203
- import_material.Chip,
208
+ return /* @__PURE__ */ import_react.default.createElement(import_Paper.default, { sx: { p: 2, mb: 2 } }, /* @__PURE__ */ import_react.default.createElement(import_Box.default, { display: "flex", alignItems: "flex-start", gap: 2, flexWrap: "wrap" }, /* @__PURE__ */ import_react.default.createElement(import_Box.default, { flexGrow: 1 }, /* @__PURE__ */ import_react.default.createElement(import_Typography.default, { variant: "h6" }, displayName), /* @__PURE__ */ import_react.default.createElement(import_Typography.default, { variant: "caption", color: "text.secondary" }, userInfo.user_id), teams.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_Box.default, { display: "flex", gap: 0.75, flexWrap: "wrap", mt: 1 }, teams.map((team) => /* @__PURE__ */ import_react.default.createElement(
209
+ import_Chip.default,
204
210
  {
205
211
  key: team.team_id,
206
212
  label: team.team_alias || team.team_id,
@@ -208,8 +214,8 @@ var init_DashboardHeader = __esm({
208
214
  variant: "outlined",
209
215
  color: "primary"
210
216
  }
211
- )))), /* @__PURE__ */ import_react.default.createElement(import_material.Box, { display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap" }, /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "row", spacing: 1 }, /* @__PURE__ */ import_react.default.createElement(import_material.Chip, { icon: /* @__PURE__ */ import_react.default.createElement(import_icons_material.Key, { fontSize: "small" }), label: `${counts.total} keys`, size: "small" }), /* @__PURE__ */ import_react.default.createElement(
212
- import_material.Chip,
217
+ )))), /* @__PURE__ */ import_react.default.createElement(import_Box.default, { display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap" }, /* @__PURE__ */ import_react.default.createElement(import_Stack.default, { direction: "row", spacing: 1 }, /* @__PURE__ */ import_react.default.createElement(import_Chip.default, { icon: /* @__PURE__ */ import_react.default.createElement(import_icons_material.Key, { fontSize: "small" }), label: `${counts.total} keys`, size: "small" }), /* @__PURE__ */ import_react.default.createElement(
218
+ import_Chip.default,
213
219
  {
214
220
  icon: /* @__PURE__ */ import_react.default.createElement(import_icons_material.Warning, { fontSize: "small" }),
215
221
  label: `${counts.expired} expired`,
@@ -217,7 +223,7 @@ var init_DashboardHeader = __esm({
217
223
  color: counts.expired > 0 ? "error" : "default"
218
224
  }
219
225
  ), /* @__PURE__ */ import_react.default.createElement(
220
- import_material.Chip,
226
+ import_Chip.default,
221
227
  {
222
228
  icon: /* @__PURE__ */ import_react.default.createElement(import_icons_material.Schedule, { fontSize: "small" }),
223
229
  label: `${counts.expiringSoon} expiring soon`,
@@ -225,7 +231,7 @@ var init_DashboardHeader = __esm({
225
231
  color: counts.expiringSoon > 0 ? "warning" : "default"
226
232
  }
227
233
  )), /* @__PURE__ */ import_react.default.createElement(
228
- import_material.Button,
234
+ import_Button.default,
229
235
  {
230
236
  variant: "contained",
231
237
  color: "primary",
@@ -239,19 +245,39 @@ var init_DashboardHeader = __esm({
239
245
  });
240
246
 
241
247
  // src/components/KeysTable.tsx
248
+ function expiryChipLabel(status, expiresAt) {
249
+ if (status === "expired") return "Expired";
250
+ if (status === "soon") return `${Math.ceil((new Date(expiresAt).getTime() - Date.now()) / 864e5)}d left`;
251
+ return formatDate(expiresAt);
252
+ }
253
+ function expiryChipColor(status) {
254
+ if (status === "expired") return "error";
255
+ if (status === "soon") return "warning";
256
+ return "default";
257
+ }
242
258
  function ExpiryChip({ expiresAt }) {
243
259
  const status = expiryStatus(expiresAt);
244
- if (!status) return /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2", color: "text.secondary" }, "-");
245
- const label = status === "expired" ? "Expired" : status === "soon" ? `${Math.ceil((new Date(expiresAt).getTime() - Date.now()) / 864e5)}d left` : formatDate(expiresAt);
246
- const color = status === "expired" ? "error" : status === "soon" ? "warning" : "default";
260
+ if (!status) return /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "body2", color: "text.secondary" }, "-");
261
+ const label = expiryChipLabel(status, expiresAt);
262
+ const color = expiryChipColor(status);
247
263
  const icon = status === "expired" || status === "soon" ? /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Warning, { fontSize: "small" }) : void 0;
248
- return /* @__PURE__ */ import_react2.default.createElement(import_material2.Chip, { label, color, size: "small", icon });
264
+ return /* @__PURE__ */ import_react2.default.createElement(import_Chip2.default, { label, color, size: "small", icon });
265
+ }
266
+ function budgetColor(pct) {
267
+ if (pct >= 100) return "error";
268
+ if (pct >= 80) return "warning";
269
+ return "primary";
270
+ }
271
+ function keyBlockIcon(isBlocking, blocked) {
272
+ if (isBlocking) return /* @__PURE__ */ import_react2.default.createElement(import_CircularProgress.default, { size: 18 });
273
+ if (blocked) return /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.LockOpen, { fontSize: "small" });
274
+ return /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Lock, { fontSize: "small" });
249
275
  }
250
276
  function BudgetCell({ spend, maxBudget }) {
251
- if (!maxBudget) return /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2", color: "text.secondary" }, "-");
277
+ if (!maxBudget) return /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "body2", color: "text.secondary" }, "-");
252
278
  const pct = Math.min(100, spend / maxBudget * 100);
253
- const color = pct >= 100 ? "error" : pct >= 80 ? "warning" : "primary";
254
- return /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { minWidth: 100 }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", justifyContent: "space-between" }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "caption" }, "$", spend.toFixed(2)), /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "caption", color: "text.secondary" }, "$", maxBudget)), /* @__PURE__ */ import_react2.default.createElement(import_material2.LinearProgress, { variant: "determinate", value: pct, color, sx: { height: 5, borderRadius: 1, mt: 0.25 } }));
279
+ const color = budgetColor(pct);
280
+ return /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { minWidth: 100 }, /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", justifyContent: "space-between" }, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "caption" }, "$", spend.toFixed(2)), /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "caption", color: "text.secondary" }, "$", maxBudget)), /* @__PURE__ */ import_react2.default.createElement(import_LinearProgress2.default, { variant: "determinate", value: pct, color, sx: { height: 5, borderRadius: 1, mt: 0.25 } }));
255
281
  }
256
282
  function fmtCost(perToken) {
257
283
  if (!perToken) return null;
@@ -273,12 +299,32 @@ function formatContextWindow(maxInput, maxOutput) {
273
299
  if (outPart) return `ctx ${outPart} out`;
274
300
  return null;
275
301
  }
276
- var import_react2, import_material2, import_icons_material2, maskKey, shortKeyId, formatDate, keyToEditForm, KeysTable;
302
+ var import_react2, import_Paper2, import_Table, import_TableBody, import_TableCell, import_TableContainer, import_TableHead, import_TableRow, import_Button2, import_IconButton, import_Box2, import_Typography2, import_Dialog, import_DialogTitle, import_DialogContent, import_DialogActions, import_TextField, import_Chip2, import_CircularProgress, import_Autocomplete, import_LinearProgress2, import_InputAdornment, import_icons_material2, maskKey, shortKeyId, formatDate, keyToEditForm, KeysTable;
277
303
  var init_KeysTable = __esm({
278
304
  "src/components/KeysTable.tsx"() {
279
305
  "use strict";
280
306
  import_react2 = __toESM(require("react"));
281
- import_material2 = require("@mui/material");
307
+ import_Paper2 = __toESM(require("@mui/material/Paper"));
308
+ import_Table = __toESM(require("@mui/material/Table"));
309
+ import_TableBody = __toESM(require("@mui/material/TableBody"));
310
+ import_TableCell = __toESM(require("@mui/material/TableCell"));
311
+ import_TableContainer = __toESM(require("@mui/material/TableContainer"));
312
+ import_TableHead = __toESM(require("@mui/material/TableHead"));
313
+ import_TableRow = __toESM(require("@mui/material/TableRow"));
314
+ import_Button2 = __toESM(require("@mui/material/Button"));
315
+ import_IconButton = __toESM(require("@mui/material/IconButton"));
316
+ import_Box2 = __toESM(require("@mui/material/Box"));
317
+ import_Typography2 = __toESM(require("@mui/material/Typography"));
318
+ import_Dialog = __toESM(require("@mui/material/Dialog"));
319
+ import_DialogTitle = __toESM(require("@mui/material/DialogTitle"));
320
+ import_DialogContent = __toESM(require("@mui/material/DialogContent"));
321
+ import_DialogActions = __toESM(require("@mui/material/DialogActions"));
322
+ import_TextField = __toESM(require("@mui/material/TextField"));
323
+ import_Chip2 = __toESM(require("@mui/material/Chip"));
324
+ import_CircularProgress = __toESM(require("@mui/material/CircularProgress"));
325
+ import_Autocomplete = __toESM(require("@mui/material/Autocomplete"));
326
+ import_LinearProgress2 = __toESM(require("@mui/material/LinearProgress"));
327
+ import_InputAdornment = __toESM(require("@mui/material/InputAdornment"));
282
328
  import_icons_material2 = require("@mui/icons-material");
283
329
  init_api();
284
330
  maskKey = (key) => {
@@ -412,10 +458,66 @@ var init_KeysTable = __esm({
412
458
  const inCost = fmtCost(m.input_cost_per_token);
413
459
  const outCost = fmtCost(m.output_cost_per_token);
414
460
  const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
415
- return /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, null, /* @__PURE__ */ import_react2.default.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
461
+ return /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, null, /* @__PURE__ */ import_react2.default.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
462
+ };
463
+ const renderTableBody = () => {
464
+ if (loading) {
465
+ return /* @__PURE__ */ import_react2.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, { colSpan: 8, align: "center" }, /* @__PURE__ */ import_react2.default.createElement(import_CircularProgress.default, { size: 24 })));
466
+ }
467
+ if (filteredKeys.length === 0) {
468
+ return /* @__PURE__ */ import_react2.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, { colSpan: 8, align: "center", sx: { py: 4 } }, filterText ? /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { color: "text.secondary" }, "No keys match filter") : /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { color: "text.secondary", gutterBottom: true }, "No keys yet \u2014 generate your first key to start calling models."), /* @__PURE__ */ import_react2.default.createElement(
469
+ import_Button2.default,
470
+ {
471
+ variant: "contained",
472
+ color: "primary",
473
+ size: "small",
474
+ startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Add, null),
475
+ onClick: onGenerateKeyClick
476
+ },
477
+ "Generate Your First Key"
478
+ ))));
479
+ }
480
+ return filteredKeys.map((key) => {
481
+ const keyId = key.token ?? key.key;
482
+ const isBlocking = blockingKeyId === keyId;
483
+ return /* @__PURE__ */ import_react2.default.createElement(import_TableRow.default, { key: keyId, sx: key.blocked ? { bgcolor: "action.disabledBackground" } : void 0 }, /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", alignItems: "center", gap: 0.5 }, key.key_alias || "-", key.blocked && /* @__PURE__ */ import_react2.default.createElement(import_Chip2.default, { label: "Blocked", color: "error", size: "small" }))), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ import_react2.default.createElement(
484
+ import_Typography2.default,
485
+ {
486
+ variant: "body2",
487
+ component: "code",
488
+ color: "text.secondary",
489
+ title: keyId,
490
+ sx: {
491
+ fontFamily: "monospace",
492
+ backgroundColor: "background.default",
493
+ px: 1,
494
+ py: 0.5,
495
+ borderRadius: 1
496
+ }
497
+ },
498
+ shortKeyId(keyId)
499
+ ), /* @__PURE__ */ import_react2.default.createElement(import_IconButton.default, { size: "small", onClick: () => copyToClipboard(keyId), title: "Copy Key ID" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, formatDate(key.created_at)), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react2.default.createElement(ExpiryChip, { expiresAt: key.expires_at })), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react2.default.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "body2" }, key.tpm_limit ?? "-", " / ", key.rpm_limit ?? "-")), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ import_react2.default.createElement(import_Chip2.default, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ import_react2.default.createElement(import_Chip2.default, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, { align: "right" }, /* @__PURE__ */ import_react2.default.createElement(import_IconButton.default, { onClick: () => handleOpenEdit(key), title: "Edit key" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Edit, { fontSize: "small" })), /* @__PURE__ */ import_react2.default.createElement(
500
+ import_IconButton.default,
501
+ {
502
+ onClick: () => handleToggleBlock(key),
503
+ disabled: isBlocking,
504
+ color: key.blocked ? "warning" : "default",
505
+ title: key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking"
506
+ },
507
+ keyBlockIcon(isBlocking, key.blocked)
508
+ ), /* @__PURE__ */ import_react2.default.createElement(
509
+ import_IconButton.default,
510
+ {
511
+ color: "error",
512
+ onClick: () => setDeleteConfirmId(keyId),
513
+ title: "Revoke key"
514
+ },
515
+ /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Delete, null)
516
+ )));
517
+ });
416
518
  };
417
- return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Paper, { sx: { mb: 2 } }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", p: 2, gap: 2 }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "h6" }, "Virtual Keys"), /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", gap: 1, alignItems: "center", flex: 1, justifyContent: "flex-end" }, /* @__PURE__ */ import_react2.default.createElement(
418
- import_material2.TextField,
519
+ return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_Paper2.default, { sx: { mb: 2 } }, /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", justifyContent: "space-between", alignItems: "center", p: 2, gap: 2 }, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "h6" }, "Virtual Keys"), /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", gap: 1, alignItems: "center", flex: 1, justifyContent: "flex-end" }, /* @__PURE__ */ import_react2.default.createElement(
520
+ import_TextField.default,
419
521
  {
420
522
  size: "small",
421
523
  placeholder: "Filter by alias or model\u2026",
@@ -423,11 +525,11 @@ var init_KeysTable = __esm({
423
525
  onChange: (e) => setFilterText(e.target.value),
424
526
  sx: { minWidth: 240 },
425
527
  InputProps: {
426
- startAdornment: /* @__PURE__ */ import_react2.default.createElement(import_material2.InputAdornment, { position: "start" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Search, { fontSize: "small" }))
528
+ startAdornment: /* @__PURE__ */ import_react2.default.createElement(import_InputAdornment.default, { position: "start" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Search, { fontSize: "small" }))
427
529
  }
428
530
  }
429
531
  ), expiredKeys.length > 0 && /* @__PURE__ */ import_react2.default.createElement(
430
- import_material2.Button,
532
+ import_Button2.default,
431
533
  {
432
534
  variant: "outlined",
433
535
  color: "error",
@@ -438,7 +540,7 @@ var init_KeysTable = __esm({
438
540
  expiredKeys.length,
439
541
  ")"
440
542
  ), /* @__PURE__ */ import_react2.default.createElement(
441
- import_material2.Button,
543
+ import_Button2.default,
442
544
  {
443
545
  variant: "contained",
444
546
  color: "primary",
@@ -446,55 +548,8 @@ var init_KeysTable = __esm({
446
548
  onClick: onGenerateKeyClick
447
549
  },
448
550
  "Generate New Key"
449
- ))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableContainer, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Table, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.TableHead, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.TableRow, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, "Alias"), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, "Key ID"), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, "Created"), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, "Expires"), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, "Budget"), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, "TPM / RPM"), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, "Models"), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableBody, null, loading ? /* @__PURE__ */ import_react2.default.createElement(import_material2.TableRow, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 24 }))) : filteredKeys.length === 0 ? /* @__PURE__ */ import_react2.default.createElement(import_material2.TableRow, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, { colSpan: 8, align: "center", sx: { py: 4 } }, filterText ? /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { color: "text.secondary" }, "No keys match filter") : /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { color: "text.secondary", gutterBottom: true }, "No keys yet \u2014 generate your first key to start calling models."), /* @__PURE__ */ import_react2.default.createElement(
450
- import_material2.Button,
451
- {
452
- variant: "contained",
453
- color: "primary",
454
- size: "small",
455
- startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Add, null),
456
- onClick: onGenerateKeyClick
457
- },
458
- "Generate Your First Key"
459
- )))) : filteredKeys.map((key) => {
460
- const keyId = key.token ?? key.key;
461
- const isBlocking = blockingKeyId === keyId;
462
- return /* @__PURE__ */ import_react2.default.createElement(import_material2.TableRow, { key: keyId, sx: key.blocked ? { bgcolor: "action.disabledBackground" } : void 0 }, /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", alignItems: "center", gap: 0.5 }, key.key_alias || "-", key.blocked && /* @__PURE__ */ import_react2.default.createElement(import_material2.Chip, { label: "Blocked", color: "error", size: "small" }))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ import_react2.default.createElement(
463
- import_material2.Typography,
464
- {
465
- variant: "body2",
466
- component: "code",
467
- color: "text.secondary",
468
- title: keyId,
469
- sx: {
470
- fontFamily: "monospace",
471
- backgroundColor: "background.default",
472
- px: 1,
473
- py: 0.5,
474
- borderRadius: 1
475
- }
476
- },
477
- shortKeyId(keyId)
478
- ), /* @__PURE__ */ import_react2.default.createElement(import_material2.IconButton, { size: "small", onClick: () => copyToClipboard(keyId), title: "Copy Key ID" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(ExpiryChip, { expiresAt: key.expires_at })), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2" }, key.tpm_limit ?? "-", " / ", key.rpm_limit ?? "-")), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ import_react2.default.createElement(import_material2.Chip, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ import_react2.default.createElement(import_material2.Chip, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, { align: "right" }, /* @__PURE__ */ import_react2.default.createElement(import_material2.IconButton, { onClick: () => handleOpenEdit(key), title: "Edit key" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Edit, { fontSize: "small" })), /* @__PURE__ */ import_react2.default.createElement(
479
- import_material2.IconButton,
480
- {
481
- onClick: () => handleToggleBlock(key),
482
- disabled: isBlocking,
483
- color: key.blocked ? "warning" : "default",
484
- title: key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking"
485
- },
486
- isBlocking ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 18 }) : key.blocked ? /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.LockOpen, { fontSize: "small" }) : /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Lock, { fontSize: "small" })
487
- ), /* @__PURE__ */ import_react2.default.createElement(
488
- import_material2.IconButton,
489
- {
490
- color: "error",
491
- onClick: () => setDeleteConfirmId(keyId),
492
- title: "Revoke key"
493
- },
494
- /* @__PURE__ */ import_react2.default.createElement(import_icons_material2.Delete, null)
495
- )));
496
- }))))), /* @__PURE__ */ import_react2.default.createElement(import_material2.Dialog, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogTitle, null, "Edit Key"), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogContent, null, editingKey && /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ import_react2.default.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editingKey.key))), /* @__PURE__ */ import_react2.default.createElement(
497
- import_material2.TextField,
551
+ ))), /* @__PURE__ */ import_react2.default.createElement(import_TableContainer.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Table.default, null, /* @__PURE__ */ import_react2.default.createElement(import_TableHead.default, null, /* @__PURE__ */ import_react2.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, "Alias"), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, "Key ID"), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, "Created"), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, "Expires"), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, "Budget"), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, "TPM / RPM"), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, null, "Models"), /* @__PURE__ */ import_react2.default.createElement(import_TableCell.default, { align: "right" }, "Actions"))), /* @__PURE__ */ import_react2.default.createElement(import_TableBody.default, null, renderTableBody())))), /* @__PURE__ */ import_react2.default.createElement(import_Dialog.default, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_DialogTitle.default, null, "Edit Key"), /* @__PURE__ */ import_react2.default.createElement(import_DialogContent.default, null, editingKey && /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ import_react2.default.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editingKey.key))), /* @__PURE__ */ import_react2.default.createElement(
552
+ import_TextField.default,
498
553
  {
499
554
  label: "Alias",
500
555
  value: editForm.key_alias || "",
@@ -502,7 +557,7 @@ var init_KeysTable = __esm({
502
557
  fullWidth: true
503
558
  }
504
559
  ), models.length > 0 && /* @__PURE__ */ import_react2.default.createElement(
505
- import_material2.Autocomplete,
560
+ import_Autocomplete.default,
506
561
  {
507
562
  multiple: true,
508
563
  options: models,
@@ -511,10 +566,10 @@ var init_KeysTable = __esm({
511
566
  value: editSelectedModels,
512
567
  onChange: (_e, selected) => setEditForm({ ...editForm, models: selected.map((m) => m.model_name) }),
513
568
  renderOption: (props, m) => /* @__PURE__ */ import_react2.default.createElement("li", { ...props }, modelOption(m)),
514
- renderInput: (params) => /* @__PURE__ */ import_react2.default.createElement(import_material2.TextField, { ...params, label: "Models", fullWidth: true })
569
+ renderInput: (params) => /* @__PURE__ */ import_react2.default.createElement(import_TextField.default, { ...params, label: "Models", fullWidth: true })
515
570
  }
516
571
  ), /* @__PURE__ */ import_react2.default.createElement(
517
- import_material2.TextField,
572
+ import_TextField.default,
518
573
  {
519
574
  label: "Max Budget (USD)",
520
575
  type: "number",
@@ -523,7 +578,7 @@ var init_KeysTable = __esm({
523
578
  fullWidth: true
524
579
  }
525
580
  ), /* @__PURE__ */ import_react2.default.createElement(
526
- import_material2.TextField,
581
+ import_TextField.default,
527
582
  {
528
583
  label: "TPM Limit",
529
584
  type: "number",
@@ -533,7 +588,7 @@ var init_KeysTable = __esm({
533
588
  fullWidth: true
534
589
  }
535
590
  ), /* @__PURE__ */ import_react2.default.createElement(
536
- import_material2.TextField,
591
+ import_TextField.default,
537
592
  {
538
593
  label: "RPM Limit",
539
594
  type: "number",
@@ -542,8 +597,8 @@ var init_KeysTable = __esm({
542
597
  helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
543
598
  fullWidth: true
544
599
  }
545
- ), /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ import_react2.default.createElement(
546
- import_material2.Button,
600
+ ), /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ import_react2.default.createElement(
601
+ import_Button2.default,
547
602
  {
548
603
  size: "small",
549
604
  color: "warning",
@@ -551,8 +606,8 @@ var init_KeysTable = __esm({
551
606
  onClick: () => setResetSpendConfirm(true)
552
607
  },
553
608
  "Reset Spend to $0"
554
- ) : /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ import_react2.default.createElement(
555
- import_material2.Button,
609
+ ) : /* @__PURE__ */ import_react2.default.createElement(import_Box2.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ import_react2.default.createElement(
610
+ import_Button2.default,
556
611
  {
557
612
  size: "small",
558
613
  color: "warning",
@@ -560,25 +615,25 @@ var init_KeysTable = __esm({
560
615
  disabled: resetSpendSubmitting,
561
616
  onClick: handleResetSpend
562
617
  },
563
- resetSpendSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 16 }) : "Confirm"
564
- ), /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel"))))), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleCloseEdit }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleUpdate, variant: "contained", color: "primary", disabled: editSubmitting }, editSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 24 }) : "Save"))), /* @__PURE__ */ import_react2.default.createElement(import_material2.Dialog, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogTitle, null, "Revoke Key?"), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogContent, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(
565
- import_material2.Button,
618
+ resetSpendSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_CircularProgress.default, { size: 16 }) : "Confirm"
619
+ ), /* @__PURE__ */ import_react2.default.createElement(import_Button2.default, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel"))))), /* @__PURE__ */ import_react2.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Button2.default, { onClick: handleCloseEdit }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(import_Button2.default, { onClick: handleUpdate, variant: "contained", color: "primary", disabled: editSubmitting }, editSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_CircularProgress.default, { size: 24 }) : "Save"))), /* @__PURE__ */ import_react2.default.createElement(import_Dialog.default, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_DialogTitle.default, null, "Revoke Key?"), /* @__PURE__ */ import_react2.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ import_react2.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Button2.default, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(
620
+ import_Button2.default,
566
621
  {
567
622
  onClick: handleConfirmDelete,
568
623
  variant: "contained",
569
624
  color: "error",
570
625
  disabled: deleteSubmitting
571
626
  },
572
- deleteSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 20 }) : "Revoke"
573
- ))), /* @__PURE__ */ import_react2.default.createElement(import_material2.Dialog, { open: pruneConfirmCount !== null, onClose: () => setPruneConfirmCount(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogTitle, null, "Prune Expired Keys?"), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogContent, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, null, "This will permanently delete ", pruneConfirmCount, " expired key", pruneConfirmCount !== 1 ? "s" : "", " from LiteLLM. Any integrations using them will stop working immediately.")), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: () => setPruneConfirmCount(null), disabled: pruneSubmitting }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(
574
- import_material2.Button,
627
+ deleteSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_CircularProgress.default, { size: 20 }) : "Revoke"
628
+ ))), /* @__PURE__ */ import_react2.default.createElement(import_Dialog.default, { open: pruneConfirmCount !== null, onClose: () => setPruneConfirmCount(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_DialogTitle.default, null, "Prune Expired Keys?"), /* @__PURE__ */ import_react2.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Typography2.default, null, "This will permanently delete ", pruneConfirmCount, " expired key", pruneConfirmCount !== 1 ? "s" : "", " from LiteLLM. Any integrations using them will stop working immediately.")), /* @__PURE__ */ import_react2.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react2.default.createElement(import_Button2.default, { onClick: () => setPruneConfirmCount(null), disabled: pruneSubmitting }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(
629
+ import_Button2.default,
575
630
  {
576
631
  onClick: handlePruneExpired,
577
632
  variant: "contained",
578
633
  color: "error",
579
634
  disabled: pruneSubmitting
580
635
  },
581
- pruneSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 20 }) : "Prune"
636
+ pruneSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_CircularProgress.default, { size: 20 }) : "Prune"
582
637
  ))));
583
638
  };
584
639
  }
@@ -626,6 +681,21 @@ response = client.chat.completions.create(
626
681
  print(response.choices[0].message.content)`
627
682
  };
628
683
  }
684
+ function aliasHelperText(aliasError, aliasDuplicate) {
685
+ if (aliasError) return "Alias is required";
686
+ if (aliasDuplicate) return "This alias is already used by one of your keys \u2014 LiteLLM requires aliases to be unique across all keys";
687
+ return void 0;
688
+ }
689
+ function teamHelperText(teamError, teamRequired) {
690
+ if (teamError) return "Team is required";
691
+ if (teamRequired) return "Bind this key to a team for scoped access";
692
+ return "Optional: bind this key to a specific team for scoped access";
693
+ }
694
+ function budgetHelperText(budgetInvalid, budgetEstimate) {
695
+ if (budgetInvalid) return 'Enter a positive budget or tick "Unlimited"';
696
+ if (budgetEstimate !== null) return `\u2248 ${fmtInt(budgetEstimate)} tokens at the selected model's rate`;
697
+ return void 0;
698
+ }
629
699
  function fmtCost2(perToken) {
630
700
  if (!perToken) return null;
631
701
  const per1k = perToken * 1e3;
@@ -646,12 +716,28 @@ function formatContextWindow2(maxInput, maxOutput) {
646
716
  if (outPart) return `ctx ${outPart} out`;
647
717
  return null;
648
718
  }
649
- var import_react3, import_material3, import_icons_material3, generateDefaultAlias, emptyForm, GenerateKeyDialog, SnippetTabs;
719
+ var import_react3, import_Box3, import_Typography3, 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_Tabs, import_Tab, import_icons_material3, generateDefaultAlias, emptyForm, SnippetTabs, GenerateKeyDialog;
650
720
  var init_GenerateKeyDialog = __esm({
651
721
  "src/components/GenerateKeyDialog.tsx"() {
652
722
  "use strict";
653
723
  import_react3 = __toESM(require("react"));
654
- import_material3 = require("@mui/material");
724
+ import_Box3 = __toESM(require("@mui/material/Box"));
725
+ import_Typography3 = __toESM(require("@mui/material/Typography"));
726
+ import_Dialog2 = __toESM(require("@mui/material/Dialog"));
727
+ import_DialogTitle2 = __toESM(require("@mui/material/DialogTitle"));
728
+ import_DialogContent2 = __toESM(require("@mui/material/DialogContent"));
729
+ import_DialogActions2 = __toESM(require("@mui/material/DialogActions"));
730
+ import_TextField2 = __toESM(require("@mui/material/TextField"));
731
+ import_MenuItem = __toESM(require("@mui/material/MenuItem"));
732
+ import_Button3 = __toESM(require("@mui/material/Button"));
733
+ import_IconButton2 = __toESM(require("@mui/material/IconButton"));
734
+ import_CircularProgress2 = __toESM(require("@mui/material/CircularProgress"));
735
+ import_Autocomplete2 = __toESM(require("@mui/material/Autocomplete"));
736
+ import_Checkbox = __toESM(require("@mui/material/Checkbox"));
737
+ import_FormControlLabel = __toESM(require("@mui/material/FormControlLabel"));
738
+ import_Alert = __toESM(require("@mui/material/Alert"));
739
+ import_Tabs = __toESM(require("@mui/material/Tabs"));
740
+ import_Tab = __toESM(require("@mui/material/Tab"));
655
741
  import_icons_material3 = require("@mui/icons-material");
656
742
  init_format();
657
743
  generateDefaultAlias = (username) => {
@@ -669,6 +755,44 @@ var init_GenerateKeyDialog = __esm({
669
755
  team_id: void 0,
670
756
  key_type: "llm_api"
671
757
  });
758
+ SnippetTabs = ({ snippets, model, onCopy }) => {
759
+ const [tab, setTab] = (0, import_react3.useState)("curl");
760
+ const code = tab === "curl" ? snippets.curl : snippets.openai;
761
+ return /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Tabs.default, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 } }, /* @__PURE__ */ import_react3.default.createElement(import_Tab.default, { label: "curl", value: "curl" }), /* @__PURE__ */ import_react3.default.createElement(import_Tab.default, { label: "OpenAI SDK", value: "openai" })), /* @__PURE__ */ import_react3.default.createElement(
762
+ import_Box3.default,
763
+ {
764
+ position: "relative",
765
+ p: 1.5,
766
+ sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
767
+ },
768
+ /* @__PURE__ */ import_react3.default.createElement(
769
+ import_IconButton2.default,
770
+ {
771
+ size: "small",
772
+ onClick: () => onCopy(code),
773
+ title: "Copy snippet",
774
+ sx: { position: "absolute", top: 4, right: 4 }
775
+ },
776
+ /* @__PURE__ */ import_react3.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" })
777
+ ),
778
+ /* @__PURE__ */ import_react3.default.createElement(
779
+ import_Typography3.default,
780
+ {
781
+ component: "pre",
782
+ sx: {
783
+ fontFamily: "monospace",
784
+ fontSize: 12,
785
+ whiteSpace: "pre-wrap",
786
+ wordBreak: "break-all",
787
+ mb: 0,
788
+ pr: 4
789
+ }
790
+ },
791
+ code
792
+ ),
793
+ model && /* @__PURE__ */ import_react3.default.createElement(import_Typography3.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.")
794
+ ));
795
+ };
672
796
  GenerateKeyDialog = ({
673
797
  open,
674
798
  onClose,
@@ -762,10 +886,42 @@ var init_GenerateKeyDialog = __esm({
762
886
  const inCost = fmtCost2(m.input_cost_per_token);
763
887
  const outCost = fmtCost2(m.output_cost_per_token);
764
888
  const ctx = formatContextWindow2(m.max_input_tokens, m.max_output_tokens);
765
- return /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, 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_material3.Typography, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
889
+ 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"));
766
890
  };
767
- return /* @__PURE__ */ import_react3.default.createElement(import_material3.Dialog, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react3.default.createElement(import_material3.DialogTitle, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ import_react3.default.createElement(import_material3.DialogContent, null, newKeyValue ? /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, null, /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ import_react3.default.createElement(
768
- import_material3.Box,
891
+ const renderTeamField = () => {
892
+ if (teams.length > 0) {
893
+ return /* @__PURE__ */ import_react3.default.createElement(
894
+ import_Autocomplete2.default,
895
+ {
896
+ options: teams,
897
+ getOptionLabel: (t) => t.team_alias || t.team_id,
898
+ value: selectedTeam,
899
+ onChange: (_e, team) => {
900
+ const teamModels = team?.models;
901
+ const restrictedModels = teamModels && teamModels.length > 0 ? (formData.models || []).filter((m) => teamModels.includes(m)) : formData.models;
902
+ setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
903
+ },
904
+ renderInput: (params) => /* @__PURE__ */ import_react3.default.createElement(
905
+ import_TextField2.default,
906
+ {
907
+ ...params,
908
+ label: "Team",
909
+ error: teamError,
910
+ helperText: teamHelperText(teamError, teamRequired),
911
+ required: teamRequired,
912
+ fullWidth: true
913
+ }
914
+ )
915
+ }
916
+ );
917
+ }
918
+ if (teamRequired) {
919
+ return /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.");
920
+ }
921
+ return null;
922
+ };
923
+ return /* @__PURE__ */ import_react3.default.createElement(import_Dialog2.default, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react3.default.createElement(import_DialogTitle2.default, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ import_react3.default.createElement(import_DialogContent2.default, null, newKeyValue ? /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, null, /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ import_react3.default.createElement(
924
+ import_Box3.default,
769
925
  {
770
926
  display: "flex",
771
927
  alignItems: "center",
@@ -780,7 +936,7 @@ var init_GenerateKeyDialog = __esm({
780
936
  }
781
937
  },
782
938
  /* @__PURE__ */ import_react3.default.createElement(
783
- import_material3.Typography,
939
+ import_Typography3.default,
784
940
  {
785
941
  component: "code",
786
942
  color: "text.primary",
@@ -788,9 +944,9 @@ var init_GenerateKeyDialog = __esm({
788
944
  },
789
945
  newKeyValue
790
946
  ),
791
- /* @__PURE__ */ import_react3.default.createElement(import_material3.IconButton, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material3.ContentCopy, null))
792
- ), newKeySnippets && /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, { mt: 3 }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material3.Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ import_react3.default.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, generateError && /* @__PURE__ */ import_react3.default.createElement(import_material3.Alert, { severity: "error", onClose: () => setGenerateError(null) }, generateError), /* @__PURE__ */ import_react3.default.createElement(
793
- import_material3.TextField,
947
+ /* @__PURE__ */ import_react3.default.createElement(import_IconButton2.default, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material3.ContentCopy, null))
948
+ ), newKeySnippets && /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { mt: 3 }, /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material3.Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react3.default.createElement(import_Typography3.default, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ import_react3.default.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ import_react3.default.createElement(import_Box3.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, generateError && /* @__PURE__ */ import_react3.default.createElement(import_Alert.default, { severity: "error", onClose: () => setGenerateError(null) }, generateError), /* @__PURE__ */ import_react3.default.createElement(
949
+ import_TextField2.default,
794
950
  {
795
951
  label: "Alias",
796
952
  value: formData.alias || "",
@@ -800,12 +956,12 @@ var init_GenerateKeyDialog = __esm({
800
956
  },
801
957
  error: aliasError,
802
958
  color: aliasDuplicate ? "warning" : void 0,
803
- helperText: aliasError ? "Alias is required" : aliasDuplicate ? "This alias is already used by one of your keys \u2014 LiteLLM requires aliases to be unique across all keys" : void 0,
959
+ helperText: aliasHelperText(aliasError, aliasDuplicate),
804
960
  required: true,
805
961
  fullWidth: true
806
962
  }
807
963
  ), /* @__PURE__ */ import_react3.default.createElement(
808
- import_material3.TextField,
964
+ import_TextField2.default,
809
965
  {
810
966
  select: true,
811
967
  label: "Duration",
@@ -813,36 +969,13 @@ var init_GenerateKeyDialog = __esm({
813
969
  onChange: (e) => setFormData({ ...formData, duration: e.target.value }),
814
970
  fullWidth: true
815
971
  },
816
- /* @__PURE__ */ import_react3.default.createElement(import_material3.MenuItem, { value: "1d" }, "1 Day"),
817
- /* @__PURE__ */ import_react3.default.createElement(import_material3.MenuItem, { value: "7d" }, "7 Days"),
818
- /* @__PURE__ */ import_react3.default.createElement(import_material3.MenuItem, { value: "30d" }, "30 Days"),
819
- /* @__PURE__ */ import_react3.default.createElement(import_material3.MenuItem, { value: "90d" }, "90 Days"),
820
- /* @__PURE__ */ import_react3.default.createElement(import_material3.MenuItem, { value: "1y" }, "1 Year")
821
- ), teams.length > 0 ? /* @__PURE__ */ import_react3.default.createElement(
822
- import_material3.Autocomplete,
823
- {
824
- options: teams,
825
- getOptionLabel: (t) => t.team_alias || t.team_id,
826
- value: selectedTeam,
827
- onChange: (_e, team) => {
828
- const teamModels = team?.models;
829
- const restrictedModels = teamModels && teamModels.length > 0 ? (formData.models || []).filter((m) => teamModels.includes(m)) : formData.models;
830
- setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
831
- },
832
- renderInput: (params) => /* @__PURE__ */ import_react3.default.createElement(
833
- import_material3.TextField,
834
- {
835
- ...params,
836
- label: "Team",
837
- error: teamError,
838
- helperText: teamError ? "Team is required" : teamRequired ? "Bind this key to a team for scoped access" : "Optional: bind this key to a specific team for scoped access",
839
- required: teamRequired,
840
- fullWidth: true
841
- }
842
- )
843
- }
844
- ) : teamRequired ? /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.") : null, availableModels.length > 0 && /* @__PURE__ */ import_react3.default.createElement(
845
- import_material3.Autocomplete,
972
+ /* @__PURE__ */ import_react3.default.createElement(import_MenuItem.default, { value: "1d" }, "1 Day"),
973
+ /* @__PURE__ */ import_react3.default.createElement(import_MenuItem.default, { value: "7d" }, "7 Days"),
974
+ /* @__PURE__ */ import_react3.default.createElement(import_MenuItem.default, { value: "30d" }, "30 Days"),
975
+ /* @__PURE__ */ import_react3.default.createElement(import_MenuItem.default, { value: "90d" }, "90 Days"),
976
+ /* @__PURE__ */ import_react3.default.createElement(import_MenuItem.default, { value: "1y" }, "1 Year")
977
+ ), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ import_react3.default.createElement(
978
+ import_Autocomplete2.default,
846
979
  {
847
980
  multiple: true,
848
981
  options: availableModels,
@@ -852,7 +985,7 @@ var init_GenerateKeyDialog = __esm({
852
985
  onChange: (_e, selected) => setFormData({ ...formData, models: selected.map((m) => m.model_name) }),
853
986
  renderOption: (props, m) => /* @__PURE__ */ import_react3.default.createElement("li", { ...props }, modelOption(m)),
854
987
  renderInput: (params) => /* @__PURE__ */ import_react3.default.createElement(
855
- import_material3.TextField,
988
+ import_TextField2.default,
856
989
  {
857
990
  ...params,
858
991
  label: "Models",
@@ -862,10 +995,10 @@ var init_GenerateKeyDialog = __esm({
862
995
  )
863
996
  }
864
997
  ), allowUnlimitedBudget && /* @__PURE__ */ import_react3.default.createElement(
865
- import_material3.FormControlLabel,
998
+ import_FormControlLabel.default,
866
999
  {
867
1000
  control: /* @__PURE__ */ import_react3.default.createElement(
868
- import_material3.Checkbox,
1001
+ import_Checkbox.default,
869
1002
  {
870
1003
  checked: unlimitedBudget,
871
1004
  onChange: (e) => {
@@ -881,20 +1014,20 @@ var init_GenerateKeyDialog = __esm({
881
1014
  label: "Unlimited budget"
882
1015
  }
883
1016
  ), /* @__PURE__ */ import_react3.default.createElement(
884
- import_material3.TextField,
1017
+ import_TextField2.default,
885
1018
  {
886
1019
  label: "Max Budget (USD)",
887
1020
  type: "number",
888
1021
  value: unlimitedBudget ? "" : formData.max_budget ?? "",
889
1022
  onChange: (e) => setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
890
1023
  error: budgetInvalid,
891
- helperText: budgetInvalid ? 'Enter a positive budget or tick "Unlimited"' : budgetEstimate !== null ? `\u2248 ${fmtInt(budgetEstimate)} tokens at the selected model's rate` : void 0,
1024
+ helperText: budgetHelperText(budgetInvalid, budgetEstimate),
892
1025
  disabled: unlimitedBudget,
893
1026
  required: true,
894
1027
  fullWidth: true
895
1028
  }
896
1029
  ), /* @__PURE__ */ import_react3.default.createElement(
897
- import_material3.TextField,
1030
+ import_TextField2.default,
898
1031
  {
899
1032
  label: "TPM Limit",
900
1033
  type: "number",
@@ -904,7 +1037,7 @@ var init_GenerateKeyDialog = __esm({
904
1037
  fullWidth: true
905
1038
  }
906
1039
  ), /* @__PURE__ */ import_react3.default.createElement(
907
- import_material3.TextField,
1040
+ import_TextField2.default,
908
1041
  {
909
1042
  label: "RPM Limit",
910
1043
  type: "number",
@@ -913,55 +1046,17 @@ var init_GenerateKeyDialog = __esm({
913
1046
  helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
914
1047
  fullWidth: true
915
1048
  }
916
- ))), /* @__PURE__ */ import_react3.default.createElement(import_material3.DialogActions, null, newKeyValue ? /* @__PURE__ */ import_react3.default.createElement(import_material3.Button, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(import_material3.Button, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ import_react3.default.createElement(
917
- import_material3.Button,
1049
+ ))), /* @__PURE__ */ import_react3.default.createElement(import_DialogActions2.default, null, newKeyValue ? /* @__PURE__ */ import_react3.default.createElement(import_Button3.default, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(import_Button3.default, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ import_react3.default.createElement(
1050
+ import_Button3.default,
918
1051
  {
919
1052
  onClick: handleGenerate,
920
1053
  variant: "contained",
921
1054
  color: "primary",
922
1055
  disabled: !canGenerate
923
1056
  },
924
- submitting ? /* @__PURE__ */ import_react3.default.createElement(import_material3.CircularProgress, { size: 24 }) : "Generate"
1057
+ submitting ? /* @__PURE__ */ import_react3.default.createElement(import_CircularProgress2.default, { size: 24 }) : "Generate"
925
1058
  ))));
926
1059
  };
927
- SnippetTabs = ({ snippets, model, onCopy }) => {
928
- const [tab, setTab] = (0, import_react3.useState)("curl");
929
- const code = tab === "curl" ? snippets.curl : snippets.openai;
930
- return /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, null, /* @__PURE__ */ import_react3.default.createElement(import_material3.Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 } }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Tab, { label: "curl", value: "curl" }), /* @__PURE__ */ import_react3.default.createElement(import_material3.Tab, { label: "OpenAI SDK", value: "openai" })), /* @__PURE__ */ import_react3.default.createElement(
931
- import_material3.Box,
932
- {
933
- position: "relative",
934
- p: 1.5,
935
- sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
936
- },
937
- /* @__PURE__ */ import_react3.default.createElement(
938
- import_material3.IconButton,
939
- {
940
- size: "small",
941
- onClick: () => onCopy(code),
942
- title: "Copy snippet",
943
- sx: { position: "absolute", top: 4, right: 4 }
944
- },
945
- /* @__PURE__ */ import_react3.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" })
946
- ),
947
- /* @__PURE__ */ import_react3.default.createElement(
948
- import_material3.Typography,
949
- {
950
- component: "pre",
951
- sx: {
952
- fontFamily: "monospace",
953
- fontSize: 12,
954
- whiteSpace: "pre-wrap",
955
- wordBreak: "break-all",
956
- mb: 0,
957
- pr: 4
958
- }
959
- },
960
- code
961
- ),
962
- model && /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "caption", color: "text.secondary", display: "block", mt: 1 }, "Using model \u201C", model, "\u201D \u2014 swap it for any model you have access to.")
963
- ));
964
- };
965
1060
  }
966
1061
  });
967
1062
 
@@ -971,12 +1066,31 @@ function modelColor(model) {
971
1066
  for (let i = 0; i < model.length; i++) h = (h << 5) + h + model.charCodeAt(i) >>> 0;
972
1067
  return MODEL_COLORS[h % MODEL_COLORS.length];
973
1068
  }
974
- var import_react4, import_material4, import_recharts, TOP_N_MODELS, PERIOD_LS_KEY, MODEL_COLORS, fmtPct, KpiCard, ChartSkeleton, EmptyChart, UsageStats;
1069
+ var import_react4, import_Paper3, import_Box4, import_Typography4, import_FormControl, import_InputLabel, import_Select, import_MenuItem2, import_Grid, import_Tabs2, import_Tab2, import_Table2, import_TableBody2, import_TableCell2, import_TableContainer2, import_TableHead2, import_TableRow2, import_Chip3, import_LinearProgress3, import_Skeleton, import_styles, import_recharts, TOP_N_MODELS, PERIOD_LS_KEY, MODEL_COLORS, fmtPct, KpiCard, ChartSkeleton, EmptyChart, ChartOrFallback, UsageStats;
975
1070
  var init_UsageStats = __esm({
976
1071
  "src/components/UsageStats.tsx"() {
977
1072
  "use strict";
978
1073
  import_react4 = __toESM(require("react"));
979
- import_material4 = require("@mui/material");
1074
+ import_Paper3 = __toESM(require("@mui/material/Paper"));
1075
+ import_Box4 = __toESM(require("@mui/material/Box"));
1076
+ import_Typography4 = __toESM(require("@mui/material/Typography"));
1077
+ import_FormControl = __toESM(require("@mui/material/FormControl"));
1078
+ import_InputLabel = __toESM(require("@mui/material/InputLabel"));
1079
+ import_Select = __toESM(require("@mui/material/Select"));
1080
+ import_MenuItem2 = __toESM(require("@mui/material/MenuItem"));
1081
+ import_Grid = __toESM(require("@mui/material/Grid"));
1082
+ import_Tabs2 = __toESM(require("@mui/material/Tabs"));
1083
+ import_Tab2 = __toESM(require("@mui/material/Tab"));
1084
+ import_Table2 = __toESM(require("@mui/material/Table"));
1085
+ import_TableBody2 = __toESM(require("@mui/material/TableBody"));
1086
+ import_TableCell2 = __toESM(require("@mui/material/TableCell"));
1087
+ import_TableContainer2 = __toESM(require("@mui/material/TableContainer"));
1088
+ import_TableHead2 = __toESM(require("@mui/material/TableHead"));
1089
+ import_TableRow2 = __toESM(require("@mui/material/TableRow"));
1090
+ import_Chip3 = __toESM(require("@mui/material/Chip"));
1091
+ import_LinearProgress3 = __toESM(require("@mui/material/LinearProgress"));
1092
+ import_Skeleton = __toESM(require("@mui/material/Skeleton"));
1093
+ import_styles = require("@mui/material/styles");
980
1094
  import_recharts = require("recharts");
981
1095
  init_format();
982
1096
  TOP_N_MODELS = 6;
@@ -994,12 +1108,22 @@ var init_UsageStats = __esm({
994
1108
  "#d0ed57"
995
1109
  ];
996
1110
  fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
997
- KpiCard = ({ label, value, hint }) => /* @__PURE__ */ import_react4.default.createElement(import_material4.Paper, { variant: "outlined", sx: { p: 2, height: "100%" } }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "caption", color: "text.secondary" }, label), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "h5", sx: { mt: 0.5 } }, value), hint ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "caption", color: "text.secondary" }, hint) : null);
998
- ChartSkeleton = ({ height = 260 }) => /* @__PURE__ */ import_react4.default.createElement(import_material4.Skeleton, { variant: "rectangular", height, sx: { borderRadius: 1 } });
1111
+ KpiCard = ({ label, value, hint }) => /* @__PURE__ */ import_react4.default.createElement(import_Paper3.default, { variant: "outlined", sx: { p: 2, height: "100%" } }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary" }, label), /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "h5", sx: { mt: 0.5 } }, value), hint ? /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary" }, hint) : null);
1112
+ ChartSkeleton = ({ height = 260 }) => /* @__PURE__ */ import_react4.default.createElement(import_Skeleton.default, { variant: "rectangular", height, sx: { borderRadius: 1 } });
999
1113
  EmptyChart = ({
1000
1114
  height = 260,
1001
1115
  message = "No data for this period"
1002
- }) => /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height, display: "flex", alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { color: "text.secondary", variant: "body2" }, message));
1116
+ }) => /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height, display: "flex", alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { color: "text.secondary", variant: "body2" }, message));
1117
+ ChartOrFallback = ({
1118
+ loading,
1119
+ empty,
1120
+ height,
1121
+ children
1122
+ }) => {
1123
+ if (loading) return /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height });
1124
+ if (empty) return /* @__PURE__ */ import_react4.default.createElement(EmptyChart, { height });
1125
+ return /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, children);
1126
+ };
1003
1127
  UsageStats = ({
1004
1128
  usage,
1005
1129
  models,
@@ -1008,7 +1132,7 @@ var init_UsageStats = __esm({
1008
1132
  loading,
1009
1133
  userInfo
1010
1134
  }) => {
1011
- const theme = (0, import_material4.useTheme)();
1135
+ const theme = (0, import_styles.useTheme)();
1012
1136
  const [selectedModel, setSelectedModel] = (0, import_react4.useState)("all");
1013
1137
  const [tab, setTab] = (0, import_react4.useState)("costs");
1014
1138
  const gridStroke = theme.palette.divider;
@@ -1123,34 +1247,66 @@ var init_UsageStats = __esm({
1123
1247
  const overallSuccessRate = totalRequests > 0 ? (usage?.successful_requests ?? 0) / totalRequests : 0;
1124
1248
  const maxBudget = userInfo?.max_budget ?? 0;
1125
1249
  const totalCumSpend = cumulativeData[cumulativeData.length - 1]?.cumulative ?? 0;
1126
- return /* @__PURE__ */ import_react4.default.createElement(import_material4.Paper, { sx: { p: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, flexWrap: "wrap", gap: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "h6" }, "Usage Analytics"), /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.FormControl, { size: "small", sx: { minWidth: 140 } }, /* @__PURE__ */ import_react4.default.createElement(import_material4.InputLabel, null, "Period"), /* @__PURE__ */ import_react4.default.createElement(
1127
- import_material4.Select,
1250
+ const renderKeyRows = () => {
1251
+ if (loading) {
1252
+ return /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { colSpan: 7 }, /* @__PURE__ */ import_react4.default.createElement(import_LinearProgress3.default, null)));
1253
+ }
1254
+ if (keyRows.length === 0) {
1255
+ return /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { colSpan: 7, align: "center" }, "No key activity"));
1256
+ }
1257
+ return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, { key: r.keyHash }, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "body2" }, r.keyAlias), r.teamId ? /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_Chip3.default, { key: m, label: m, size: "small", variant: "outlined" })))), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.totalTokens)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, r.apiRequests > 0 ? /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react4.default.createElement(
1258
+ import_LinearProgress3.default,
1259
+ {
1260
+ variant: "determinate",
1261
+ value: r.successRate * 100,
1262
+ sx: { flex: 1, height: 6, borderRadius: 3 }
1263
+ }
1264
+ ), /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")));
1265
+ };
1266
+ const renderModelRows = () => {
1267
+ if (loading) {
1268
+ return /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { colSpan: 8 }, /* @__PURE__ */ import_react4.default.createElement(import_LinearProgress3.default, null)));
1269
+ }
1270
+ if (modelRows.length === 0) {
1271
+ return /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { colSpan: 8, align: "center" }, "No model activity"));
1272
+ }
1273
+ return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, { key: r.model }, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 0.75 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { sx: { width: 10, height: 10, borderRadius: "50%", bgcolor: modelColor(r.model), flexShrink: 0 } }), r.model)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.promptTokens)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, fmtInt(r.completionTokens)), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, r.apiRequests > 0 ? /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react4.default.createElement(
1274
+ import_LinearProgress3.default,
1275
+ {
1276
+ variant: "determinate",
1277
+ value: r.successRate * 100,
1278
+ sx: { flex: 1, height: 6, borderRadius: 3 }
1279
+ }
1280
+ ), /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")));
1281
+ };
1282
+ return /* @__PURE__ */ import_react4.default.createElement(import_Paper3.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, flexWrap: "wrap", gap: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "h6" }, "Usage Analytics"), /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap" }, /* @__PURE__ */ import_react4.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 140 } }, /* @__PURE__ */ import_react4.default.createElement(import_InputLabel.default, null, "Period"), /* @__PURE__ */ import_react4.default.createElement(
1283
+ import_Select.default,
1128
1284
  {
1129
1285
  value: selectedPreset,
1130
1286
  label: "Period",
1131
1287
  onChange: (e) => handlePresetChange(e.target.value)
1132
1288
  },
1133
- /* @__PURE__ */ import_react4.default.createElement(import_material4.MenuItem, { value: "today" }, "Today"),
1134
- /* @__PURE__ */ import_react4.default.createElement(import_material4.MenuItem, { value: "24h" }, "Last 24h"),
1135
- /* @__PURE__ */ import_react4.default.createElement(import_material4.MenuItem, { value: "7d" }, "Last 7 days"),
1136
- /* @__PURE__ */ import_react4.default.createElement(import_material4.MenuItem, { value: "30d" }, "Last 30 days")
1137
- )), tab === "models" && /* @__PURE__ */ import_react4.default.createElement(import_material4.FormControl, { size: "small", sx: { minWidth: 180 } }, /* @__PURE__ */ import_react4.default.createElement(import_material4.InputLabel, null, "Model"), /* @__PURE__ */ import_react4.default.createElement(
1138
- import_material4.Select,
1289
+ /* @__PURE__ */ import_react4.default.createElement(import_MenuItem2.default, { value: "today" }, "Today"),
1290
+ /* @__PURE__ */ import_react4.default.createElement(import_MenuItem2.default, { value: "24h" }, "Last 24h"),
1291
+ /* @__PURE__ */ import_react4.default.createElement(import_MenuItem2.default, { value: "7d" }, "Last 7 days"),
1292
+ /* @__PURE__ */ import_react4.default.createElement(import_MenuItem2.default, { value: "30d" }, "Last 30 days")
1293
+ )), tab === "models" && /* @__PURE__ */ import_react4.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 180 } }, /* @__PURE__ */ import_react4.default.createElement(import_InputLabel.default, null, "Model"), /* @__PURE__ */ import_react4.default.createElement(
1294
+ import_Select.default,
1139
1295
  {
1140
1296
  value: selectedModel,
1141
1297
  label: "Model",
1142
1298
  onChange: (e) => setSelectedModel(e.target.value)
1143
1299
  },
1144
- /* @__PURE__ */ import_react4.default.createElement(import_material4.MenuItem, { value: "all" }, "All Models"),
1145
- models.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_material4.MenuItem, { key: m.model_name, value: m.model_name }, m.model_name))
1146
- )))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { container: true, spacing: 2, sx: { mb: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(KpiCard, { label: "Total Spend", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(KpiCard, { label: "Total Requests", value: fmtInt(totalRequests), hint: `${fmtInt(usage?.failed_requests ?? 0)} failed` })), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(KpiCard, { label: "Success Rate", value: totalRequests > 0 ? fmtPct(overallSuccessRate) : "\u2014" })), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(
1300
+ /* @__PURE__ */ import_react4.default.createElement(import_MenuItem2.default, { value: "all" }, "All Models"),
1301
+ models.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_MenuItem2.default, { key: m.model_name, value: m.model_name }, m.model_name))
1302
+ )))), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { container: true, spacing: 2, sx: { mb: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(KpiCard, { label: "Total Spend", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(KpiCard, { label: "Total Requests", value: fmtInt(totalRequests), hint: `${fmtInt(usage?.failed_requests ?? 0)} failed` })), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(KpiCard, { label: "Success Rate", value: totalRequests > 0 ? fmtPct(overallSuccessRate) : "\u2014" })), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 6, sm: 3 }, loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 80 }) : /* @__PURE__ */ import_react4.default.createElement(
1147
1303
  KpiCard,
1148
1304
  {
1149
1305
  label: "Total Tokens",
1150
1306
  value: fmtInt(usage?.total_tokens ?? 0),
1151
1307
  hint: `${fmtInt(usage?.prompt_tokens ?? 0)} in \xB7 ${fmtInt(usage?.completion_tokens ?? 0)} out`
1152
1308
  }
1153
- ))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Tab, { value: "costs", label: "Costs" }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Tab, { value: "models", label: "Model Activity" }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Tab, { value: "keys", label: "Key Activity" })), tab === "costs" && /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { container: true, spacing: 3 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend by Model"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : modelSpendByDate.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(EmptyChart, null) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.AreaChart, { data: modelSpendByDate }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, void 0] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), topSpendModels.map((m) => /* @__PURE__ */ import_react4.default.createElement(
1309
+ ))), /* @__PURE__ */ import_react4.default.createElement(import_Tabs2.default, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_Tab2.default, { value: "costs", label: "Costs" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab2.default, { value: "models", label: "Model Activity" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab2.default, { value: "keys", label: "Key Activity" })), tab === "costs" && /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { container: true, spacing: 3 }, /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend by Model"), /* @__PURE__ */ import_react4.default.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.AreaChart, { data: modelSpendByDate }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, void 0] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), topSpendModels.map((m) => /* @__PURE__ */ import_react4.default.createElement(
1154
1310
  import_recharts.Area,
1155
1311
  {
1156
1312
  key: m,
@@ -1161,38 +1317,45 @@ var init_UsageStats = __esm({
1161
1317
  fill: modelColor(m),
1162
1318
  fillOpacity: 0.6
1163
1319
  }
1164
- )))))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Token Usage"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : dailyData.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(EmptyChart, null) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.AreaChart, { data: dailyData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle, tickFormatter: fmtInt }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [fmtInt(v), void 0] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "promptTokens", name: "Input (prompt)", stackId: "tok", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.5 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "completionTokens", name: "Output (completion)", stackId: "tok", stroke: "#82ca9d", fill: "#82ca9d", fillOpacity: 0.5 }))))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Requests"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : dailyData.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(EmptyChart, null) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: dailyData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "successfulRequests", name: "Successful", fill: "#82ca9d", stackId: "r" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "failedRequests", name: "Failed", fill: "#e57373", stackId: "r" }))))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Success Rate"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : successRateData.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(EmptyChart, null) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.LineChart, { data: successRateData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { domain: [0, 100], tick: tickStyle, tickFormatter: (v) => `${v}%` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [`${v}%`, "Success rate"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.ReferenceLine, { y: 100, stroke: "#82ca9d", strokeDasharray: "4 2" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Line, { type: "monotone", dataKey: "successRate", name: "Success rate", stroke: "#8884d8", dot: { r: 3 } }))))), (maxBudget > 0 || cumulativeData.length > 0) && /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Cumulative Spend", maxBudget > 0 ? ` vs Budget (${fmtUsd(maxBudget)})` : "", maxBudget > 0 && /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 1 } }, fmtUsd(totalCumSpend), " used \xB7 ", fmtUsd(Math.max(0, maxBudget - totalCumSpend)), " remaining")), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, { height: 180 }) : cumulativeData.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(EmptyChart, { height: 180 }) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 180 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.AreaChart, { data: cumulativeData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Cumulative spend"] }), maxBudget > 0 && /* @__PURE__ */ import_react4.default.createElement(import_recharts.ReferenceLine, { y: maxBudget, stroke: "#e57373", strokeDasharray: "6 3", label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 11 } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "cumulative", name: "Cumulative spend", stroke: "#ffc658", fill: "#ffc658", fillOpacity: 0.3 })))))), tab === "models" && /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { container: true, spacing: 3 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Model"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : topModelSpendBars.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(EmptyChart, null) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: Math.max(200, topModelSpendBars.length * 36) }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", tick: tickStyleSmall, width: 200 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", radius: [0, 4, 4, 0] }, topModelSpendBars.map((r) => /* @__PURE__ */ import_react4.default.createElement("rect", { key: r.model, fill: modelColor(r.model) }))))))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Tokens by Model"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : modelRows.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(EmptyChart, null) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: modelRows }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "model", tick: tickStyleTiny, interval: 0, angle: -15, textAnchor: "end", height: 60 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "promptTokens", name: "Prompt", fill: "#8884d8", stackId: "t" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "completionTokens", name: "Completion", fill: "#82ca9d", stackId: "t" }))))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableContainer, { component: import_material4.Paper, variant: "outlined" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Table, { size: "small" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableHead, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, "Model"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Spend"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Requests"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Success"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Failed"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Prompt"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Completion"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableBody, null, loading ? /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { colSpan: 8 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.LinearProgress, null))) : modelRows.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { colSpan: 8, align: "center" }, "No model activity")) : [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, { key: r.model }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", alignItems: "center", gap: 0.75 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { sx: { width: 10, height: 10, borderRadius: "50%", bgcolor: modelColor(r.model), flexShrink: 0 } }), r.model)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.promptTokens)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.completionTokens)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, r.apiRequests > 0 ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react4.default.createElement(
1165
- import_material4.LinearProgress,
1166
- {
1167
- variant: "determinate",
1168
- value: r.successRate * 100,
1169
- sx: { flex: 1, height: 6, borderRadius: 3 }
1170
- }
1171
- ), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")))))))), tab === "keys" && /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { container: true, spacing: 3 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Key"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: Math.max(160, topKeySpendBars.length * 36) }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: topKeySpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "keyAlias", tick: tickStyleSmall, width: 160 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", fill: "#8884d8", radius: [0, 4, 4, 0] }))))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Grid, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableContainer, { component: import_material4.Paper, variant: "outlined" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Table, { size: "small" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableHead, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, "Key"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, "Models"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Spend"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Requests"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Tokens"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, "Failed"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableBody, null, loading ? /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { colSpan: 7 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.LinearProgress, null))) : keyRows.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { colSpan: 7, align: "center" }, "No key activity")) : [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, { key: r.keyHash }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "body2" }, r.keyAlias), r.teamId ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_material4.Chip, { key: m, label: m, size: "small", variant: "outlined" })))), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.totalTokens)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, r.apiRequests > 0 ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react4.default.createElement(
1172
- import_material4.LinearProgress,
1173
- {
1174
- variant: "determinate",
1175
- value: r.successRate * 100,
1176
- sx: { flex: 1, height: 6, borderRadius: 3 }
1177
- }
1178
- ), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")))))))));
1320
+ ))))))), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Token Usage"), /* @__PURE__ */ import_react4.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.AreaChart, { data: dailyData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle, tickFormatter: fmtInt }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [fmtInt(v), void 0] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "promptTokens", name: "Input (prompt)", stackId: "tok", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.5 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "completionTokens", name: "Output (completion)", stackId: "tok", stroke: "#82ca9d", fill: "#82ca9d", fillOpacity: 0.5 })))))), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Requests"), /* @__PURE__ */ import_react4.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: dailyData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "successfulRequests", name: "Successful", fill: "#82ca9d", stackId: "r" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "failedRequests", name: "Failed", fill: "#e57373", stackId: "r" })))))), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Success Rate"), /* @__PURE__ */ import_react4.default.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.LineChart, { data: successRateData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { domain: [0, 100], tick: tickStyle, tickFormatter: (v) => `${v}%` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [`${v}%`, "Success rate"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.ReferenceLine, { y: 100, stroke: "#82ca9d", strokeDasharray: "4 2" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Line, { type: "monotone", dataKey: "successRate", name: "Success rate", stroke: "#8884d8", dot: { r: 3 } })))))), (maxBudget > 0 || cumulativeData.length > 0) && /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Cumulative Spend", maxBudget > 0 ? ` vs Budget (${fmtUsd(maxBudget)})` : "", maxBudget > 0 && /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 1 } }, fmtUsd(totalCumSpend), " used \xB7 ", fmtUsd(Math.max(0, maxBudget - totalCumSpend)), " remaining")), /* @__PURE__ */ import_react4.default.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0, height: 180 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: 180 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.AreaChart, { data: cumulativeData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Cumulative spend"] }), maxBudget > 0 && /* @__PURE__ */ import_react4.default.createElement(import_recharts.ReferenceLine, { y: maxBudget, stroke: "#e57373", strokeDasharray: "6 3", label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 11 } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "cumulative", name: "Cumulative spend", stroke: "#ffc658", fill: "#ffc658", fillOpacity: 0.3 }))))))), tab === "models" && /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { container: true, spacing: 3 }, /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Model"), /* @__PURE__ */ import_react4.default.createElement(ChartOrFallback, { loading, empty: topModelSpendBars.length === 0 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: Math.max(200, topModelSpendBars.length * 36) }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", tick: tickStyleSmall, width: 200 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", radius: [0, 4, 4, 0] }, topModelSpendBars.map((r) => /* @__PURE__ */ import_react4.default.createElement("rect", { key: r.model, fill: modelColor(r.model) })))))))), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Tokens by Model"), /* @__PURE__ */ import_react4.default.createElement(ChartOrFallback, { loading, empty: modelRows.length === 0 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: 260 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: modelRows }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: "model", tick: tickStyleTiny, interval: 0, angle: -15, textAnchor: "end", height: 60 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: tickStyle }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, null), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "promptTokens", name: "Prompt", fill: "#8884d8", stackId: "t" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "completionTokens", name: "Completion", fill: "#82ca9d", stackId: "t" })))))), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_TableContainer2.default, { component: import_Paper3.default, variant: "outlined" }, /* @__PURE__ */ import_react4.default.createElement(import_Table2.default, { size: "small" }, /* @__PURE__ */ import_react4.default.createElement(import_TableHead2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, "Model"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Success"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Prompt"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Completion"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ import_react4.default.createElement(import_TableBody2.default, null, renderModelRows()))))), tab === "keys" && /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { container: true, spacing: 3 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Key"), loading ? /* @__PURE__ */ import_react4.default.createElement(ChartSkeleton, null) : /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { height: Math.max(160, topKeySpendBars.length * 36) }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: topKeySpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "keyAlias", tick: tickStyleSmall, width: 160 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", fill: "#8884d8", radius: [0, 4, 4, 0] }))))), /* @__PURE__ */ import_react4.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react4.default.createElement(import_TableContainer2.default, { component: import_Paper3.default, variant: "outlined" }, /* @__PURE__ */ import_react4.default.createElement(import_Table2.default, { size: "small" }, /* @__PURE__ */ import_react4.default.createElement(import_TableHead2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, "Key"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, null, "Models"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Tokens"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell2.default, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ import_react4.default.createElement(import_TableBody2.default, null, renderKeyRows()))))));
1179
1321
  };
1180
1322
  }
1181
1323
  });
1182
1324
 
1183
1325
  // src/components/TeamUsage.tsx
1184
- var import_react5, import_material5, import_icons_material4, import_recharts2, Stat, TeamCard, TeamUsage;
1326
+ function budgetBarColor(isOver, isNear) {
1327
+ if (isOver) return "error";
1328
+ if (isNear) return "warning";
1329
+ return "primary";
1330
+ }
1331
+ var import_react5, import_Paper4, import_Box5, import_Typography5, import_LinearProgress4, import_Chip4, import_Divider, import_Stack2, import_Table3, import_TableBody3, import_TableCell3, import_TableHead3, import_TableRow3, import_TableContainer3, import_Collapse, import_IconButton3, import_CircularProgress3, import_styles2, import_icons_material4, import_recharts2, Stat, TeamCard, TeamUsage;
1185
1332
  var init_TeamUsage = __esm({
1186
1333
  "src/components/TeamUsage.tsx"() {
1187
1334
  "use strict";
1188
1335
  import_react5 = __toESM(require("react"));
1189
- import_material5 = require("@mui/material");
1336
+ import_Paper4 = __toESM(require("@mui/material/Paper"));
1337
+ import_Box5 = __toESM(require("@mui/material/Box"));
1338
+ import_Typography5 = __toESM(require("@mui/material/Typography"));
1339
+ import_LinearProgress4 = __toESM(require("@mui/material/LinearProgress"));
1340
+ import_Chip4 = __toESM(require("@mui/material/Chip"));
1341
+ import_Divider = __toESM(require("@mui/material/Divider"));
1342
+ import_Stack2 = __toESM(require("@mui/material/Stack"));
1343
+ import_Table3 = __toESM(require("@mui/material/Table"));
1344
+ import_TableBody3 = __toESM(require("@mui/material/TableBody"));
1345
+ import_TableCell3 = __toESM(require("@mui/material/TableCell"));
1346
+ import_TableHead3 = __toESM(require("@mui/material/TableHead"));
1347
+ import_TableRow3 = __toESM(require("@mui/material/TableRow"));
1348
+ import_TableContainer3 = __toESM(require("@mui/material/TableContainer"));
1349
+ import_Collapse = __toESM(require("@mui/material/Collapse"));
1350
+ import_IconButton3 = __toESM(require("@mui/material/IconButton"));
1351
+ import_CircularProgress3 = __toESM(require("@mui/material/CircularProgress"));
1352
+ import_styles2 = require("@mui/material/styles");
1190
1353
  import_icons_material4 = require("@mui/icons-material");
1191
1354
  import_recharts2 = require("recharts");
1192
- Stat = ({ label, value }) => /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "body2", fontWeight: 600, sx: { fontFamily: "monospace" } }, value));
1355
+ Stat = ({ label, value }) => /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", fontWeight: 600, sx: { fontFamily: "monospace" } }, value));
1193
1356
  TeamCard = ({ team, usage, usageLoading }) => {
1194
1357
  const [expanded, setExpanded] = (0, import_react5.useState)(false);
1195
- const theme = (0, import_material5.useTheme)();
1358
+ const theme = (0, import_styles2.useTheme)();
1196
1359
  const gridStroke = theme.palette.divider;
1197
1360
  const tickFill = theme.palette.text.secondary;
1198
1361
  const budget = team.max_budget ?? 0;
@@ -1201,7 +1364,16 @@ var init_TeamUsage = __esm({
1201
1364
  const isOver = budget > 0 && spend >= budget;
1202
1365
  const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;
1203
1366
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
1204
- return /* @__PURE__ */ import_react5.default.createElement(import_material5.Paper, { variant: "outlined", sx: { p: 2, mb: 2 } }, /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Group, { color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { flexGrow: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "subtitle1", fontWeight: 600 }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "caption", color: "text.secondary", sx: { fontFamily: "monospace" } }, team.team_id)), /* @__PURE__ */ import_react5.default.createElement(import_material5.IconButton, { size: "small", onClick: () => setExpanded((e) => !e), "aria-label": "toggle details" }, expanded ? /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.ExpandLess, null) : /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.ExpandMore, null))), /* @__PURE__ */ import_react5.default.createElement(import_material5.Stack, { direction: "row", spacing: 3, mt: 1.5, flexWrap: "wrap", useFlexGap: true, gap: 1.5 }, /* @__PURE__ */ import_react5.default.createElement(
1367
+ const renderDailySpendSection = () => {
1368
+ if (usageLoading) {
1369
+ return /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress3.default, { size: 24 }));
1370
+ }
1371
+ if (dailyData.length > 0) {
1372
+ return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { height: 160, mb: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.AreaChart, { data: dailyData }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.XAxis, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Area, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ import_react5.default.createElement(import_Divider.default, { sx: { mb: 2 } }));
1373
+ }
1374
+ return null;
1375
+ };
1376
+ return /* @__PURE__ */ import_react5.default.createElement(import_Paper4.default, { variant: "outlined", sx: { p: 2, mb: 2 } }, /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Group, { color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { flexGrow: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle1", fontWeight: 600 }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "caption", color: "text.secondary", sx: { fontFamily: "monospace" } }, team.team_id)), /* @__PURE__ */ import_react5.default.createElement(import_IconButton3.default, { size: "small", onClick: () => setExpanded((e) => !e), "aria-label": "toggle details" }, expanded ? /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.ExpandLess, null) : /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.ExpandMore, null))), /* @__PURE__ */ import_react5.default.createElement(import_Stack2.default, { direction: "row", spacing: 3, mt: 1.5, flexWrap: "wrap", useFlexGap: true, gap: 1.5 }, /* @__PURE__ */ import_react5.default.createElement(
1205
1377
  Stat,
1206
1378
  {
1207
1379
  label: "Members",
@@ -1229,38 +1401,38 @@ var init_TeamUsage = __esm({
1229
1401
  Stat,
1230
1402
  {
1231
1403
  label: "TPM",
1232
- value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "\u2014"
1404
+ value: typeof team.tpm_limit === "number" && team.tpm_limit > 0 ? String(team.tpm_limit) : "\u2014"
1233
1405
  }
1234
1406
  ), /* @__PURE__ */ import_react5.default.createElement(
1235
1407
  Stat,
1236
1408
  {
1237
1409
  label: "RPM",
1238
- value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "\u2014"
1410
+ value: typeof team.rpm_limit === "number" && team.rpm_limit > 0 ? String(team.rpm_limit) : "\u2014"
1239
1411
  }
1240
- )), budget > 0 && /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { mt: 1.5 }, /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { display: "flex", justifyContent: "space-between", mb: 0.5 }, /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "body2" }, "$", spend.toFixed(2), " / $", budget.toFixed(2)), isOver && /* @__PURE__ */ import_react5.default.createElement(import_material5.Chip, { label: "Over Budget", size: "small", color: "error" }), isNear && /* @__PURE__ */ import_react5.default.createElement(import_material5.Chip, { label: "Near Limit", size: "small", color: "warning" })), /* @__PURE__ */ import_react5.default.createElement(
1241
- import_material5.LinearProgress,
1412
+ )), budget > 0 && /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { mt: 1.5 }, /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { display: "flex", justifyContent: "space-between", mb: 0.5 }, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2" }, "$", spend.toFixed(2), " / $", budget.toFixed(2)), isOver && /* @__PURE__ */ import_react5.default.createElement(import_Chip4.default, { label: "Over Budget", size: "small", color: "error" }), isNear && /* @__PURE__ */ import_react5.default.createElement(import_Chip4.default, { label: "Near Limit", size: "small", color: "warning" })), /* @__PURE__ */ import_react5.default.createElement(
1413
+ import_LinearProgress4.default,
1242
1414
  {
1243
1415
  variant: "determinate",
1244
1416
  value: budgetPct,
1245
- color: isOver ? "error" : isNear ? "warning" : "primary",
1417
+ color: budgetBarColor(isOver, isNear),
1246
1418
  sx: { height: 6, borderRadius: 1 }
1247
1419
  }
1248
- )), /* @__PURE__ */ import_react5.default.createElement(import_material5.Collapse, { in: expanded }, /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { mt: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ import_react5.default.createElement(import_material5.Stack, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ import_react5.default.createElement(Stat, { label: "TPM limit", value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react5.default.createElement(Stat, { label: "RPM limit", value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react5.default.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ import_react5.default.createElement(import_material5.Divider, { sx: { mb: 2 } }), /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ import_react5.default.createElement(import_material5.Chip, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ import_react5.default.createElement(import_material5.Divider, { sx: { mb: 2 } }), usageLoading ? /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_material5.CircularProgress, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { height: 160, mb: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.AreaChart, { data: dailyData }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.XAxis, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Area, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ import_react5.default.createElement(import_material5.Divider, { sx: { mb: 2 } })) : null, /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ import_react5.default.createElement(import_material5.TableContainer, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.Table, { size: "small" }, /* @__PURE__ */ import_react5.default.createElement(import_material5.TableHead, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.TableRow, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.TableCell, null, "User"), /* @__PURE__ */ import_react5.default.createElement(import_material5.TableCell, null, "Role"))), /* @__PURE__ */ import_react5.default.createElement(import_material5.TableBody, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react5.default.createElement(import_material5.TableRow, { key: m.user_id }, /* @__PURE__ */ import_react5.default.createElement(import_material5.TableCell, null, m.user_email ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react5.default.createElement(
1249
- import_material5.Typography,
1420
+ )), /* @__PURE__ */ import_react5.default.createElement(import_Collapse.default, { in: expanded }, /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { mt: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ import_react5.default.createElement(import_Stack2.default, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ import_react5.default.createElement(Stat, { label: "TPM limit", value: typeof team.tpm_limit === "number" && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react5.default.createElement(Stat, { label: "RPM limit", value: typeof team.rpm_limit === "number" && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react5.default.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ import_react5.default.createElement(import_Divider.default, { sx: { mb: 2 } }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ import_react5.default.createElement(import_Chip4.default, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ import_react5.default.createElement(import_Divider.default, { sx: { mb: 2 } }), renderDailySpendSection(), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ import_react5.default.createElement(import_TableContainer3.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Table3.default, { size: "small" }, /* @__PURE__ */ import_react5.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react5.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react5.default.createElement(import_TableCell3.default, null, "User"), /* @__PURE__ */ import_react5.default.createElement(import_TableCell3.default, null, "Role"))), /* @__PURE__ */ import_react5.default.createElement(import_TableBody3.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react5.default.createElement(import_TableRow3.default, { key: m.user_id }, /* @__PURE__ */ import_react5.default.createElement(import_TableCell3.default, null, m.user_email ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react5.default.createElement(
1421
+ import_Typography5.default,
1250
1422
  {
1251
1423
  variant: "caption",
1252
1424
  color: "text.secondary",
1253
1425
  sx: { fontFamily: "monospace" }
1254
1426
  },
1255
1427
  m.user_id
1256
- )) : /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "body2", sx: { fontFamily: "monospace" } }, m.user_id)), /* @__PURE__ */ import_react5.default.createElement(import_material5.TableCell, null, /* @__PURE__ */ import_react5.default.createElement(
1257
- import_material5.Chip,
1428
+ )) : /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", sx: { fontFamily: "monospace" } }, m.user_id)), /* @__PURE__ */ import_react5.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react5.default.createElement(
1429
+ import_Chip4.default,
1258
1430
  {
1259
1431
  label: m.role,
1260
1432
  size: "small",
1261
1433
  color: m.role === "admin" ? "primary" : "default"
1262
1434
  }
1263
- ))))))) : /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
1435
+ ))))))) : /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
1264
1436
  };
1265
1437
  TeamUsage = ({
1266
1438
  teams,
@@ -1269,12 +1441,12 @@ var init_TeamUsage = __esm({
1269
1441
  getTeamUsageLoading
1270
1442
  }) => {
1271
1443
  if (loading) {
1272
- return /* @__PURE__ */ import_react5.default.createElement(import_material5.Paper, { sx: { p: 2 } }, /* @__PURE__ */ import_react5.default.createElement(import_material5.LinearProgress, null));
1444
+ return /* @__PURE__ */ import_react5.default.createElement(import_Paper4.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react5.default.createElement(import_LinearProgress4.default, null));
1273
1445
  }
1274
1446
  if (!teams.length) {
1275
- return /* @__PURE__ */ import_react5.default.createElement(import_material5.Paper, { sx: { p: 2 } }, /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, { display: "flex", alignItems: "flex-start", gap: 1.5 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Group, { color: "disabled", sx: { mt: 0.5 } }), /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { color: "text.secondary", variant: "body2" }, "You're not a member of any LiteLLM team yet."), /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { color: "text.secondary", variant: "body2", mt: 0.5 }, "That's fine \u2014 your account is provisioned, so you can still generate personal keys and use models. If you need a shared budget with colleagues, ask an admin to add you to a team."))));
1447
+ return /* @__PURE__ */ import_react5.default.createElement(import_Paper4.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { display: "flex", alignItems: "flex-start", gap: 1.5 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Group, { color: "disabled", sx: { mt: 0.5 } }), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { color: "text.secondary", variant: "body2" }, "You're not a member of any LiteLLM team yet."), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { color: "text.secondary", variant: "body2", mt: 0.5 }, "That's fine \u2014 your account is provisioned, so you can still generate personal keys and use models. If you need a shared budget with colleagues, ask an admin to add you to a team."))));
1276
1448
  }
1277
- return /* @__PURE__ */ import_react5.default.createElement(import_material5.Box, null, /* @__PURE__ */ import_react5.default.createElement(import_material5.Typography, { variant: "h6", mb: 1 }, "Teams"), teams.map((team) => /* @__PURE__ */ import_react5.default.createElement(
1449
+ return /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "h6", mb: 1 }, "Teams"), teams.map((team) => /* @__PURE__ */ import_react5.default.createElement(
1278
1450
  TeamCard,
1279
1451
  {
1280
1452
  key: team.team_id,
@@ -1302,12 +1474,37 @@ function formatDateTime(iso) {
1302
1474
  return iso;
1303
1475
  }
1304
1476
  }
1305
- var import_react6, import_material6, import_icons_material5, import_react_use, ACTION_COLORS, DetailRow, AuditLog;
1477
+ function renderAuditLogBody(loading, entries) {
1478
+ if (loading) {
1479
+ return /* @__PURE__ */ import_react6.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ import_react6.default.createElement(import_CircularProgress4.default, { size: 24 })));
1480
+ }
1481
+ if (entries.length === 0) {
1482
+ return /* @__PURE__ */ import_react6.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { color: "text.secondary" }, "No audit events found")));
1483
+ }
1484
+ return entries.map((entry) => /* @__PURE__ */ import_react6.default.createElement(DetailRow, { key: entry.id, entry }));
1485
+ }
1486
+ var import_react6, import_Paper5, import_Box6, import_Typography6, import_Table4, import_TableBody4, import_TableCell4, import_TableContainer4, import_TableHead4, import_TableRow4, import_TablePagination, import_TextField3, import_MenuItem3, import_Chip5, import_CircularProgress4, import_Collapse2, import_IconButton4, import_Alert2, import_icons_material5, import_react_use, ACTION_COLORS, DetailRow, AuditLog;
1306
1487
  var init_AuditLog = __esm({
1307
1488
  "src/components/AuditLog.tsx"() {
1308
1489
  "use strict";
1309
1490
  import_react6 = __toESM(require("react"));
1310
- import_material6 = require("@mui/material");
1491
+ import_Paper5 = __toESM(require("@mui/material/Paper"));
1492
+ import_Box6 = __toESM(require("@mui/material/Box"));
1493
+ import_Typography6 = __toESM(require("@mui/material/Typography"));
1494
+ import_Table4 = __toESM(require("@mui/material/Table"));
1495
+ import_TableBody4 = __toESM(require("@mui/material/TableBody"));
1496
+ import_TableCell4 = __toESM(require("@mui/material/TableCell"));
1497
+ import_TableContainer4 = __toESM(require("@mui/material/TableContainer"));
1498
+ import_TableHead4 = __toESM(require("@mui/material/TableHead"));
1499
+ import_TableRow4 = __toESM(require("@mui/material/TableRow"));
1500
+ import_TablePagination = __toESM(require("@mui/material/TablePagination"));
1501
+ import_TextField3 = __toESM(require("@mui/material/TextField"));
1502
+ import_MenuItem3 = __toESM(require("@mui/material/MenuItem"));
1503
+ import_Chip5 = __toESM(require("@mui/material/Chip"));
1504
+ import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
1505
+ import_Collapse2 = __toESM(require("@mui/material/Collapse"));
1506
+ import_IconButton4 = __toESM(require("@mui/material/IconButton"));
1507
+ import_Alert2 = __toESM(require("@mui/material/Alert"));
1311
1508
  import_icons_material5 = require("@mui/icons-material");
1312
1509
  import_react_use = require("react-use");
1313
1510
  ACTION_COLORS = {
@@ -1320,7 +1517,7 @@ var init_AuditLog = __esm({
1320
1517
  DetailRow = ({ entry }) => {
1321
1518
  const [open, setOpen] = (0, import_react6.useState)(false);
1322
1519
  const hasDetail = entry.before_value || entry.updated_values;
1323
- return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableRow, { hover: true }, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react6.default.createElement(import_material6.IconButton, { size: "small", onClick: () => setOpen((o) => !o) }, open ? /* @__PURE__ */ import_react6.default.createElement(import_icons_material5.KeyboardArrowUp, { fontSize: "small" }) : /* @__PURE__ */ import_react6.default.createElement(import_icons_material5.KeyboardArrowDown, { fontSize: "small" }))), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, { sx: { whiteSpace: "nowrap" } }, formatDateTime(entry.updated_at)), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, entry.action && /* @__PURE__ */ import_react6.default.createElement(import_material6.Chip, { label: entry.action, color: actionColor(entry.action), size: "small" })), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, entry.table_name ?? "-"), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.Typography, { variant: "body2", component: "code", sx: { fontFamily: "monospace", fontSize: "0.75rem" } }, entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "-")), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, entry.changed_by ?? "-")), hasDetail && /* @__PURE__ */ import_react6.default.createElement(import_material6.TableRow, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, { colSpan: 6, sx: { py: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_material6.Collapse, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react6.default.createElement(import_material6.Box, { p: 2, display: "flex", gap: 2, flexWrap: "wrap" }, entry.before_value && /* @__PURE__ */ import_react6.default.createElement(import_material6.Box, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react6.default.createElement(import_material6.Typography, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, "Before"), /* @__PURE__ */ import_react6.default.createElement(import_material6.Typography, { component: "pre", variant: "caption", sx: { fontFamily: "monospace", whiteSpace: "pre-wrap", wordBreak: "break-all" } }, JSON.stringify(entry.before_value, null, 2))), entry.updated_values && /* @__PURE__ */ import_react6.default.createElement(import_material6.Box, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react6.default.createElement(import_material6.Typography, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, "After"), /* @__PURE__ */ import_react6.default.createElement(import_material6.Typography, { component: "pre", variant: "caption", sx: { fontFamily: "monospace", whiteSpace: "pre-wrap", wordBreak: "break-all" } }, JSON.stringify(entry.updated_values, null, 2))))))));
1520
+ return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow4.default, { hover: true }, /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react6.default.createElement(import_IconButton4.default, { size: "small", onClick: () => setOpen((o) => !o) }, open ? /* @__PURE__ */ import_react6.default.createElement(import_icons_material5.KeyboardArrowUp, { fontSize: "small" }) : /* @__PURE__ */ import_react6.default.createElement(import_icons_material5.KeyboardArrowDown, { fontSize: "small" }))), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, { sx: { whiteSpace: "nowrap" } }, formatDateTime(entry.updated_at)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, entry.action && /* @__PURE__ */ import_react6.default.createElement(import_Chip5.default, { label: entry.action, color: actionColor(entry.action), size: "small" })), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, entry.table_name ?? "-"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "body2", component: "code", sx: { fontFamily: "monospace", fontSize: "0.75rem" } }, entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "-")), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, entry.changed_by ?? "-")), hasDetail && /* @__PURE__ */ import_react6.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, { colSpan: 6, sx: { py: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_Collapse2.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { p: 2, display: "flex", gap: 2, flexWrap: "wrap" }, entry.before_value && /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, "Before"), /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { component: "pre", variant: "caption", sx: { fontFamily: "monospace", whiteSpace: "pre-wrap", wordBreak: "break-all" } }, JSON.stringify(entry.before_value, null, 2))), entry.updated_values && /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, "After"), /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { component: "pre", variant: "caption", sx: { fontFamily: "monospace", whiteSpace: "pre-wrap", wordBreak: "break-all" } }, JSON.stringify(entry.updated_values, null, 2))))))));
1324
1521
  };
1325
1522
  AuditLog = ({ api }) => {
1326
1523
  const [page, setPage] = (0, import_react6.useState)(0);
@@ -1336,8 +1533,8 @@ var init_AuditLog = __esm({
1336
1533
  );
1337
1534
  const entries = value?.audit_logs ?? [];
1338
1535
  const total = value?.total ?? 0;
1339
- return /* @__PURE__ */ import_react6.default.createElement(import_material6.Paper, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.Box, { p: 2, display: "flex", gap: 2, flexWrap: "wrap", alignItems: "center" }, /* @__PURE__ */ import_react6.default.createElement(import_material6.Typography, { variant: "h6", sx: { flex: "0 0 auto", mr: 1 } }, "Audit Log"), /* @__PURE__ */ import_react6.default.createElement(
1340
- import_material6.TextField,
1536
+ return /* @__PURE__ */ import_react6.default.createElement(import_Paper5.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { p: 2, display: "flex", gap: 2, flexWrap: "wrap", alignItems: "center" }, /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "h6", sx: { flex: "0 0 auto", mr: 1 } }, "Audit Log"), /* @__PURE__ */ import_react6.default.createElement(
1537
+ import_TextField3.default,
1341
1538
  {
1342
1539
  size: "small",
1343
1540
  label: "Action",
@@ -1349,13 +1546,13 @@ var init_AuditLog = __esm({
1349
1546
  },
1350
1547
  sx: { minWidth: 160 }
1351
1548
  },
1352
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "" }, "All actions"),
1353
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "created" }, "Created"),
1354
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "updated" }, "Updated"),
1355
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "deleted" }, "Deleted"),
1356
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "blocked" }, "Blocked")
1549
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "" }, "All actions"),
1550
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "created" }, "Created"),
1551
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "updated" }, "Updated"),
1552
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "deleted" }, "Deleted"),
1553
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "blocked" }, "Blocked")
1357
1554
  ), /* @__PURE__ */ import_react6.default.createElement(
1358
- import_material6.TextField,
1555
+ import_TextField3.default,
1359
1556
  {
1360
1557
  size: "small",
1361
1558
  label: "Table",
@@ -1367,12 +1564,12 @@ var init_AuditLog = __esm({
1367
1564
  },
1368
1565
  sx: { minWidth: 160 }
1369
1566
  },
1370
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "" }, "All tables"),
1371
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "LiteLLM_VerificationToken" }, "Key"),
1372
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "LiteLLM_TeamTable" }, "Team"),
1373
- /* @__PURE__ */ import_react6.default.createElement(import_material6.MenuItem, { value: "LiteLLM_UserTable" }, "User")
1567
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "" }, "All tables"),
1568
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "LiteLLM_VerificationToken" }, "Key"),
1569
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "LiteLLM_TeamTable" }, "Team"),
1570
+ /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "LiteLLM_UserTable" }, "User")
1374
1571
  ), /* @__PURE__ */ import_react6.default.createElement(
1375
- import_material6.TextField,
1572
+ import_TextField3.default,
1376
1573
  {
1377
1574
  size: "small",
1378
1575
  label: "Changed by",
@@ -1383,8 +1580,8 @@ var init_AuditLog = __esm({
1383
1580
  },
1384
1581
  sx: { minWidth: 200 }
1385
1582
  }
1386
- )), error && /* @__PURE__ */ import_react6.default.createElement(import_material6.Box, { px: 2, pb: 2 }, /* @__PURE__ */ import_react6.default.createElement(import_material6.Alert, { severity: "error" }, error.message)), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableContainer, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.Table, { size: "small" }, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableHead, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableRow, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, { sx: { width: 40 } }), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, "Time"), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, "Action"), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, "Table"), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, "Object ID"), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, null, "Changed By"))), /* @__PURE__ */ import_react6.default.createElement(import_material6.TableBody, null, loading ? /* @__PURE__ */ import_react6.default.createElement(import_material6.TableRow, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ import_react6.default.createElement(import_material6.CircularProgress, { size: 24 }))) : entries.length === 0 ? /* @__PURE__ */ import_react6.default.createElement(import_material6.TableRow, null, /* @__PURE__ */ import_react6.default.createElement(import_material6.TableCell, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ import_react6.default.createElement(import_material6.Typography, { color: "text.secondary" }, "No audit events found"))) : entries.map((entry) => /* @__PURE__ */ import_react6.default.createElement(DetailRow, { key: entry.id, entry }))))), /* @__PURE__ */ import_react6.default.createElement(
1387
- import_material6.TablePagination,
1583
+ )), error && /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { px: 2, pb: 2 }, /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error" }, error.message)), /* @__PURE__ */ import_react6.default.createElement(import_TableContainer4.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Table4.default, { size: "small" }, /* @__PURE__ */ import_react6.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, "Time"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, "Action"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, "Table"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, "Object ID"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell4.default, null, "Changed By"))), /* @__PURE__ */ import_react6.default.createElement(import_TableBody4.default, null, renderAuditLogBody(loading, entries)))), /* @__PURE__ */ import_react6.default.createElement(
1584
+ import_TablePagination.default,
1388
1585
  {
1389
1586
  component: "div",
1390
1587
  count: total,
@@ -1421,12 +1618,19 @@ function initDateRange() {
1421
1618
  else start.setDate(start.getDate() - 7);
1422
1619
  return { start, end };
1423
1620
  }
1424
- var import_react7, import_material7, import_react_use2, import_core_plugin_api2, PERIOD_LS_KEY2, LiteLLMPage;
1621
+ var import_react7, import_Box7, import_Snackbar, import_Alert3, import_CircularProgress5, import_Typography7, import_Paper6, import_Tabs3, import_Tab3, import_react_use2, import_core_plugin_api2, PERIOD_LS_KEY2, LiteLLMPage;
1425
1622
  var init_LiteLLMPage = __esm({
1426
1623
  "src/components/LiteLLMPage.tsx"() {
1427
1624
  "use strict";
1428
1625
  import_react7 = __toESM(require("react"));
1429
- import_material7 = require("@mui/material");
1626
+ import_Box7 = __toESM(require("@mui/material/Box"));
1627
+ import_Snackbar = __toESM(require("@mui/material/Snackbar"));
1628
+ import_Alert3 = __toESM(require("@mui/material/Alert"));
1629
+ import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
1630
+ import_Typography7 = __toESM(require("@mui/material/Typography"));
1631
+ import_Paper6 = __toESM(require("@mui/material/Paper"));
1632
+ import_Tabs3 = __toESM(require("@mui/material/Tabs"));
1633
+ import_Tab3 = __toESM(require("@mui/material/Tab"));
1430
1634
  import_react_use2 = require("react-use");
1431
1635
  import_core_plugin_api2 = require("@backstage/core-plugin-api");
1432
1636
  init_DashboardHeader();
@@ -1613,14 +1817,14 @@ var init_LiteLLMPage = __esm({
1613
1817
  );
1614
1818
  const isInitialLoading = userLoading && !userInfo;
1615
1819
  if (isInitialLoading) {
1616
- return /* @__PURE__ */ import_react7.default.createElement(import_material7.Box, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react7.default.createElement(import_material7.CircularProgress, null));
1820
+ return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react7.default.createElement(import_CircularProgress5.default, null));
1617
1821
  }
1618
1822
  if (userError || !userInfo) {
1619
1823
  const isProvisioningEnabled = userError?.body?.provisioning === true;
1620
1824
  const hint = userError?.body?.hint;
1621
- return /* @__PURE__ */ import_react7.default.createElement(import_material7.Box, { p: 3 }, /* @__PURE__ */ import_react7.default.createElement(import_material7.Paper, { sx: { p: 3 } }, /* @__PURE__ */ import_react7.default.createElement(import_material7.Typography, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react7.default.createElement(import_material7.Typography, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react7.default.createElement(import_material7.Typography, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react7.default.createElement(import_material7.Typography, { 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.")));
1825
+ return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { p: 3 }, /* @__PURE__ */ import_react7.default.createElement(import_Paper6.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react7.default.createElement(import_Typography7.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.")));
1622
1826
  }
1623
- return /* @__PURE__ */ import_react7.default.createElement(import_material7.Box, { p: 3 }, /* @__PURE__ */ import_react7.default.createElement(import_material7.Box, { mb: 2 }, /* @__PURE__ */ import_react7.default.createElement(
1827
+ return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { p: 3 }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { mb: 2 }, /* @__PURE__ */ import_react7.default.createElement(
1624
1828
  DashboardHeader,
1625
1829
  {
1626
1830
  userInfo,
@@ -1630,16 +1834,16 @@ var init_LiteLLMPage = __esm({
1630
1834
  onGenerateKeyClick: () => setGenerateDialogOpen(true)
1631
1835
  }
1632
1836
  )), /* @__PURE__ */ import_react7.default.createElement(
1633
- import_material7.Tabs,
1837
+ import_Tabs3.default,
1634
1838
  {
1635
1839
  value: activeTab,
1636
1840
  onChange: (_, v) => setActiveTab(v),
1637
1841
  sx: { mb: 2, borderBottom: 1, borderColor: "divider" }
1638
1842
  },
1639
- /* @__PURE__ */ import_react7.default.createElement(import_material7.Tab, { label: "Overview", value: "overview" }),
1640
- /* @__PURE__ */ import_react7.default.createElement(import_material7.Tab, { label: "Keys", value: "keys" }),
1641
- /* @__PURE__ */ import_react7.default.createElement(import_material7.Tab, { label: "Teams", value: "teams" }),
1642
- userInfo.can_view_audit && /* @__PURE__ */ import_react7.default.createElement(import_material7.Tab, { label: "Audit Log", value: "audit" })
1843
+ /* @__PURE__ */ import_react7.default.createElement(import_Tab3.default, { label: "Overview", value: "overview" }),
1844
+ /* @__PURE__ */ import_react7.default.createElement(import_Tab3.default, { label: "Keys", value: "keys" }),
1845
+ /* @__PURE__ */ import_react7.default.createElement(import_Tab3.default, { label: "Teams", value: "teams" }),
1846
+ userInfo.can_view_audit && /* @__PURE__ */ import_react7.default.createElement(import_Tab3.default, { label: "Audit Log", value: "audit" })
1643
1847
  ), activeTab === "overview" && /* @__PURE__ */ import_react7.default.createElement(
1644
1848
  UsageStats,
1645
1849
  {
@@ -1689,14 +1893,14 @@ var init_LiteLLMPage = __esm({
1689
1893
  onGetConfig: () => api.getConfig()
1690
1894
  }
1691
1895
  ), /* @__PURE__ */ import_react7.default.createElement(
1692
- import_material7.Snackbar,
1896
+ import_Snackbar.default,
1693
1897
  {
1694
1898
  open: !!snackbar,
1695
1899
  autoHideDuration: 5e3,
1696
1900
  onClose: () => setSnackbar(null),
1697
1901
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
1698
1902
  },
1699
- snackbar ? /* @__PURE__ */ import_react7.default.createElement(import_material7.Alert, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
1903
+ snackbar ? /* @__PURE__ */ import_react7.default.createElement(import_Alert3.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
1700
1904
  ));
1701
1905
  };
1702
1906
  }
@@ -1754,7 +1958,15 @@ init_TeamUsage();
1754
1958
 
1755
1959
  // src/components/LiteLLMHomeWidget.tsx
1756
1960
  var import_react9 = __toESM(require("react"));
1757
- var import_material8 = require("@mui/material");
1961
+ var import_Paper7 = __toESM(require("@mui/material/Paper"));
1962
+ var import_Box8 = __toESM(require("@mui/material/Box"));
1963
+ var import_Typography8 = __toESM(require("@mui/material/Typography"));
1964
+ var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
1965
+ var import_Select2 = __toESM(require("@mui/material/Select"));
1966
+ var import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
1967
+ var import_Grid2 = __toESM(require("@mui/material/Grid"));
1968
+ var import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
1969
+ var import_Alert4 = __toESM(require("@mui/material/Alert"));
1758
1970
  var import_recharts3 = require("recharts");
1759
1971
  var import_core_plugin_api3 = require("@backstage/core-plugin-api");
1760
1972
  init_api();
@@ -1771,7 +1983,7 @@ function presetToDateRange(preset) {
1771
1983
  }
1772
1984
  return { start, end };
1773
1985
  }
1774
- var Kpi = ({ label, value }) => /* @__PURE__ */ import_react9.default.createElement(import_material8.Box, null, /* @__PURE__ */ import_react9.default.createElement(import_material8.Typography, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react9.default.createElement(import_material8.Typography, { variant: "subtitle1", fontWeight: 600 }, value));
1986
+ var Kpi = ({ label, value }) => /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "subtitle1", fontWeight: 600 }, value));
1775
1987
  var LiteLLMHomeWidget = ({
1776
1988
  defaultPeriod = "7d",
1777
1989
  title = "LiteLLM Usage"
@@ -1820,17 +2032,17 @@ var LiteLLMHomeWidget = ({
1820
2032
  spend: d.spend
1821
2033
  }));
1822
2034
  const hasSparkline = dailyData.length > 0;
1823
- return /* @__PURE__ */ import_react9.default.createElement(import_material8.Paper, { sx: { p: 2 } }, /* @__PURE__ */ import_react9.default.createElement(import_material8.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react9.default.createElement(import_material8.Typography, { variant: "h6" }, title), /* @__PURE__ */ import_react9.default.createElement(import_material8.FormControl, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react9.default.createElement(
1824
- import_material8.Select,
2035
+ return /* @__PURE__ */ import_react9.default.createElement(import_Paper7.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "h6" }, title), /* @__PURE__ */ import_react9.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react9.default.createElement(
2036
+ import_Select2.default,
1825
2037
  {
1826
2038
  value: period,
1827
2039
  onChange: (e) => setPeriod(e.target.value),
1828
2040
  displayEmpty: true
1829
2041
  },
1830
- /* @__PURE__ */ import_react9.default.createElement(import_material8.MenuItem, { value: "today" }, "Today"),
1831
- /* @__PURE__ */ import_react9.default.createElement(import_material8.MenuItem, { value: "7d" }, "7d"),
1832
- /* @__PURE__ */ import_react9.default.createElement(import_material8.MenuItem, { value: "30d" }, "30d")
1833
- ))), loading && /* @__PURE__ */ import_react9.default.createElement(import_material8.Box, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react9.default.createElement(import_material8.CircularProgress, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react9.default.createElement(import_material8.Alert, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react9.default.createElement(import_material8.Alert, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react9.default.createElement(import_material8.Grid, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react9.default.createElement(import_material8.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react9.default.createElement(import_material8.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react9.default.createElement(import_material8.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react9.default.createElement(import_material8.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react9.default.createElement(import_material8.Box, { height: 120 }, /* @__PURE__ */ import_react9.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react9.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react9.default.createElement(
2042
+ /* @__PURE__ */ import_react9.default.createElement(import_MenuItem4.default, { value: "today" }, "Today"),
2043
+ /* @__PURE__ */ import_react9.default.createElement(import_MenuItem4.default, { value: "7d" }, "7d"),
2044
+ /* @__PURE__ */ import_react9.default.createElement(import_MenuItem4.default, { value: "30d" }, "30d")
2045
+ ))), loading && /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react9.default.createElement(import_CircularProgress6.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react9.default.createElement(import_Alert4.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react9.default.createElement(import_Alert4.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react9.default.createElement(import_Grid2.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react9.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react9.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react9.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react9.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react9.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { height: 120 }, /* @__PURE__ */ import_react9.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react9.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react9.default.createElement(
1834
2046
  import_recharts3.Area,
1835
2047
  {
1836
2048
  type: "monotone",