@acarmisc/backstage-plugin-litellm 0.14.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/components/ModelsTable.d.ts +9 -0
- package/dist/index.cjs.js +293 -116
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.esm.js +284 -107
- package/dist/index.esm.js.map +4 -4
- package/package.json +1 -1
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2602
|
+
return entries.map((entry) => /* @__PURE__ */ import_react8.default.createElement(DetailRow, { key: entry.id, entry }));
|
|
2435
2603
|
}
|
|
2436
|
-
var
|
|
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
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2456
|
-
|
|
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,
|
|
2641
|
+
const [open, setOpen] = (0, import_react8.useState)(false);
|
|
2474
2642
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
2475
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
2489
|
-
)), /* @__PURE__ */
|
|
2490
|
-
|
|
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__ */
|
|
2500
|
-
|
|
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,
|
|
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__ */
|
|
2513
|
-
|
|
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__ */
|
|
2523
|
-
entry.updated_values && /* @__PURE__ */
|
|
2524
|
-
|
|
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__ */
|
|
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,
|
|
2538
|
-
const [pageSize, setPageSize] = (0,
|
|
2539
|
-
const [filters, setFilters] = (0,
|
|
2540
|
-
const fetchParams = (0,
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2570
|
-
/* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
2572
|
-
/* @__PURE__ */
|
|
2573
|
-
/* @__PURE__ */
|
|
2574
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
2588
|
-
/* @__PURE__ */
|
|
2589
|
-
/* @__PURE__ */
|
|
2590
|
-
/* @__PURE__ */
|
|
2591
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
2606
|
-
/* @__PURE__ */
|
|
2607
|
-
/* @__PURE__ */
|
|
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
|
|
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
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
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
|
-
|
|
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,
|
|
2673
|
-
const [activeTab, setActiveTab] = (0,
|
|
2674
|
-
const [snackbar, setSnackbar] = (0,
|
|
2675
|
-
const [generateDialogOpen, setGenerateDialogOpen] = (0,
|
|
2676
|
-
const [teamUsageCache, setTeamUsageCache] = (0,
|
|
2677
|
-
const [teamUsageLoading, setTeamUsageLoading] = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2867
|
-
/* @__PURE__ */
|
|
2868
|
-
/* @__PURE__ */
|
|
2869
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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 === "
|
|
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__ */
|
|
2930
|
-
|
|
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__ */
|
|
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
|
}
|
|
@@ -2956,7 +3133,7 @@ __export(index_exports, {
|
|
|
2956
3133
|
module.exports = __toCommonJS(index_exports);
|
|
2957
3134
|
|
|
2958
3135
|
// src/plugin.tsx
|
|
2959
|
-
var
|
|
3136
|
+
var import_react10 = __toESM(require("react"));
|
|
2960
3137
|
var import_icons_material6 = require("@mui/icons-material");
|
|
2961
3138
|
var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
|
|
2962
3139
|
init_api();
|
|
@@ -2971,10 +3148,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
|
|
|
2971
3148
|
params: {
|
|
2972
3149
|
path: "/litellm",
|
|
2973
3150
|
title: "LiteLLM",
|
|
2974
|
-
icon: /* @__PURE__ */
|
|
3151
|
+
icon: /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.TrendingUp, null),
|
|
2975
3152
|
loader: async () => {
|
|
2976
3153
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
2977
|
-
return /* @__PURE__ */
|
|
3154
|
+
return /* @__PURE__ */ import_react10.default.createElement(LiteLLMPage2, null);
|
|
2978
3155
|
}
|
|
2979
3156
|
}
|
|
2980
3157
|
});
|
|
@@ -2991,16 +3168,16 @@ init_UsageStats();
|
|
|
2991
3168
|
init_TeamUsage();
|
|
2992
3169
|
|
|
2993
3170
|
// src/components/LiteLLMHomeWidget.tsx
|
|
2994
|
-
var
|
|
3171
|
+
var import_react11 = __toESM(require("react"));
|
|
2995
3172
|
var import_Paper6 = __toESM(require("@mui/material/Paper"));
|
|
2996
|
-
var
|
|
2997
|
-
var
|
|
2998
|
-
var
|
|
2999
|
-
var
|
|
3000
|
-
var
|
|
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"));
|
|
3001
3178
|
var import_Grid2 = __toESM(require("@mui/material/Grid"));
|
|
3002
3179
|
var import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
|
|
3003
|
-
var
|
|
3180
|
+
var import_Alert5 = __toESM(require("@mui/material/Alert"));
|
|
3004
3181
|
var import_recharts3 = require("recharts");
|
|
3005
3182
|
var import_core_plugin_api3 = require("@backstage/core-plugin-api");
|
|
3006
3183
|
init_api();
|
|
@@ -3017,19 +3194,19 @@ function presetToDateRange(preset) {
|
|
|
3017
3194
|
}
|
|
3018
3195
|
return { start, end };
|
|
3019
3196
|
}
|
|
3020
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
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));
|
|
3021
3198
|
var LiteLLMHomeWidget = ({
|
|
3022
3199
|
defaultPeriod = "7d",
|
|
3023
3200
|
title = "LiteLLM Usage"
|
|
3024
3201
|
}) => {
|
|
3025
3202
|
const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
|
|
3026
|
-
const [period, setPeriod] = (0,
|
|
3027
|
-
const [loading, setLoading] = (0,
|
|
3028
|
-
const [usageError, setUsageError] = (0,
|
|
3029
|
-
const [keysError, setKeysError] = (0,
|
|
3030
|
-
const [usage, setUsage] = (0,
|
|
3031
|
-
const [keys, setKeys] = (0,
|
|
3032
|
-
(0,
|
|
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)(() => {
|
|
3033
3210
|
let cancelled = false;
|
|
3034
3211
|
setLoading(true);
|
|
3035
3212
|
setUsageError(null);
|
|
@@ -3066,17 +3243,17 @@ var LiteLLMHomeWidget = ({
|
|
|
3066
3243
|
spend: d.spend
|
|
3067
3244
|
}));
|
|
3068
3245
|
const hasSparkline = dailyData.length > 0;
|
|
3069
|
-
return /* @__PURE__ */
|
|
3070
|
-
|
|
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,
|
|
3071
3248
|
{
|
|
3072
3249
|
value: period,
|
|
3073
3250
|
onChange: (e) => setPeriod(e.target.value),
|
|
3074
3251
|
displayEmpty: true
|
|
3075
3252
|
},
|
|
3076
|
-
/* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
/* @__PURE__ */
|
|
3079
|
-
))), loading && /* @__PURE__ */
|
|
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(
|
|
3080
3257
|
import_recharts3.Area,
|
|
3081
3258
|
{
|
|
3082
3259
|
type: "monotone",
|