@acarmisc/backstage-plugin-litellm 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -1353,10 +1353,18 @@ var init_format = __esm({
1353
1353
  function trimSlash(url) {
1354
1354
  return url.replace(/\/+$/, "");
1355
1355
  }
1356
+ function isModelAllowedByTeam(model, teamModels) {
1357
+ if (!teamModels || teamModels.length === 0) return true;
1358
+ if (teamModels.includes(ALL_PROXY_MODELS)) return true;
1359
+ if (teamModels.includes(model.model_name)) return true;
1360
+ return !!model.access_groups?.some((group) => teamModels.includes(group));
1361
+ }
1356
1362
  function buildSnippets(baseUrl, key, model) {
1357
1363
  const base = trimSlash(baseUrl);
1364
+ const apiBase = `${base}/v1`;
1358
1365
  return {
1359
- curl: `curl ${base}/v1/chat/completions \\
1366
+ publicEndpoint: apiBase,
1367
+ curl: `curl ${apiBase}/chat/completions \\
1360
1368
  -H "Authorization: Bearer ${key}" \\
1361
1369
  -H "Content-Type: application/json" \\
1362
1370
  -d '{
@@ -1367,14 +1375,61 @@ function buildSnippets(baseUrl, key, model) {
1367
1375
 
1368
1376
  client = OpenAI(
1369
1377
  api_key="${key}",
1370
- base_url="${base}/v1",
1378
+ base_url="${apiBase}",
1371
1379
  )
1372
1380
 
1373
1381
  response = client.chat.completions.create(
1374
1382
  model="${model}",
1375
1383
  messages=[{ "role": "user", "content": "Hello!" }],
1376
1384
  )
1377
- print(response.choices[0].message.content)`
1385
+ print(response.choices[0].message.content)`,
1386
+ opencode: `{
1387
+ "$schema": "https://opencode.ai/config.json",
1388
+ "provider": {
1389
+ "litellm": {
1390
+ "npm": "@ai-sdk/openai-compatible",
1391
+ "name": "LiteLLM",
1392
+ "options": {
1393
+ "baseURL": "${apiBase}",
1394
+ "apiKey": "${key}"
1395
+ },
1396
+ "models": {
1397
+ "${model}": {}
1398
+ }
1399
+ }
1400
+ }
1401
+ }`,
1402
+ pi: `{
1403
+ "litellm": {
1404
+ "baseUrl": "${apiBase}",
1405
+ "apiKey": "${key}",
1406
+ "api": "openai-completions",
1407
+ "models": [
1408
+ { "id": "${model}", "name": "${model}" }
1409
+ ]
1410
+ }
1411
+ }`,
1412
+ claudeCode: `# Option 1 \u2014 Static key (Method 1: Unified Endpoint)
1413
+ export ANTHROPIC_AUTH_TOKEN="${key}"
1414
+ export ANTHROPIC_BASE_URL="${base}"
1415
+ claude --model ${model}
1416
+
1417
+ # Option 2 \u2014 Dynamic key via helper script
1418
+ # 1. Create ~/bin/get-litellm-key.sh:
1419
+ cat > ~/bin/get-litellm-key.sh << 'SCRIPT'
1420
+ #!/bin/bash
1421
+ curl -s -X POST ${base}/key/generate \\
1422
+ -H "Authorization: Bearer ${key}" \\
1423
+ -H "Content-Type: application/json" \\
1424
+ -d '{}' | jq -r '.key'
1425
+ SCRIPT
1426
+ chmod +x ~/bin/get-litellm-key.sh
1427
+
1428
+ # 2. Add to ~/.claude/settings.json:
1429
+ # { "apiKeyHelper": "~/bin/get-litellm-key.sh" }
1430
+
1431
+ # 3. Set refresh interval (optional, default 1h):
1432
+ export CLAUDE_CODE_API_KEY_HELPER_TTL_MS=3600000`
1378
1433
  };
1379
1434
  }
1380
1435
  function aliasHelperText(aliasError, aliasDuplicate) {
@@ -1412,7 +1467,7 @@ function formatContextWindow2(maxInput, maxOutput) {
1412
1467
  if (outPart) return `ctx ${outPart} out`;
1413
1468
  return null;
1414
1469
  }
1415
- var import_react4, import_Box4, import_Typography4, import_Dialog2, import_DialogTitle2, import_DialogContent2, import_DialogActions2, import_TextField2, import_MenuItem, import_Button3, import_IconButton2, import_CircularProgress2, import_Autocomplete2, import_Checkbox, import_FormControlLabel, import_Alert, import_Tabs, import_Tab, import_icons_material3, generateDefaultAlias, emptyForm, SnippetTabs, GenerateKeyDialog;
1470
+ var import_react4, import_Box4, import_Typography4, import_Dialog2, import_DialogTitle2, import_DialogContent2, import_DialogActions2, import_TextField2, import_MenuItem, import_Button3, import_IconButton2, import_CircularProgress2, import_Autocomplete2, import_Checkbox, import_FormControlLabel, import_Alert, import_Tabs, import_Tab, import_icons_material3, generateDefaultAlias, emptyForm, ALL_PROXY_MODELS, SNIPPET_FILE_HINTS, SnippetTabs, GenerateKeyDialog;
1416
1471
  var init_GenerateKeyDialog = __esm({
1417
1472
  "src/components/GenerateKeyDialog.tsx"() {
1418
1473
  "use strict";
@@ -1451,10 +1506,18 @@ var init_GenerateKeyDialog = __esm({
1451
1506
  team_id: void 0,
1452
1507
  key_type: "llm_api"
1453
1508
  });
1509
+ ALL_PROXY_MODELS = "all-proxy-models";
1510
+ SNIPPET_FILE_HINTS = {
1511
+ opencode: "Add to ~/.config/opencode/opencode.json",
1512
+ pi: "Add to ~/.pi/agent/models.json",
1513
+ "claude-code": "Requires Claude Code CLI installed"
1514
+ };
1454
1515
  SnippetTabs = ({ snippets, model, onCopy }) => {
1455
1516
  const [tab, setTab] = (0, import_react4.useState)("curl");
1456
- const code = tab === "curl" ? snippets.curl : snippets.openai;
1457
- return /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Tabs.default, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 } }, /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "curl", value: "curl" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "OpenAI SDK", value: "openai" })), /* @__PURE__ */ import_react4.default.createElement(
1517
+ const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
1518
+ const code = snippets[snippetKey];
1519
+ const fileHint = SNIPPET_FILE_HINTS[tab];
1520
+ return /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Tabs.default, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 }, variant: "scrollable" }, /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "curl", value: "curl" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "OpenAI SDK", value: "openai" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "opencode", value: "opencode" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "pi", value: "pi" }), /* @__PURE__ */ import_react4.default.createElement(import_Tab.default, { label: "Claude Code", value: "claude-code" })), fileHint && /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, fileHint), /* @__PURE__ */ import_react4.default.createElement(
1458
1521
  import_Box4.default,
1459
1522
  {
1460
1523
  position: "relative",
@@ -1521,12 +1584,7 @@ var init_GenerateKeyDialog = __esm({
1521
1584
  const teamRequired = keyGenerationSettings?.teamRequired ?? true;
1522
1585
  const selectedTeam = teams.find((t) => t.team_id === formData.team_id) ?? null;
1523
1586
  const availableModels = (0, import_react4.useMemo)(() => {
1524
- const teamModels = selectedTeam?.models;
1525
- if (teamModels && teamModels.length > 0) {
1526
- const allowed = new Set(teamModels);
1527
- return models.filter((m) => allowed.has(m.model_name));
1528
- }
1529
- return models;
1587
+ return models.filter((m) => isModelAllowedByTeam(m, selectedTeam?.models));
1530
1588
  }, [models, selectedTeam]);
1531
1589
  const selectedModels = availableModels.filter((m) => (formData.models || []).includes(m.model_name));
1532
1590
  const aliasError = !(formData.alias || "").trim();
@@ -1550,12 +1608,12 @@ var init_GenerateKeyDialog = __esm({
1550
1608
  max_budget: unlimitedBudget ? null : formData.max_budget
1551
1609
  };
1552
1610
  const response = await onGenerateKey(request);
1611
+ const model = formData.models?.[0] ?? availableModels[0]?.model_name ?? "";
1553
1612
  setNewKeyValue(response.key);
1554
- setNewKeyModel(formData.models?.[0] ?? "");
1613
+ setNewKeyModel(model);
1555
1614
  setNewKeySnippets(null);
1556
1615
  try {
1557
1616
  const config = await onGetConfig();
1558
- const model = formData.models?.[0] ?? "";
1559
1617
  setNewKeySnippets(buildSnippets(config.baseUrl, response.key, model));
1560
1618
  } catch {
1561
1619
  }
@@ -1593,8 +1651,10 @@ var init_GenerateKeyDialog = __esm({
1593
1651
  getOptionLabel: (t) => t.team_alias || t.team_id,
1594
1652
  value: selectedTeam,
1595
1653
  onChange: (_e, team) => {
1596
- const teamModels = team?.models;
1597
- const restrictedModels = teamModels && teamModels.length > 0 ? (formData.models || []).filter((m) => teamModels.includes(m)) : formData.models;
1654
+ const restrictedModels = (formData.models || []).filter((name) => {
1655
+ const model = models.find((m) => m.model_name === name);
1656
+ return model ? isModelAllowedByTeam(model, team?.models) : false;
1657
+ });
1598
1658
  setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
1599
1659
  },
1600
1660
  renderInput: (params) => /* @__PURE__ */ import_react4.default.createElement(
@@ -1641,7 +1701,31 @@ var init_GenerateKeyDialog = __esm({
1641
1701
  newKeyValue
1642
1702
  ),
1643
1703
  /* @__PURE__ */ import_react4.default.createElement(import_IconButton2.default, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.ContentCopy, null))
1644
- ), newKeySnippets && /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { mt: 3 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ import_react4.default.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, generateError && /* @__PURE__ */ import_react4.default.createElement(import_Alert.default, { severity: "error", onClose: () => setGenerateError(null) }, generateError), /* @__PURE__ */ import_react4.default.createElement(
1704
+ ), newKeySnippets && /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { mt: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "Public endpoint \u2014 paste into any tool's base URL / API base field"), /* @__PURE__ */ import_react4.default.createElement(
1705
+ import_Box4.default,
1706
+ {
1707
+ display: "flex",
1708
+ alignItems: "center",
1709
+ gap: 1,
1710
+ p: 1.5,
1711
+ sx: {
1712
+ backgroundColor: "action.hover",
1713
+ border: "1px solid",
1714
+ borderColor: "divider",
1715
+ borderRadius: 1
1716
+ }
1717
+ },
1718
+ /* @__PURE__ */ import_react4.default.createElement(
1719
+ import_Typography4.default,
1720
+ {
1721
+ component: "code",
1722
+ color: "text.primary",
1723
+ sx: { fontFamily: "monospace", fontSize: 13, wordBreak: "break-all", flex: 1 }
1724
+ },
1725
+ newKeySnippets.publicEndpoint
1726
+ ),
1727
+ /* @__PURE__ */ import_react4.default.createElement(import_IconButton2.default, { size: "small", onClick: () => copyToClipboard(newKeySnippets.publicEndpoint) }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" }))
1728
+ )), newKeySnippets && /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { mt: 3 }, /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material3.Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react4.default.createElement(import_Typography4.default, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ import_react4.default.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ import_react4.default.createElement(import_Box4.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, generateError && /* @__PURE__ */ import_react4.default.createElement(import_Alert.default, { severity: "error", onClose: () => setGenerateError(null) }, generateError), /* @__PURE__ */ import_react4.default.createElement(
1645
1729
  import_TextField2.default,
1646
1730
  {
1647
1731
  label: "Alias",
@@ -2338,6 +2422,151 @@ var init_TeamUsage = __esm({
2338
2422
  }
2339
2423
  });
2340
2424
 
2425
+ // src/components/ModelsTable.tsx
2426
+ function fmtCostPerToken(cost) {
2427
+ if (cost === void 0 || cost === null) return "\u2014";
2428
+ if (cost === 0) return "$0";
2429
+ if (cost < 1e-6) return `$${(cost * 1e6).toFixed(2)}/M`;
2430
+ if (cost < 1e-3) return `$${(cost * 1e3).toFixed(3)}/K`;
2431
+ return `$${cost.toFixed(4)}`;
2432
+ }
2433
+ function fmtTokens(n) {
2434
+ if (n === void 0 || n === null) return "\u2014";
2435
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
2436
+ if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
2437
+ return String(n);
2438
+ }
2439
+ var import_react7, import_Box7, import_Table4, import_TableBody4, import_TableCell4, import_TableContainer4, import_TableHead4, import_TableRow4, import_Typography7, import_MenuItem3, import_Select, import_FormControl, import_InputLabel, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert2, import_styles5, ModelsTable;
2440
+ var init_ModelsTable = __esm({
2441
+ "src/components/ModelsTable.tsx"() {
2442
+ "use strict";
2443
+ import_react7 = __toESM(require("react"));
2444
+ import_Box7 = __toESM(require("@mui/material/Box"));
2445
+ import_Table4 = __toESM(require("@mui/material/Table"));
2446
+ import_TableBody4 = __toESM(require("@mui/material/TableBody"));
2447
+ import_TableCell4 = __toESM(require("@mui/material/TableCell"));
2448
+ import_TableContainer4 = __toESM(require("@mui/material/TableContainer"));
2449
+ import_TableHead4 = __toESM(require("@mui/material/TableHead"));
2450
+ import_TableRow4 = __toESM(require("@mui/material/TableRow"));
2451
+ import_Typography7 = __toESM(require("@mui/material/Typography"));
2452
+ import_MenuItem3 = __toESM(require("@mui/material/MenuItem"));
2453
+ import_Select = __toESM(require("@mui/material/Select"));
2454
+ import_FormControl = __toESM(require("@mui/material/FormControl"));
2455
+ import_InputLabel = __toESM(require("@mui/material/InputLabel"));
2456
+ import_ContentCopy = __toESM(require("@mui/icons-material/ContentCopy"));
2457
+ import_Tooltip = __toESM(require("@mui/material/Tooltip"));
2458
+ import_Snackbar = __toESM(require("@mui/material/Snackbar"));
2459
+ import_Alert2 = __toESM(require("@mui/material/Alert"));
2460
+ init_ui();
2461
+ import_styles5 = require("@mui/material/styles");
2462
+ ModelsTable = ({ allModels, teams, loading }) => {
2463
+ const theme = (0, import_styles5.useTheme)();
2464
+ const [selectedTeamId, setSelectedTeamId] = (0, import_react7.useState)("");
2465
+ const [copiedSnackbar, setCopiedSnackbar] = (0, import_react7.useState)(false);
2466
+ const selectedTeam = (0, import_react7.useMemo)(
2467
+ () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
2468
+ [teams, selectedTeamId]
2469
+ );
2470
+ const filteredModels = (0, import_react7.useMemo)(() => {
2471
+ if (!selectedTeam) return [];
2472
+ const teamModels = selectedTeam.models ?? [];
2473
+ if (teamModels.length === 0) return allModels;
2474
+ const allowed = new Set(teamModels);
2475
+ return allModels.filter((m) => allowed.has(m.model_name));
2476
+ }, [allModels, selectedTeam]);
2477
+ const handleCopyModelId = (0, import_react7.useCallback)((modelId) => {
2478
+ navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
2479
+ }, []);
2480
+ const modeColor = (mode) => {
2481
+ switch (mode) {
2482
+ case "chat":
2483
+ return theme.palette.info.main;
2484
+ case "embedding":
2485
+ return theme.palette.success.main;
2486
+ case "completion":
2487
+ return theme.palette.primary.main;
2488
+ case "image":
2489
+ return theme.palette.warning.main;
2490
+ default:
2491
+ return theme.palette.info.main;
2492
+ }
2493
+ };
2494
+ return /* @__PURE__ */ import_react7.default.createElement(
2495
+ SectionCard,
2496
+ {
2497
+ title: "Available Models",
2498
+ subtitle: selectedTeam ? `${filteredModels.length} model${filteredModels.length !== 1 ? "s" : ""} in ${selectedTeam.team_alias ?? selectedTeam.team_id}` : "Select a team to view its models"
2499
+ },
2500
+ /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { px: 2.5, pb: 2.5, pt: 1 } }, /* @__PURE__ */ import_react7.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react7.default.createElement(
2501
+ import_Select.default,
2502
+ {
2503
+ labelId: "team-select-label",
2504
+ value: selectedTeamId,
2505
+ label: "Team",
2506
+ onChange: (e) => setSelectedTeamId(e.target.value)
2507
+ },
2508
+ teams.map((t) => /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2509
+ ))),
2510
+ loading && /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "Loading models\u2026" }),
2511
+ !loading && !selectedTeamId && /* @__PURE__ */ import_react7.default.createElement(
2512
+ EmptyState,
2513
+ {
2514
+ message: "Select a team to see available models",
2515
+ hint: "Each team may have a different set of models assigned"
2516
+ }
2517
+ ),
2518
+ !loading && selectedTeamId && filteredModels.length === 0 && /* @__PURE__ */ import_react7.default.createElement(
2519
+ EmptyState,
2520
+ {
2521
+ message: "No models available for this team",
2522
+ hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
2523
+ }
2524
+ ),
2525
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ import_react7.default.createElement(import_TableContainer4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react7.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Model ID"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Mode"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, "Input Cost"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, "Output Cost"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, "Max Input"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, "Max Output"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Capabilities"))), /* @__PURE__ */ import_react7.default.createElement(import_TableBody4.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react7.default.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ import_react7.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react7.default.createElement(
2526
+ import_Box7.default,
2527
+ {
2528
+ component: "span",
2529
+ role: "button",
2530
+ tabIndex: 0,
2531
+ onClick: () => handleCopyModelId(m.model_name),
2532
+ onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
2533
+ sx: quietIconButtonSx("accent"),
2534
+ style: { cursor: "pointer", display: "inline-flex" }
2535
+ },
2536
+ /* @__PURE__ */ import_react7.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
2537
+ )))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, /* @__PURE__ */ import_react7.default.createElement(
2538
+ import_Box7.default,
2539
+ {
2540
+ component: "span",
2541
+ sx: {
2542
+ display: "inline-block",
2543
+ px: 0.75,
2544
+ py: 0.25,
2545
+ borderRadius: 1,
2546
+ fontSize: 11,
2547
+ fontWeight: 600,
2548
+ textTransform: "capitalize",
2549
+ bgcolor: (0, import_styles5.alpha)(modeColor(m.mode), 0.1),
2550
+ color: modeColor(m.mode)
2551
+ }
2552
+ },
2553
+ m.mode
2554
+ )), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react7.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react7.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
2555
+ /* @__PURE__ */ import_react7.default.createElement(
2556
+ import_Snackbar.default,
2557
+ {
2558
+ open: copiedSnackbar,
2559
+ autoHideDuration: 2e3,
2560
+ onClose: () => setCopiedSnackbar(false),
2561
+ anchorOrigin: { vertical: "bottom", horizontal: "center" }
2562
+ },
2563
+ /* @__PURE__ */ import_react7.default.createElement(import_Alert2.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
2564
+ )
2565
+ );
2566
+ };
2567
+ }
2568
+ });
2569
+
2341
2570
  // src/components/AuditLog.tsx
2342
2571
  function actionTone(action) {
2343
2572
  if (!action) return "neutral";
@@ -2359,10 +2588,10 @@ function formatDateTime(iso) {
2359
2588
  }
2360
2589
  function renderAuditLogBody(loading, entries) {
2361
2590
  if (loading) {
2362
- return /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
2591
+ return /* @__PURE__ */ import_react8.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react8.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
2363
2592
  }
2364
2593
  if (entries.length === 0) {
2365
- return /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { colSpan: 6 }, /* @__PURE__ */ import_react7.default.createElement(
2594
+ return /* @__PURE__ */ import_react8.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { colSpan: 6 }, /* @__PURE__ */ import_react8.default.createElement(
2366
2595
  EmptyState,
2367
2596
  {
2368
2597
  message: "No audit events found",
@@ -2370,29 +2599,29 @@ function renderAuditLogBody(loading, entries) {
2370
2599
  }
2371
2600
  )));
2372
2601
  }
2373
- return entries.map((entry) => /* @__PURE__ */ import_react7.default.createElement(DetailRow, { key: entry.id, entry }));
2602
+ return entries.map((entry) => /* @__PURE__ */ import_react8.default.createElement(DetailRow, { key: entry.id, entry }));
2374
2603
  }
2375
- var import_react7, import_Box7, import_Typography7, import_Table4, import_TableBody4, import_TableCell4, import_TableContainer4, import_TableHead4, import_TableRow4, import_TablePagination, import_TextField4, import_MenuItem3, import_Skeleton5, import_Collapse2, import_IconButton4, import_Alert2, import_styles5, import_icons_material5, import_react_use, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
2604
+ var import_react8, import_Box8, import_Typography8, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TablePagination, import_TextField4, import_MenuItem4, import_Skeleton5, import_Collapse2, import_IconButton4, import_Alert3, import_styles6, import_icons_material5, import_react_use, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
2376
2605
  var init_AuditLog = __esm({
2377
2606
  "src/components/AuditLog.tsx"() {
2378
2607
  "use strict";
2379
- import_react7 = __toESM(require("react"));
2380
- import_Box7 = __toESM(require("@mui/material/Box"));
2381
- import_Typography7 = __toESM(require("@mui/material/Typography"));
2382
- import_Table4 = __toESM(require("@mui/material/Table"));
2383
- import_TableBody4 = __toESM(require("@mui/material/TableBody"));
2384
- import_TableCell4 = __toESM(require("@mui/material/TableCell"));
2385
- import_TableContainer4 = __toESM(require("@mui/material/TableContainer"));
2386
- import_TableHead4 = __toESM(require("@mui/material/TableHead"));
2387
- import_TableRow4 = __toESM(require("@mui/material/TableRow"));
2608
+ import_react8 = __toESM(require("react"));
2609
+ import_Box8 = __toESM(require("@mui/material/Box"));
2610
+ import_Typography8 = __toESM(require("@mui/material/Typography"));
2611
+ import_Table5 = __toESM(require("@mui/material/Table"));
2612
+ import_TableBody5 = __toESM(require("@mui/material/TableBody"));
2613
+ import_TableCell5 = __toESM(require("@mui/material/TableCell"));
2614
+ import_TableContainer5 = __toESM(require("@mui/material/TableContainer"));
2615
+ import_TableHead5 = __toESM(require("@mui/material/TableHead"));
2616
+ import_TableRow5 = __toESM(require("@mui/material/TableRow"));
2388
2617
  import_TablePagination = __toESM(require("@mui/material/TablePagination"));
2389
2618
  import_TextField4 = __toESM(require("@mui/material/TextField"));
2390
- import_MenuItem3 = __toESM(require("@mui/material/MenuItem"));
2619
+ import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
2391
2620
  import_Skeleton5 = __toESM(require("@mui/material/Skeleton"));
2392
2621
  import_Collapse2 = __toESM(require("@mui/material/Collapse"));
2393
2622
  import_IconButton4 = __toESM(require("@mui/material/IconButton"));
2394
- import_Alert2 = __toESM(require("@mui/material/Alert"));
2395
- import_styles5 = require("@mui/material/styles");
2623
+ import_Alert3 = __toESM(require("@mui/material/Alert"));
2624
+ import_styles6 = require("@mui/material/styles");
2396
2625
  import_icons_material5 = require("@mui/icons-material");
2397
2626
  import_react_use = require("react-use");
2398
2627
  init_ui();
@@ -2409,9 +2638,9 @@ var init_AuditLog = __esm({
2409
2638
  LiteLLM_UserTable: "User"
2410
2639
  };
2411
2640
  DetailRow = ({ entry }) => {
2412
- const [open, setOpen] = (0, import_react7.useState)(false);
2641
+ const [open, setOpen] = (0, import_react8.useState)(false);
2413
2642
  const hasDetail = entry.before_value || entry.updated_values;
2414
- return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react7.default.createElement(
2643
+ return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react8.default.createElement(
2415
2644
  import_IconButton4.default,
2416
2645
  {
2417
2646
  size: "small",
@@ -2424,9 +2653,9 @@ var init_AuditLog = __esm({
2424
2653
  transition: theme.transitions.create("transform")
2425
2654
  })
2426
2655
  },
2427
- /* @__PURE__ */ import_react7.default.createElement(import_icons_material5.KeyboardArrowDown, { fontSize: "small" })
2428
- )), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, entry.action && /* @__PURE__ */ import_react7.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, /* @__PURE__ */ import_react7.default.createElement(
2429
- import_Typography7.default,
2656
+ /* @__PURE__ */ import_react8.default.createElement(import_icons_material5.KeyboardArrowDown, { fontSize: "small" })
2657
+ )), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, entry.action && /* @__PURE__ */ import_react8.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react8.default.createElement(
2658
+ import_Typography8.default,
2430
2659
  {
2431
2660
  variant: "body2",
2432
2661
  component: "code",
@@ -2435,8 +2664,8 @@ var init_AuditLog = __esm({
2435
2664
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
2436
2665
  },
2437
2666
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
2438
- )), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react7.default.createElement(import_Collapse2.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react7.default.createElement(
2439
- import_Box7.default,
2667
+ )), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react8.default.createElement(import_TableRow5.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react8.default.createElement(import_Collapse2.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react8.default.createElement(
2668
+ import_Box8.default,
2440
2669
  {
2441
2670
  display: "flex",
2442
2671
  gap: 2,
@@ -2445,11 +2674,11 @@ var init_AuditLog = __esm({
2445
2674
  p: 2,
2446
2675
  mb: 1.5,
2447
2676
  borderRadius: 1.5,
2448
- bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
2677
+ bgcolor: (0, import_styles6.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
2449
2678
  })
2450
2679
  },
2451
- entry.before_value && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react7.default.createElement(
2452
- import_Typography7.default,
2680
+ entry.before_value && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react8.default.createElement(
2681
+ import_Typography8.default,
2453
2682
  {
2454
2683
  variant: "caption",
2455
2684
  color: "text.secondary",
@@ -2458,9 +2687,9 @@ var init_AuditLog = __esm({
2458
2687
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2459
2688
  },
2460
2689
  "Before"
2461
- ), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
2462
- entry.updated_values && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react7.default.createElement(
2463
- import_Typography7.default,
2690
+ ), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
2691
+ entry.updated_values && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react8.default.createElement(
2692
+ import_Typography8.default,
2464
2693
  {
2465
2694
  variant: "caption",
2466
2695
  color: "text.secondary",
@@ -2469,14 +2698,14 @@ var init_AuditLog = __esm({
2469
2698
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2470
2699
  },
2471
2700
  "After"
2472
- ), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
2701
+ ), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
2473
2702
  )))));
2474
2703
  };
2475
2704
  AuditLog = ({ api }) => {
2476
- const [page, setPage] = (0, import_react7.useState)(0);
2477
- const [pageSize, setPageSize] = (0, import_react7.useState)(25);
2478
- const [filters, setFilters] = (0, import_react7.useState)({});
2479
- const fetchParams = (0, import_react7.useCallback)(
2705
+ const [page, setPage] = (0, import_react8.useState)(0);
2706
+ const [pageSize, setPageSize] = (0, import_react8.useState)(25);
2707
+ const [filters, setFilters] = (0, import_react8.useState)({});
2708
+ const fetchParams = (0, import_react8.useCallback)(
2480
2709
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
2481
2710
  [page, pageSize, filters]
2482
2711
  )();
@@ -2486,13 +2715,13 @@ var init_AuditLog = __esm({
2486
2715
  );
2487
2716
  const entries = value?.audit_logs ?? [];
2488
2717
  const total = value?.total ?? 0;
2489
- return /* @__PURE__ */ import_react7.default.createElement(
2718
+ return /* @__PURE__ */ import_react8.default.createElement(
2490
2719
  SectionCard,
2491
2720
  {
2492
2721
  title: "Audit Log",
2493
2722
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
2494
2723
  flush: true,
2495
- actions: /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(
2724
+ actions: /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(
2496
2725
  import_TextField4.default,
2497
2726
  {
2498
2727
  size: "small",
@@ -2505,12 +2734,12 @@ var init_AuditLog = __esm({
2505
2734
  },
2506
2735
  sx: { minWidth: 150 }
2507
2736
  },
2508
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "" }, "All actions"),
2509
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "created" }, "Created"),
2510
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "updated" }, "Updated"),
2511
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "deleted" }, "Deleted"),
2512
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "blocked" }, "Blocked")
2513
- ), /* @__PURE__ */ import_react7.default.createElement(
2737
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "" }, "All actions"),
2738
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "created" }, "Created"),
2739
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "updated" }, "Updated"),
2740
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "deleted" }, "Deleted"),
2741
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "blocked" }, "Blocked")
2742
+ ), /* @__PURE__ */ import_react8.default.createElement(
2514
2743
  import_TextField4.default,
2515
2744
  {
2516
2745
  size: "small",
@@ -2523,11 +2752,11 @@ var init_AuditLog = __esm({
2523
2752
  },
2524
2753
  sx: { minWidth: 150 }
2525
2754
  },
2526
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "" }, "All tables"),
2527
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "LiteLLM_VerificationToken" }, "Key"),
2528
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "LiteLLM_TeamTable" }, "Team"),
2529
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "LiteLLM_UserTable" }, "User")
2530
- ), /* @__PURE__ */ import_react7.default.createElement(
2755
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "" }, "All tables"),
2756
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "LiteLLM_VerificationToken" }, "Key"),
2757
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "LiteLLM_TeamTable" }, "Team"),
2758
+ /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { value: "LiteLLM_UserTable" }, "User")
2759
+ ), /* @__PURE__ */ import_react8.default.createElement(
2531
2760
  import_TextField4.default,
2532
2761
  {
2533
2762
  size: "small",
@@ -2541,9 +2770,9 @@ var init_AuditLog = __esm({
2541
2770
  }
2542
2771
  ))
2543
2772
  },
2544
- error && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react7.default.createElement(import_Alert2.default, { severity: "error" }, error.message)),
2545
- /* @__PURE__ */ import_react7.default.createElement(import_TableContainer4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react7.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Time"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Action"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Table"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Object ID"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "Changed By"))), /* @__PURE__ */ import_react7.default.createElement(import_TableBody4.default, null, renderAuditLogBody(loading, entries)))),
2546
- /* @__PURE__ */ import_react7.default.createElement(
2773
+ error && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react8.default.createElement(import_Alert3.default, { severity: "error" }, error.message)),
2774
+ /* @__PURE__ */ import_react8.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react8.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Time"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Action"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Table"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Object ID"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Changed By"))), /* @__PURE__ */ import_react8.default.createElement(import_TableBody5.default, null, renderAuditLogBody(loading, entries)))),
2775
+ /* @__PURE__ */ import_react8.default.createElement(
2547
2776
  import_TablePagination.default,
2548
2777
  {
2549
2778
  component: "div",
@@ -2583,16 +2812,16 @@ function initDateRange() {
2583
2812
  else start.setDate(start.getDate() - 7);
2584
2813
  return { start, end };
2585
2814
  }
2586
- var import_react8, import_Box8, import_Snackbar, import_Alert3, import_CircularProgress4, import_Typography8, import_Paper5, import_Tabs2, import_Tab2, import_react_use2, import_core_plugin_api2, PERIOD_LS_KEY2, LiteLLMPage;
2815
+ var import_react9, import_Box9, import_Snackbar2, import_Alert4, import_CircularProgress4, import_Typography9, import_Paper5, import_Tabs2, import_Tab2, import_react_use2, import_core_plugin_api2, PERIOD_LS_KEY2, LiteLLMPage;
2587
2816
  var init_LiteLLMPage = __esm({
2588
2817
  "src/components/LiteLLMPage.tsx"() {
2589
2818
  "use strict";
2590
- import_react8 = __toESM(require("react"));
2591
- import_Box8 = __toESM(require("@mui/material/Box"));
2592
- import_Snackbar = __toESM(require("@mui/material/Snackbar"));
2593
- import_Alert3 = __toESM(require("@mui/material/Alert"));
2819
+ import_react9 = __toESM(require("react"));
2820
+ import_Box9 = __toESM(require("@mui/material/Box"));
2821
+ import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
2822
+ import_Alert4 = __toESM(require("@mui/material/Alert"));
2594
2823
  import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
2595
- import_Typography8 = __toESM(require("@mui/material/Typography"));
2824
+ import_Typography9 = __toESM(require("@mui/material/Typography"));
2596
2825
  import_Paper5 = __toESM(require("@mui/material/Paper"));
2597
2826
  import_Tabs2 = __toESM(require("@mui/material/Tabs"));
2598
2827
  import_Tab2 = __toESM(require("@mui/material/Tab"));
@@ -2603,17 +2832,18 @@ var init_LiteLLMPage = __esm({
2603
2832
  init_GenerateKeyDialog();
2604
2833
  init_UsageStats();
2605
2834
  init_TeamUsage();
2835
+ init_ModelsTable();
2606
2836
  init_AuditLog();
2607
2837
  init_api();
2608
2838
  PERIOD_LS_KEY2 = "litellm_usage_period";
2609
2839
  LiteLLMPage = () => {
2610
2840
  const api = (0, import_core_plugin_api2.useApi)(liteLlmApiRef);
2611
- const [dateRange, setDateRange] = (0, import_react8.useState)(initDateRange);
2612
- const [activeTab, setActiveTab] = (0, import_react8.useState)("overview");
2613
- const [snackbar, setSnackbar] = (0, import_react8.useState)(null);
2614
- const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react8.useState)(false);
2615
- const [teamUsageCache, setTeamUsageCache] = (0, import_react8.useState)({});
2616
- const [teamUsageLoading, setTeamUsageLoading] = (0, import_react8.useState)({});
2841
+ const [dateRange, setDateRange] = (0, import_react9.useState)(initDateRange);
2842
+ const [activeTab, setActiveTab] = (0, import_react9.useState)("overview");
2843
+ const [snackbar, setSnackbar] = (0, import_react9.useState)(null);
2844
+ const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react9.useState)(false);
2845
+ const [teamUsageCache, setTeamUsageCache] = (0, import_react9.useState)({});
2846
+ const [teamUsageLoading, setTeamUsageLoading] = (0, import_react9.useState)({});
2617
2847
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use2.useAsync)(
2618
2848
  () => api.getUserInfo(),
2619
2849
  [api]
@@ -2638,7 +2868,7 @@ var init_LiteLLMPage = __esm({
2638
2868
  [api]
2639
2869
  );
2640
2870
  const { value: liteLlmConfig } = (0, import_react_use2.useAsync)(() => api.getConfig(), [api]);
2641
- const teams = (0, import_react8.useMemo)(() => {
2871
+ const teams = (0, import_react9.useMemo)(() => {
2642
2872
  if (!allTeams?.length) return [];
2643
2873
  if (!userInfo) return allTeams;
2644
2874
  const userId = userInfo.user_id;
@@ -2655,12 +2885,12 @@ var init_LiteLLMPage = __esm({
2655
2885
  const endDate = dateRange.end.toISOString().split("T")[0];
2656
2886
  return api.getUsage(startDate, endDate);
2657
2887
  }, [api, dateRange]);
2658
- const handleDateRangeChange = (0, import_react8.useCallback)((range) => {
2888
+ const handleDateRangeChange = (0, import_react9.useCallback)((range) => {
2659
2889
  setDateRange(range);
2660
2890
  setTeamUsageCache({});
2661
2891
  setTeamUsageLoading({});
2662
2892
  }, [setDateRange]);
2663
- const loadTeamUsage = (0, import_react8.useCallback)(async (teamId) => {
2893
+ const loadTeamUsage = (0, import_react9.useCallback)(async (teamId) => {
2664
2894
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
2665
2895
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
2666
2896
  try {
@@ -2674,7 +2904,7 @@ var init_LiteLLMPage = __esm({
2674
2904
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
2675
2905
  }
2676
2906
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
2677
- const allowedModels = (0, import_react8.useMemo)(() => {
2907
+ const allowedModels = (0, import_react9.useMemo)(() => {
2678
2908
  if (!allModels?.length) return [];
2679
2909
  const userModels = userInfo?.models;
2680
2910
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -2687,7 +2917,7 @@ var init_LiteLLMPage = __esm({
2687
2917
  ]);
2688
2918
  return allModels.filter((m) => allowed.has(m.model_name));
2689
2919
  }, [allModels, userInfo, teams]);
2690
- const handleGenerateKey = (0, import_react8.useCallback)(
2920
+ const handleGenerateKey = (0, import_react9.useCallback)(
2691
2921
  async (request) => {
2692
2922
  try {
2693
2923
  const response = await api.generateKey(request);
@@ -2701,7 +2931,7 @@ var init_LiteLLMPage = __esm({
2701
2931
  },
2702
2932
  [api, refreshKeys]
2703
2933
  );
2704
- const handleUpdateKey = (0, import_react8.useCallback)(
2934
+ const handleUpdateKey = (0, import_react9.useCallback)(
2705
2935
  async (keyId, request) => {
2706
2936
  try {
2707
2937
  await api.updateKey(keyId, request);
@@ -2714,7 +2944,7 @@ var init_LiteLLMPage = __esm({
2714
2944
  },
2715
2945
  [api, refreshKeys]
2716
2946
  );
2717
- const handleBlockKey = (0, import_react8.useCallback)(
2947
+ const handleBlockKey = (0, import_react9.useCallback)(
2718
2948
  async (keyId) => {
2719
2949
  try {
2720
2950
  await api.blockKey(keyId);
@@ -2726,7 +2956,7 @@ var init_LiteLLMPage = __esm({
2726
2956
  },
2727
2957
  [api, refreshKeys]
2728
2958
  );
2729
- const handleUnblockKey = (0, import_react8.useCallback)(
2959
+ const handleUnblockKey = (0, import_react9.useCallback)(
2730
2960
  async (keyId) => {
2731
2961
  try {
2732
2962
  await api.unblockKey(keyId);
@@ -2738,7 +2968,7 @@ var init_LiteLLMPage = __esm({
2738
2968
  },
2739
2969
  [api, refreshKeys]
2740
2970
  );
2741
- const handleResetKeySpend = (0, import_react8.useCallback)(
2971
+ const handleResetKeySpend = (0, import_react9.useCallback)(
2742
2972
  async (keyId) => {
2743
2973
  try {
2744
2974
  await api.resetKeySpend(keyId);
@@ -2750,7 +2980,7 @@ var init_LiteLLMPage = __esm({
2750
2980
  },
2751
2981
  [api, refreshKeys]
2752
2982
  );
2753
- const handleDeleteKey = (0, import_react8.useCallback)(
2983
+ const handleDeleteKey = (0, import_react9.useCallback)(
2754
2984
  async (keyId) => {
2755
2985
  try {
2756
2986
  await api.deleteKey(keyId);
@@ -2767,7 +2997,7 @@ var init_LiteLLMPage = __esm({
2767
2997
  },
2768
2998
  [api, refreshKeys]
2769
2999
  );
2770
- const handlePruneExpiredKeys = (0, import_react8.useCallback)(
3000
+ const handlePruneExpiredKeys = (0, import_react9.useCallback)(
2771
3001
  async () => {
2772
3002
  try {
2773
3003
  const result = await api.pruneExpiredKeys();
@@ -2782,14 +3012,14 @@ var init_LiteLLMPage = __esm({
2782
3012
  );
2783
3013
  const isInitialLoading = userLoading && !userInfo;
2784
3014
  if (isInitialLoading) {
2785
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react8.default.createElement(import_CircularProgress4.default, null));
3015
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react9.default.createElement(import_CircularProgress4.default, null));
2786
3016
  }
2787
3017
  if (userError || !userInfo) {
2788
3018
  const isProvisioningEnabled = userError?.body?.provisioning === true;
2789
3019
  const hint = userError?.body?.hint;
2790
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { p: 3 }, /* @__PURE__ */ import_react8.default.createElement(import_Paper5.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react8.default.createElement(import_Typography8.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.")));
3020
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { p: 3 }, /* @__PURE__ */ import_react9.default.createElement(import_Paper5.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.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.")));
2791
3021
  }
2792
- const pageTabs = /* @__PURE__ */ import_react8.default.createElement(
3022
+ const pageTabs = /* @__PURE__ */ import_react9.default.createElement(
2793
3023
  import_Tabs2.default,
2794
3024
  {
2795
3025
  value: activeTab,
@@ -2802,12 +3032,13 @@ var init_LiteLLMPage = __esm({
2802
3032
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
2803
3033
  }
2804
3034
  },
2805
- /* @__PURE__ */ import_react8.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
2806
- /* @__PURE__ */ import_react8.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
2807
- /* @__PURE__ */ import_react8.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
2808
- userInfo.can_view_audit && /* @__PURE__ */ import_react8.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
3035
+ /* @__PURE__ */ import_react9.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
3036
+ /* @__PURE__ */ import_react9.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
3037
+ /* @__PURE__ */ import_react9.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
3038
+ /* @__PURE__ */ import_react9.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
3039
+ userInfo.can_view_audit && /* @__PURE__ */ import_react9.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
2809
3040
  );
2810
- return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react8.default.createElement(
3041
+ return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react9.default.createElement(
2811
3042
  DashboardHeader,
2812
3043
  {
2813
3044
  userInfo,
@@ -2817,7 +3048,7 @@ var init_LiteLLMPage = __esm({
2817
3048
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
2818
3049
  tabs: pageTabs
2819
3050
  }
2820
- ), activeTab === "overview" && /* @__PURE__ */ import_react8.default.createElement(
3051
+ ), activeTab === "overview" && /* @__PURE__ */ import_react9.default.createElement(
2821
3052
  UsageStats,
2822
3053
  {
2823
3054
  usage: usage ?? null,
@@ -2827,7 +3058,7 @@ var init_LiteLLMPage = __esm({
2827
3058
  loading: usageLoading,
2828
3059
  userInfo
2829
3060
  }
2830
- ), activeTab === "keys" && /* @__PURE__ */ import_react8.default.createElement(
3061
+ ), activeTab === "keys" && /* @__PURE__ */ import_react9.default.createElement(
2831
3062
  KeysTable,
2832
3063
  {
2833
3064
  keys: keys ?? [],
@@ -2841,7 +3072,7 @@ var init_LiteLLMPage = __esm({
2841
3072
  onDeleteKey: handleDeleteKey,
2842
3073
  onPruneExpiredKeys: handlePruneExpiredKeys
2843
3074
  }
2844
- ), activeTab === "teams" && /* @__PURE__ */ import_react8.default.createElement(
3075
+ ), activeTab === "teams" && /* @__PURE__ */ import_react9.default.createElement(
2845
3076
  TeamUsage,
2846
3077
  {
2847
3078
  teams: teams ?? [],
@@ -2852,7 +3083,14 @@ var init_LiteLLMPage = __esm({
2852
3083
  },
2853
3084
  getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false
2854
3085
  }
2855
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react8.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react8.default.createElement(
3086
+ ), activeTab === "models" && /* @__PURE__ */ import_react9.default.createElement(
3087
+ ModelsTable,
3088
+ {
3089
+ allModels: allModels ?? [],
3090
+ teams: teams ?? [],
3091
+ loading: modelsLoading
3092
+ }
3093
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react9.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react9.default.createElement(
2856
3094
  GenerateKeyDialog,
2857
3095
  {
2858
3096
  open: generateDialogOpen,
@@ -2865,15 +3103,15 @@ var init_LiteLLMPage = __esm({
2865
3103
  onGenerateKey: handleGenerateKey,
2866
3104
  onGetConfig: () => api.getConfig()
2867
3105
  }
2868
- ), /* @__PURE__ */ import_react8.default.createElement(
2869
- import_Snackbar.default,
3106
+ ), /* @__PURE__ */ import_react9.default.createElement(
3107
+ import_Snackbar2.default,
2870
3108
  {
2871
3109
  open: !!snackbar,
2872
3110
  autoHideDuration: 5e3,
2873
3111
  onClose: () => setSnackbar(null),
2874
3112
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
2875
3113
  },
2876
- snackbar ? /* @__PURE__ */ import_react8.default.createElement(import_Alert3.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
3114
+ snackbar ? /* @__PURE__ */ import_react9.default.createElement(import_Alert4.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
2877
3115
  ));
2878
3116
  };
2879
3117
  }
@@ -2895,7 +3133,7 @@ __export(index_exports, {
2895
3133
  module.exports = __toCommonJS(index_exports);
2896
3134
 
2897
3135
  // src/plugin.tsx
2898
- var import_react9 = __toESM(require("react"));
3136
+ var import_react10 = __toESM(require("react"));
2899
3137
  var import_icons_material6 = require("@mui/icons-material");
2900
3138
  var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
2901
3139
  init_api();
@@ -2910,10 +3148,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
2910
3148
  params: {
2911
3149
  path: "/litellm",
2912
3150
  title: "LiteLLM",
2913
- icon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.TrendingUp, null),
3151
+ icon: /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.TrendingUp, null),
2914
3152
  loader: async () => {
2915
3153
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
2916
- return /* @__PURE__ */ import_react9.default.createElement(LiteLLMPage2, null);
3154
+ return /* @__PURE__ */ import_react10.default.createElement(LiteLLMPage2, null);
2917
3155
  }
2918
3156
  }
2919
3157
  });
@@ -2930,16 +3168,16 @@ init_UsageStats();
2930
3168
  init_TeamUsage();
2931
3169
 
2932
3170
  // src/components/LiteLLMHomeWidget.tsx
2933
- var import_react10 = __toESM(require("react"));
3171
+ var import_react11 = __toESM(require("react"));
2934
3172
  var import_Paper6 = __toESM(require("@mui/material/Paper"));
2935
- var import_Box9 = __toESM(require("@mui/material/Box"));
2936
- var import_Typography9 = __toESM(require("@mui/material/Typography"));
2937
- var import_FormControl = __toESM(require("@mui/material/FormControl"));
2938
- var import_Select = __toESM(require("@mui/material/Select"));
2939
- var import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
3173
+ var import_Box10 = __toESM(require("@mui/material/Box"));
3174
+ var import_Typography10 = __toESM(require("@mui/material/Typography"));
3175
+ var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
3176
+ var import_Select2 = __toESM(require("@mui/material/Select"));
3177
+ var import_MenuItem5 = __toESM(require("@mui/material/MenuItem"));
2940
3178
  var import_Grid2 = __toESM(require("@mui/material/Grid"));
2941
3179
  var import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
2942
- var import_Alert4 = __toESM(require("@mui/material/Alert"));
3180
+ var import_Alert5 = __toESM(require("@mui/material/Alert"));
2943
3181
  var import_recharts3 = require("recharts");
2944
3182
  var import_core_plugin_api3 = require("@backstage/core-plugin-api");
2945
3183
  init_api();
@@ -2956,19 +3194,19 @@ function presetToDateRange(preset) {
2956
3194
  }
2957
3195
  return { start, end };
2958
3196
  }
2959
- var Kpi = ({ label, value }) => /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "subtitle1", fontWeight: 600 }, value));
3197
+ var Kpi = ({ label, value }) => /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "subtitle1", fontWeight: 600 }, value));
2960
3198
  var LiteLLMHomeWidget = ({
2961
3199
  defaultPeriod = "7d",
2962
3200
  title = "LiteLLM Usage"
2963
3201
  }) => {
2964
3202
  const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
2965
- const [period, setPeriod] = (0, import_react10.useState)(defaultPeriod);
2966
- const [loading, setLoading] = (0, import_react10.useState)(true);
2967
- const [usageError, setUsageError] = (0, import_react10.useState)(null);
2968
- const [keysError, setKeysError] = (0, import_react10.useState)(null);
2969
- const [usage, setUsage] = (0, import_react10.useState)(null);
2970
- const [keys, setKeys] = (0, import_react10.useState)([]);
2971
- (0, import_react10.useEffect)(() => {
3203
+ const [period, setPeriod] = (0, import_react11.useState)(defaultPeriod);
3204
+ const [loading, setLoading] = (0, import_react11.useState)(true);
3205
+ const [usageError, setUsageError] = (0, import_react11.useState)(null);
3206
+ const [keysError, setKeysError] = (0, import_react11.useState)(null);
3207
+ const [usage, setUsage] = (0, import_react11.useState)(null);
3208
+ const [keys, setKeys] = (0, import_react11.useState)([]);
3209
+ (0, import_react11.useEffect)(() => {
2972
3210
  let cancelled = false;
2973
3211
  setLoading(true);
2974
3212
  setUsageError(null);
@@ -3005,17 +3243,17 @@ var LiteLLMHomeWidget = ({
3005
3243
  spend: d.spend
3006
3244
  }));
3007
3245
  const hasSparkline = dailyData.length > 0;
3008
- return /* @__PURE__ */ import_react10.default.createElement(import_Paper6.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "h6" }, title), /* @__PURE__ */ import_react10.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react10.default.createElement(
3009
- import_Select.default,
3246
+ return /* @__PURE__ */ import_react11.default.createElement(import_Paper6.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "h6" }, title), /* @__PURE__ */ import_react11.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react11.default.createElement(
3247
+ import_Select2.default,
3010
3248
  {
3011
3249
  value: period,
3012
3250
  onChange: (e) => setPeriod(e.target.value),
3013
3251
  displayEmpty: true
3014
3252
  },
3015
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { value: "today" }, "Today"),
3016
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { value: "7d" }, "7d"),
3017
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { value: "30d" }, "30d")
3018
- ))), loading && /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react10.default.createElement(import_CircularProgress5.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react10.default.createElement(import_Alert4.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react10.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_react10.default.createElement(import_Grid2.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react10.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react10.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react10.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react10.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react10.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react10.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react10.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react10.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { height: 120 }, /* @__PURE__ */ import_react10.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react10.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react10.default.createElement(
3253
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "today" }, "Today"),
3254
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "7d" }, "7d"),
3255
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "30d" }, "30d")
3256
+ ))), loading && /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react11.default.createElement(import_CircularProgress5.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react11.default.createElement(import_Alert5.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react11.default.createElement(import_Alert5.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react11.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react11.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react11.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react11.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react11.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { height: 120 }, /* @__PURE__ */ import_react11.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react11.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react11.default.createElement(
3019
3257
  import_recharts3.Area,
3020
3258
  {
3021
3259
  type: "monotone",