@acarmisc/backstage-plugin-litellm 0.14.0 → 0.15.1

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
@@ -1408,7 +1408,28 @@ print(response.choices[0].message.content)`,
1408
1408
  { "id": "${model}", "name": "${model}" }
1409
1409
  ]
1410
1410
  }
1411
- }`
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`
1412
1433
  };
1413
1434
  }
1414
1435
  function aliasHelperText(aliasError, aliasDuplicate) {
@@ -1488,13 +1509,15 @@ var init_GenerateKeyDialog = __esm({
1488
1509
  ALL_PROXY_MODELS = "all-proxy-models";
1489
1510
  SNIPPET_FILE_HINTS = {
1490
1511
  opencode: "Add to ~/.config/opencode/opencode.json",
1491
- pi: "Add to ~/.pi/agent/models.json"
1512
+ pi: "Add to ~/.pi/agent/models.json",
1513
+ "claude-code": "Requires Claude Code CLI installed"
1492
1514
  };
1493
1515
  SnippetTabs = ({ snippets, model, onCopy }) => {
1494
1516
  const [tab, setTab] = (0, import_react4.useState)("curl");
1495
- const code = snippets[tab];
1517
+ const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
1518
+ const code = snippets[snippetKey];
1496
1519
  const fileHint = SNIPPET_FILE_HINTS[tab];
1497
- 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" })), 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(
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(
1498
1521
  import_Box4.default,
1499
1522
  {
1500
1523
  position: "relative",
@@ -2399,6 +2422,151 @@ var init_TeamUsage = __esm({
2399
2422
  }
2400
2423
  });
2401
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
+
2402
2570
  // src/components/AuditLog.tsx
2403
2571
  function actionTone(action) {
2404
2572
  if (!action) return "neutral";
@@ -2420,10 +2588,10 @@ function formatDateTime(iso) {
2420
2588
  }
2421
2589
  function renderAuditLogBody(loading, entries) {
2422
2590
  if (loading) {
2423
- 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 })));
2424
2592
  }
2425
2593
  if (entries.length === 0) {
2426
- 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(
2427
2595
  EmptyState,
2428
2596
  {
2429
2597
  message: "No audit events found",
@@ -2431,29 +2599,29 @@ function renderAuditLogBody(loading, entries) {
2431
2599
  }
2432
2600
  )));
2433
2601
  }
2434
- 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 }));
2435
2603
  }
2436
- 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;
2437
2605
  var init_AuditLog = __esm({
2438
2606
  "src/components/AuditLog.tsx"() {
2439
2607
  "use strict";
2440
- import_react7 = __toESM(require("react"));
2441
- import_Box7 = __toESM(require("@mui/material/Box"));
2442
- import_Typography7 = __toESM(require("@mui/material/Typography"));
2443
- import_Table4 = __toESM(require("@mui/material/Table"));
2444
- import_TableBody4 = __toESM(require("@mui/material/TableBody"));
2445
- import_TableCell4 = __toESM(require("@mui/material/TableCell"));
2446
- import_TableContainer4 = __toESM(require("@mui/material/TableContainer"));
2447
- import_TableHead4 = __toESM(require("@mui/material/TableHead"));
2448
- 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"));
2449
2617
  import_TablePagination = __toESM(require("@mui/material/TablePagination"));
2450
2618
  import_TextField4 = __toESM(require("@mui/material/TextField"));
2451
- import_MenuItem3 = __toESM(require("@mui/material/MenuItem"));
2619
+ import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
2452
2620
  import_Skeleton5 = __toESM(require("@mui/material/Skeleton"));
2453
2621
  import_Collapse2 = __toESM(require("@mui/material/Collapse"));
2454
2622
  import_IconButton4 = __toESM(require("@mui/material/IconButton"));
2455
- import_Alert2 = __toESM(require("@mui/material/Alert"));
2456
- import_styles5 = require("@mui/material/styles");
2623
+ import_Alert3 = __toESM(require("@mui/material/Alert"));
2624
+ import_styles6 = require("@mui/material/styles");
2457
2625
  import_icons_material5 = require("@mui/icons-material");
2458
2626
  import_react_use = require("react-use");
2459
2627
  init_ui();
@@ -2470,9 +2638,9 @@ var init_AuditLog = __esm({
2470
2638
  LiteLLM_UserTable: "User"
2471
2639
  };
2472
2640
  DetailRow = ({ entry }) => {
2473
- const [open, setOpen] = (0, import_react7.useState)(false);
2641
+ const [open, setOpen] = (0, import_react8.useState)(false);
2474
2642
  const hasDetail = entry.before_value || entry.updated_values;
2475
- 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(
2476
2644
  import_IconButton4.default,
2477
2645
  {
2478
2646
  size: "small",
@@ -2485,9 +2653,9 @@ var init_AuditLog = __esm({
2485
2653
  transition: theme.transitions.create("transform")
2486
2654
  })
2487
2655
  },
2488
- /* @__PURE__ */ import_react7.default.createElement(import_icons_material5.KeyboardArrowDown, { fontSize: "small" })
2489
- )), /* @__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(
2490
- 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,
2491
2659
  {
2492
2660
  variant: "body2",
2493
2661
  component: "code",
@@ -2496,8 +2664,8 @@ var init_AuditLog = __esm({
2496
2664
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
2497
2665
  },
2498
2666
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
2499
- )), /* @__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(
2500
- 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,
2501
2669
  {
2502
2670
  display: "flex",
2503
2671
  gap: 2,
@@ -2506,11 +2674,11 @@ var init_AuditLog = __esm({
2506
2674
  p: 2,
2507
2675
  mb: 1.5,
2508
2676
  borderRadius: 1.5,
2509
- 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)
2510
2678
  })
2511
2679
  },
2512
- entry.before_value && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react7.default.createElement(
2513
- 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,
2514
2682
  {
2515
2683
  variant: "caption",
2516
2684
  color: "text.secondary",
@@ -2519,9 +2687,9 @@ var init_AuditLog = __esm({
2519
2687
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2520
2688
  },
2521
2689
  "Before"
2522
- ), /* @__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))),
2523
- entry.updated_values && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react7.default.createElement(
2524
- 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,
2525
2693
  {
2526
2694
  variant: "caption",
2527
2695
  color: "text.secondary",
@@ -2530,14 +2698,14 @@ var init_AuditLog = __esm({
2530
2698
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2531
2699
  },
2532
2700
  "After"
2533
- ), /* @__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)))
2534
2702
  )))));
2535
2703
  };
2536
2704
  AuditLog = ({ api }) => {
2537
- const [page, setPage] = (0, import_react7.useState)(0);
2538
- const [pageSize, setPageSize] = (0, import_react7.useState)(25);
2539
- const [filters, setFilters] = (0, import_react7.useState)({});
2540
- 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)(
2541
2709
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
2542
2710
  [page, pageSize, filters]
2543
2711
  )();
@@ -2547,13 +2715,13 @@ var init_AuditLog = __esm({
2547
2715
  );
2548
2716
  const entries = value?.audit_logs ?? [];
2549
2717
  const total = value?.total ?? 0;
2550
- return /* @__PURE__ */ import_react7.default.createElement(
2718
+ return /* @__PURE__ */ import_react8.default.createElement(
2551
2719
  SectionCard,
2552
2720
  {
2553
2721
  title: "Audit Log",
2554
2722
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
2555
2723
  flush: true,
2556
- 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(
2557
2725
  import_TextField4.default,
2558
2726
  {
2559
2727
  size: "small",
@@ -2566,12 +2734,12 @@ var init_AuditLog = __esm({
2566
2734
  },
2567
2735
  sx: { minWidth: 150 }
2568
2736
  },
2569
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "" }, "All actions"),
2570
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "created" }, "Created"),
2571
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "updated" }, "Updated"),
2572
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "deleted" }, "Deleted"),
2573
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "blocked" }, "Blocked")
2574
- ), /* @__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(
2575
2743
  import_TextField4.default,
2576
2744
  {
2577
2745
  size: "small",
@@ -2584,11 +2752,11 @@ var init_AuditLog = __esm({
2584
2752
  },
2585
2753
  sx: { minWidth: 150 }
2586
2754
  },
2587
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "" }, "All tables"),
2588
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "LiteLLM_VerificationToken" }, "Key"),
2589
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "LiteLLM_TeamTable" }, "Team"),
2590
- /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "LiteLLM_UserTable" }, "User")
2591
- ), /* @__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(
2592
2760
  import_TextField4.default,
2593
2761
  {
2594
2762
  size: "small",
@@ -2602,9 +2770,9 @@ var init_AuditLog = __esm({
2602
2770
  }
2603
2771
  ))
2604
2772
  },
2605
- 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)),
2606
- /* @__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)))),
2607
- /* @__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(
2608
2776
  import_TablePagination.default,
2609
2777
  {
2610
2778
  component: "div",
@@ -2644,16 +2812,16 @@ function initDateRange() {
2644
2812
  else start.setDate(start.getDate() - 7);
2645
2813
  return { start, end };
2646
2814
  }
2647
- 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;
2648
2816
  var init_LiteLLMPage = __esm({
2649
2817
  "src/components/LiteLLMPage.tsx"() {
2650
2818
  "use strict";
2651
- import_react8 = __toESM(require("react"));
2652
- import_Box8 = __toESM(require("@mui/material/Box"));
2653
- import_Snackbar = __toESM(require("@mui/material/Snackbar"));
2654
- 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"));
2655
2823
  import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
2656
- import_Typography8 = __toESM(require("@mui/material/Typography"));
2824
+ import_Typography9 = __toESM(require("@mui/material/Typography"));
2657
2825
  import_Paper5 = __toESM(require("@mui/material/Paper"));
2658
2826
  import_Tabs2 = __toESM(require("@mui/material/Tabs"));
2659
2827
  import_Tab2 = __toESM(require("@mui/material/Tab"));
@@ -2664,17 +2832,18 @@ var init_LiteLLMPage = __esm({
2664
2832
  init_GenerateKeyDialog();
2665
2833
  init_UsageStats();
2666
2834
  init_TeamUsage();
2835
+ init_ModelsTable();
2667
2836
  init_AuditLog();
2668
2837
  init_api();
2669
2838
  PERIOD_LS_KEY2 = "litellm_usage_period";
2670
2839
  LiteLLMPage = () => {
2671
2840
  const api = (0, import_core_plugin_api2.useApi)(liteLlmApiRef);
2672
- const [dateRange, setDateRange] = (0, import_react8.useState)(initDateRange);
2673
- const [activeTab, setActiveTab] = (0, import_react8.useState)("overview");
2674
- const [snackbar, setSnackbar] = (0, import_react8.useState)(null);
2675
- const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react8.useState)(false);
2676
- const [teamUsageCache, setTeamUsageCache] = (0, import_react8.useState)({});
2677
- 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)({});
2678
2847
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use2.useAsync)(
2679
2848
  () => api.getUserInfo(),
2680
2849
  [api]
@@ -2699,7 +2868,7 @@ var init_LiteLLMPage = __esm({
2699
2868
  [api]
2700
2869
  );
2701
2870
  const { value: liteLlmConfig } = (0, import_react_use2.useAsync)(() => api.getConfig(), [api]);
2702
- const teams = (0, import_react8.useMemo)(() => {
2871
+ const teams = (0, import_react9.useMemo)(() => {
2703
2872
  if (!allTeams?.length) return [];
2704
2873
  if (!userInfo) return allTeams;
2705
2874
  const userId = userInfo.user_id;
@@ -2716,12 +2885,12 @@ var init_LiteLLMPage = __esm({
2716
2885
  const endDate = dateRange.end.toISOString().split("T")[0];
2717
2886
  return api.getUsage(startDate, endDate);
2718
2887
  }, [api, dateRange]);
2719
- const handleDateRangeChange = (0, import_react8.useCallback)((range) => {
2888
+ const handleDateRangeChange = (0, import_react9.useCallback)((range) => {
2720
2889
  setDateRange(range);
2721
2890
  setTeamUsageCache({});
2722
2891
  setTeamUsageLoading({});
2723
2892
  }, [setDateRange]);
2724
- const loadTeamUsage = (0, import_react8.useCallback)(async (teamId) => {
2893
+ const loadTeamUsage = (0, import_react9.useCallback)(async (teamId) => {
2725
2894
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
2726
2895
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
2727
2896
  try {
@@ -2735,7 +2904,7 @@ var init_LiteLLMPage = __esm({
2735
2904
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
2736
2905
  }
2737
2906
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
2738
- const allowedModels = (0, import_react8.useMemo)(() => {
2907
+ const allowedModels = (0, import_react9.useMemo)(() => {
2739
2908
  if (!allModels?.length) return [];
2740
2909
  const userModels = userInfo?.models;
2741
2910
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -2748,7 +2917,7 @@ var init_LiteLLMPage = __esm({
2748
2917
  ]);
2749
2918
  return allModels.filter((m) => allowed.has(m.model_name));
2750
2919
  }, [allModels, userInfo, teams]);
2751
- const handleGenerateKey = (0, import_react8.useCallback)(
2920
+ const handleGenerateKey = (0, import_react9.useCallback)(
2752
2921
  async (request) => {
2753
2922
  try {
2754
2923
  const response = await api.generateKey(request);
@@ -2762,7 +2931,7 @@ var init_LiteLLMPage = __esm({
2762
2931
  },
2763
2932
  [api, refreshKeys]
2764
2933
  );
2765
- const handleUpdateKey = (0, import_react8.useCallback)(
2934
+ const handleUpdateKey = (0, import_react9.useCallback)(
2766
2935
  async (keyId, request) => {
2767
2936
  try {
2768
2937
  await api.updateKey(keyId, request);
@@ -2775,7 +2944,7 @@ var init_LiteLLMPage = __esm({
2775
2944
  },
2776
2945
  [api, refreshKeys]
2777
2946
  );
2778
- const handleBlockKey = (0, import_react8.useCallback)(
2947
+ const handleBlockKey = (0, import_react9.useCallback)(
2779
2948
  async (keyId) => {
2780
2949
  try {
2781
2950
  await api.blockKey(keyId);
@@ -2787,7 +2956,7 @@ var init_LiteLLMPage = __esm({
2787
2956
  },
2788
2957
  [api, refreshKeys]
2789
2958
  );
2790
- const handleUnblockKey = (0, import_react8.useCallback)(
2959
+ const handleUnblockKey = (0, import_react9.useCallback)(
2791
2960
  async (keyId) => {
2792
2961
  try {
2793
2962
  await api.unblockKey(keyId);
@@ -2799,7 +2968,7 @@ var init_LiteLLMPage = __esm({
2799
2968
  },
2800
2969
  [api, refreshKeys]
2801
2970
  );
2802
- const handleResetKeySpend = (0, import_react8.useCallback)(
2971
+ const handleResetKeySpend = (0, import_react9.useCallback)(
2803
2972
  async (keyId) => {
2804
2973
  try {
2805
2974
  await api.resetKeySpend(keyId);
@@ -2811,7 +2980,7 @@ var init_LiteLLMPage = __esm({
2811
2980
  },
2812
2981
  [api, refreshKeys]
2813
2982
  );
2814
- const handleDeleteKey = (0, import_react8.useCallback)(
2983
+ const handleDeleteKey = (0, import_react9.useCallback)(
2815
2984
  async (keyId) => {
2816
2985
  try {
2817
2986
  await api.deleteKey(keyId);
@@ -2828,7 +2997,7 @@ var init_LiteLLMPage = __esm({
2828
2997
  },
2829
2998
  [api, refreshKeys]
2830
2999
  );
2831
- const handlePruneExpiredKeys = (0, import_react8.useCallback)(
3000
+ const handlePruneExpiredKeys = (0, import_react9.useCallback)(
2832
3001
  async () => {
2833
3002
  try {
2834
3003
  const result = await api.pruneExpiredKeys();
@@ -2843,14 +3012,14 @@ var init_LiteLLMPage = __esm({
2843
3012
  );
2844
3013
  const isInitialLoading = userLoading && !userInfo;
2845
3014
  if (isInitialLoading) {
2846
- 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));
2847
3016
  }
2848
3017
  if (userError || !userInfo) {
2849
3018
  const isProvisioningEnabled = userError?.body?.provisioning === true;
2850
3019
  const hint = userError?.body?.hint;
2851
- 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.")));
2852
3021
  }
2853
- const pageTabs = /* @__PURE__ */ import_react8.default.createElement(
3022
+ const pageTabs = /* @__PURE__ */ import_react9.default.createElement(
2854
3023
  import_Tabs2.default,
2855
3024
  {
2856
3025
  value: activeTab,
@@ -2863,12 +3032,13 @@ var init_LiteLLMPage = __esm({
2863
3032
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
2864
3033
  }
2865
3034
  },
2866
- /* @__PURE__ */ import_react8.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
2867
- /* @__PURE__ */ import_react8.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
2868
- /* @__PURE__ */ import_react8.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
2869
- 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" })
2870
3040
  );
2871
- 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(
2872
3042
  DashboardHeader,
2873
3043
  {
2874
3044
  userInfo,
@@ -2878,7 +3048,7 @@ var init_LiteLLMPage = __esm({
2878
3048
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
2879
3049
  tabs: pageTabs
2880
3050
  }
2881
- ), activeTab === "overview" && /* @__PURE__ */ import_react8.default.createElement(
3051
+ ), activeTab === "overview" && /* @__PURE__ */ import_react9.default.createElement(
2882
3052
  UsageStats,
2883
3053
  {
2884
3054
  usage: usage ?? null,
@@ -2888,7 +3058,7 @@ var init_LiteLLMPage = __esm({
2888
3058
  loading: usageLoading,
2889
3059
  userInfo
2890
3060
  }
2891
- ), activeTab === "keys" && /* @__PURE__ */ import_react8.default.createElement(
3061
+ ), activeTab === "keys" && /* @__PURE__ */ import_react9.default.createElement(
2892
3062
  KeysTable,
2893
3063
  {
2894
3064
  keys: keys ?? [],
@@ -2902,7 +3072,7 @@ var init_LiteLLMPage = __esm({
2902
3072
  onDeleteKey: handleDeleteKey,
2903
3073
  onPruneExpiredKeys: handlePruneExpiredKeys
2904
3074
  }
2905
- ), activeTab === "teams" && /* @__PURE__ */ import_react8.default.createElement(
3075
+ ), activeTab === "teams" && /* @__PURE__ */ import_react9.default.createElement(
2906
3076
  TeamUsage,
2907
3077
  {
2908
3078
  teams: teams ?? [],
@@ -2913,7 +3083,14 @@ var init_LiteLLMPage = __esm({
2913
3083
  },
2914
3084
  getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false
2915
3085
  }
2916
- ), 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(
2917
3094
  GenerateKeyDialog,
2918
3095
  {
2919
3096
  open: generateDialogOpen,
@@ -2926,15 +3103,15 @@ var init_LiteLLMPage = __esm({
2926
3103
  onGenerateKey: handleGenerateKey,
2927
3104
  onGetConfig: () => api.getConfig()
2928
3105
  }
2929
- ), /* @__PURE__ */ import_react8.default.createElement(
2930
- import_Snackbar.default,
3106
+ ), /* @__PURE__ */ import_react9.default.createElement(
3107
+ import_Snackbar2.default,
2931
3108
  {
2932
3109
  open: !!snackbar,
2933
3110
  autoHideDuration: 5e3,
2934
3111
  onClose: () => setSnackbar(null),
2935
3112
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
2936
3113
  },
2937
- 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
2938
3115
  ));
2939
3116
  };
2940
3117
  }
@@ -2944,6 +3121,7 @@ var init_LiteLLMPage = __esm({
2944
3121
  var index_exports = {};
2945
3122
  __export(index_exports, {
2946
3123
  DashboardHeader: () => DashboardHeader,
3124
+ GenerateKeyDialog: () => GenerateKeyDialog,
2947
3125
  KeysTable: () => KeysTable,
2948
3126
  LiteLLMHomeWidget: () => LiteLLMHomeWidget,
2949
3127
  LiteLLMPage: () => LiteLLMPage,
@@ -2956,7 +3134,7 @@ __export(index_exports, {
2956
3134
  module.exports = __toCommonJS(index_exports);
2957
3135
 
2958
3136
  // src/plugin.tsx
2959
- var import_react9 = __toESM(require("react"));
3137
+ var import_react10 = __toESM(require("react"));
2960
3138
  var import_icons_material6 = require("@mui/icons-material");
2961
3139
  var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
2962
3140
  init_api();
@@ -2971,10 +3149,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
2971
3149
  params: {
2972
3150
  path: "/litellm",
2973
3151
  title: "LiteLLM",
2974
- icon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.TrendingUp, null),
3152
+ icon: /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.TrendingUp, null),
2975
3153
  loader: async () => {
2976
3154
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
2977
- return /* @__PURE__ */ import_react9.default.createElement(LiteLLMPage2, null);
3155
+ return /* @__PURE__ */ import_react10.default.createElement(LiteLLMPage2, null);
2978
3156
  }
2979
3157
  }
2980
3158
  });
@@ -2991,16 +3169,16 @@ init_UsageStats();
2991
3169
  init_TeamUsage();
2992
3170
 
2993
3171
  // src/components/LiteLLMHomeWidget.tsx
2994
- var import_react10 = __toESM(require("react"));
3172
+ var import_react11 = __toESM(require("react"));
2995
3173
  var import_Paper6 = __toESM(require("@mui/material/Paper"));
2996
- var import_Box9 = __toESM(require("@mui/material/Box"));
2997
- var import_Typography9 = __toESM(require("@mui/material/Typography"));
2998
- var import_FormControl = __toESM(require("@mui/material/FormControl"));
2999
- var import_Select = __toESM(require("@mui/material/Select"));
3000
- var import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
3174
+ var import_Box10 = __toESM(require("@mui/material/Box"));
3175
+ var import_Typography10 = __toESM(require("@mui/material/Typography"));
3176
+ var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
3177
+ var import_Select2 = __toESM(require("@mui/material/Select"));
3178
+ var import_MenuItem5 = __toESM(require("@mui/material/MenuItem"));
3001
3179
  var import_Grid2 = __toESM(require("@mui/material/Grid"));
3002
3180
  var import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
3003
- var import_Alert4 = __toESM(require("@mui/material/Alert"));
3181
+ var import_Alert5 = __toESM(require("@mui/material/Alert"));
3004
3182
  var import_recharts3 = require("recharts");
3005
3183
  var import_core_plugin_api3 = require("@backstage/core-plugin-api");
3006
3184
  init_api();
@@ -3017,19 +3195,19 @@ function presetToDateRange(preset) {
3017
3195
  }
3018
3196
  return { start, end };
3019
3197
  }
3020
- 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));
3198
+ 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));
3021
3199
  var LiteLLMHomeWidget = ({
3022
3200
  defaultPeriod = "7d",
3023
3201
  title = "LiteLLM Usage"
3024
3202
  }) => {
3025
3203
  const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
3026
- const [period, setPeriod] = (0, import_react10.useState)(defaultPeriod);
3027
- const [loading, setLoading] = (0, import_react10.useState)(true);
3028
- const [usageError, setUsageError] = (0, import_react10.useState)(null);
3029
- const [keysError, setKeysError] = (0, import_react10.useState)(null);
3030
- const [usage, setUsage] = (0, import_react10.useState)(null);
3031
- const [keys, setKeys] = (0, import_react10.useState)([]);
3032
- (0, import_react10.useEffect)(() => {
3204
+ const [period, setPeriod] = (0, import_react11.useState)(defaultPeriod);
3205
+ const [loading, setLoading] = (0, import_react11.useState)(true);
3206
+ const [usageError, setUsageError] = (0, import_react11.useState)(null);
3207
+ const [keysError, setKeysError] = (0, import_react11.useState)(null);
3208
+ const [usage, setUsage] = (0, import_react11.useState)(null);
3209
+ const [keys, setKeys] = (0, import_react11.useState)([]);
3210
+ (0, import_react11.useEffect)(() => {
3033
3211
  let cancelled = false;
3034
3212
  setLoading(true);
3035
3213
  setUsageError(null);
@@ -3066,17 +3244,17 @@ var LiteLLMHomeWidget = ({
3066
3244
  spend: d.spend
3067
3245
  }));
3068
3246
  const hasSparkline = dailyData.length > 0;
3069
- 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(
3070
- import_Select.default,
3247
+ 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(
3248
+ import_Select2.default,
3071
3249
  {
3072
3250
  value: period,
3073
3251
  onChange: (e) => setPeriod(e.target.value),
3074
3252
  displayEmpty: true
3075
3253
  },
3076
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { value: "today" }, "Today"),
3077
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { value: "7d" }, "7d"),
3078
- /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { value: "30d" }, "30d")
3079
- ))), 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(
3254
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "today" }, "Today"),
3255
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "7d" }, "7d"),
3256
+ /* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "30d" }, "30d")
3257
+ ))), 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(
3080
3258
  import_recharts3.Area,
3081
3259
  {
3082
3260
  type: "monotone",
@@ -3091,5 +3269,6 @@ var LiteLLMHomeWidget = ({
3091
3269
  };
3092
3270
 
3093
3271
  // src/index.ts
3272
+ init_GenerateKeyDialog();
3094
3273
  init_api();
3095
3274
  //# sourceMappingURL=index.cjs.js.map