@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.esm.js
CHANGED
|
@@ -1405,7 +1405,28 @@ print(response.choices[0].message.content)`,
|
|
|
1405
1405
|
{ "id": "${model}", "name": "${model}" }
|
|
1406
1406
|
]
|
|
1407
1407
|
}
|
|
1408
|
-
}
|
|
1408
|
+
}`,
|
|
1409
|
+
claudeCode: `# Option 1 \u2014 Static key (Method 1: Unified Endpoint)
|
|
1410
|
+
export ANTHROPIC_AUTH_TOKEN="${key}"
|
|
1411
|
+
export ANTHROPIC_BASE_URL="${base}"
|
|
1412
|
+
claude --model ${model}
|
|
1413
|
+
|
|
1414
|
+
# Option 2 \u2014 Dynamic key via helper script
|
|
1415
|
+
# 1. Create ~/bin/get-litellm-key.sh:
|
|
1416
|
+
cat > ~/bin/get-litellm-key.sh << 'SCRIPT'
|
|
1417
|
+
#!/bin/bash
|
|
1418
|
+
curl -s -X POST ${base}/key/generate \\
|
|
1419
|
+
-H "Authorization: Bearer ${key}" \\
|
|
1420
|
+
-H "Content-Type: application/json" \\
|
|
1421
|
+
-d '{}' | jq -r '.key'
|
|
1422
|
+
SCRIPT
|
|
1423
|
+
chmod +x ~/bin/get-litellm-key.sh
|
|
1424
|
+
|
|
1425
|
+
# 2. Add to ~/.claude/settings.json:
|
|
1426
|
+
# { "apiKeyHelper": "~/bin/get-litellm-key.sh" }
|
|
1427
|
+
|
|
1428
|
+
# 3. Set refresh interval (optional, default 1h):
|
|
1429
|
+
export CLAUDE_CODE_API_KEY_HELPER_TTL_MS=3600000`
|
|
1409
1430
|
};
|
|
1410
1431
|
}
|
|
1411
1432
|
function aliasHelperText(aliasError, aliasDuplicate) {
|
|
@@ -1466,13 +1487,15 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1466
1487
|
ALL_PROXY_MODELS = "all-proxy-models";
|
|
1467
1488
|
SNIPPET_FILE_HINTS = {
|
|
1468
1489
|
opencode: "Add to ~/.config/opencode/opencode.json",
|
|
1469
|
-
pi: "Add to ~/.pi/agent/models.json"
|
|
1490
|
+
pi: "Add to ~/.pi/agent/models.json",
|
|
1491
|
+
"claude-code": "Requires Claude Code CLI installed"
|
|
1470
1492
|
};
|
|
1471
1493
|
SnippetTabs = ({ snippets, model, onCopy }) => {
|
|
1472
1494
|
const [tab, setTab] = useState2("curl");
|
|
1473
|
-
const
|
|
1495
|
+
const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
|
|
1496
|
+
const code = snippets[snippetKey];
|
|
1474
1497
|
const fileHint = SNIPPET_FILE_HINTS[tab];
|
|
1475
|
-
return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 }, variant: "scrollable" }, /* @__PURE__ */ React4.createElement(Tab, { label: "curl", value: "curl" }), /* @__PURE__ */ React4.createElement(Tab, { label: "OpenAI SDK", value: "openai" }), /* @__PURE__ */ React4.createElement(Tab, { label: "opencode", value: "opencode" }), /* @__PURE__ */ React4.createElement(Tab, { label: "pi", value: "pi" })), fileHint && /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, fileHint), /* @__PURE__ */ React4.createElement(
|
|
1498
|
+
return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 }, variant: "scrollable" }, /* @__PURE__ */ React4.createElement(Tab, { label: "curl", value: "curl" }), /* @__PURE__ */ React4.createElement(Tab, { label: "OpenAI SDK", value: "openai" }), /* @__PURE__ */ React4.createElement(Tab, { label: "opencode", value: "opencode" }), /* @__PURE__ */ React4.createElement(Tab, { label: "pi", value: "pi" }), /* @__PURE__ */ React4.createElement(Tab, { label: "Claude Code", value: "claude-code" })), fileHint && /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, fileHint), /* @__PURE__ */ React4.createElement(
|
|
1476
1499
|
Box4,
|
|
1477
1500
|
{
|
|
1478
1501
|
position: "relative",
|
|
@@ -2400,24 +2423,169 @@ var init_TeamUsage = __esm({
|
|
|
2400
2423
|
}
|
|
2401
2424
|
});
|
|
2402
2425
|
|
|
2403
|
-
// src/components/
|
|
2404
|
-
import React7, { useState as useState5, useCallback } from "react";
|
|
2426
|
+
// src/components/ModelsTable.tsx
|
|
2427
|
+
import React7, { useState as useState5, useMemo as useMemo5, useCallback } from "react";
|
|
2405
2428
|
import Box7 from "@mui/material/Box";
|
|
2406
|
-
import Typography7 from "@mui/material/Typography";
|
|
2407
2429
|
import Table4 from "@mui/material/Table";
|
|
2408
2430
|
import TableBody4 from "@mui/material/TableBody";
|
|
2409
2431
|
import TableCell4 from "@mui/material/TableCell";
|
|
2410
2432
|
import TableContainer4 from "@mui/material/TableContainer";
|
|
2411
2433
|
import TableHead4 from "@mui/material/TableHead";
|
|
2412
2434
|
import TableRow4 from "@mui/material/TableRow";
|
|
2435
|
+
import Typography7 from "@mui/material/Typography";
|
|
2436
|
+
import MenuItem3 from "@mui/material/MenuItem";
|
|
2437
|
+
import Select from "@mui/material/Select";
|
|
2438
|
+
import FormControl from "@mui/material/FormControl";
|
|
2439
|
+
import InputLabel from "@mui/material/InputLabel";
|
|
2440
|
+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
|
2441
|
+
import Tooltip3 from "@mui/material/Tooltip";
|
|
2442
|
+
import Snackbar from "@mui/material/Snackbar";
|
|
2443
|
+
import Alert2 from "@mui/material/Alert";
|
|
2444
|
+
import { alpha as alpha5, useTheme as useTheme2 } from "@mui/material/styles";
|
|
2445
|
+
function fmtCostPerToken(cost) {
|
|
2446
|
+
if (cost === void 0 || cost === null) return "\u2014";
|
|
2447
|
+
if (cost === 0) return "$0";
|
|
2448
|
+
if (cost < 1e-6) return `$${(cost * 1e6).toFixed(2)}/M`;
|
|
2449
|
+
if (cost < 1e-3) return `$${(cost * 1e3).toFixed(3)}/K`;
|
|
2450
|
+
return `$${cost.toFixed(4)}`;
|
|
2451
|
+
}
|
|
2452
|
+
function fmtTokens(n) {
|
|
2453
|
+
if (n === void 0 || n === null) return "\u2014";
|
|
2454
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2455
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
2456
|
+
return String(n);
|
|
2457
|
+
}
|
|
2458
|
+
var ModelsTable;
|
|
2459
|
+
var init_ModelsTable = __esm({
|
|
2460
|
+
"src/components/ModelsTable.tsx"() {
|
|
2461
|
+
"use strict";
|
|
2462
|
+
init_ui();
|
|
2463
|
+
ModelsTable = ({ allModels, teams, loading }) => {
|
|
2464
|
+
const theme = useTheme2();
|
|
2465
|
+
const [selectedTeamId, setSelectedTeamId] = useState5("");
|
|
2466
|
+
const [copiedSnackbar, setCopiedSnackbar] = useState5(false);
|
|
2467
|
+
const selectedTeam = useMemo5(
|
|
2468
|
+
() => teams.find((t) => t.team_id === selectedTeamId) ?? null,
|
|
2469
|
+
[teams, selectedTeamId]
|
|
2470
|
+
);
|
|
2471
|
+
const filteredModels = useMemo5(() => {
|
|
2472
|
+
if (!selectedTeam) return [];
|
|
2473
|
+
const teamModels = selectedTeam.models ?? [];
|
|
2474
|
+
if (teamModels.length === 0) return allModels;
|
|
2475
|
+
const allowed = new Set(teamModels);
|
|
2476
|
+
return allModels.filter((m) => allowed.has(m.model_name));
|
|
2477
|
+
}, [allModels, selectedTeam]);
|
|
2478
|
+
const handleCopyModelId = useCallback((modelId) => {
|
|
2479
|
+
navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
|
|
2480
|
+
}, []);
|
|
2481
|
+
const modeColor = (mode) => {
|
|
2482
|
+
switch (mode) {
|
|
2483
|
+
case "chat":
|
|
2484
|
+
return theme.palette.info.main;
|
|
2485
|
+
case "embedding":
|
|
2486
|
+
return theme.palette.success.main;
|
|
2487
|
+
case "completion":
|
|
2488
|
+
return theme.palette.primary.main;
|
|
2489
|
+
case "image":
|
|
2490
|
+
return theme.palette.warning.main;
|
|
2491
|
+
default:
|
|
2492
|
+
return theme.palette.info.main;
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
return /* @__PURE__ */ React7.createElement(
|
|
2496
|
+
SectionCard,
|
|
2497
|
+
{
|
|
2498
|
+
title: "Available Models",
|
|
2499
|
+
subtitle: selectedTeam ? `${filteredModels.length} model${filteredModels.length !== 1 ? "s" : ""} in ${selectedTeam.team_alias ?? selectedTeam.team_id}` : "Select a team to view its models"
|
|
2500
|
+
},
|
|
2501
|
+
/* @__PURE__ */ React7.createElement(Box7, { sx: { px: 2.5, pb: 2.5, pt: 1 } }, /* @__PURE__ */ React7.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React7.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React7.createElement(
|
|
2502
|
+
Select,
|
|
2503
|
+
{
|
|
2504
|
+
labelId: "team-select-label",
|
|
2505
|
+
value: selectedTeamId,
|
|
2506
|
+
label: "Team",
|
|
2507
|
+
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
2508
|
+
},
|
|
2509
|
+
teams.map((t) => /* @__PURE__ */ React7.createElement(MenuItem3, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
|
|
2510
|
+
))),
|
|
2511
|
+
loading && /* @__PURE__ */ React7.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
2512
|
+
!loading && !selectedTeamId && /* @__PURE__ */ React7.createElement(
|
|
2513
|
+
EmptyState,
|
|
2514
|
+
{
|
|
2515
|
+
message: "Select a team to see available models",
|
|
2516
|
+
hint: "Each team may have a different set of models assigned"
|
|
2517
|
+
}
|
|
2518
|
+
),
|
|
2519
|
+
!loading && selectedTeamId && filteredModels.length === 0 && /* @__PURE__ */ React7.createElement(
|
|
2520
|
+
EmptyState,
|
|
2521
|
+
{
|
|
2522
|
+
message: "No models available for this team",
|
|
2523
|
+
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
2524
|
+
}
|
|
2525
|
+
),
|
|
2526
|
+
!loading && filteredModels.length > 0 && /* @__PURE__ */ React7.createElement(TableContainer4, null, /* @__PURE__ */ React7.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead4, null, /* @__PURE__ */ React7.createElement(TableRow4, null, /* @__PURE__ */ React7.createElement(TableCell4, null, "Model ID"), /* @__PURE__ */ React7.createElement(TableCell4, null, "Mode"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Input Cost"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Output Cost"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Max Input"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Max Output"), /* @__PURE__ */ React7.createElement(TableCell4, null, "Capabilities"))), /* @__PURE__ */ React7.createElement(TableBody4, null, filteredModels.map((m) => /* @__PURE__ */ React7.createElement(TableRow4, { key: m.model_name, hover: true }, /* @__PURE__ */ React7.createElement(TableCell4, null, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React7.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ React7.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React7.createElement(
|
|
2527
|
+
Box7,
|
|
2528
|
+
{
|
|
2529
|
+
component: "span",
|
|
2530
|
+
role: "button",
|
|
2531
|
+
tabIndex: 0,
|
|
2532
|
+
onClick: () => handleCopyModelId(m.model_name),
|
|
2533
|
+
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
2534
|
+
sx: quietIconButtonSx("accent"),
|
|
2535
|
+
style: { cursor: "pointer", display: "inline-flex" }
|
|
2536
|
+
},
|
|
2537
|
+
/* @__PURE__ */ React7.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
|
|
2538
|
+
)))), /* @__PURE__ */ React7.createElement(TableCell4, null, /* @__PURE__ */ React7.createElement(
|
|
2539
|
+
Box7,
|
|
2540
|
+
{
|
|
2541
|
+
component: "span",
|
|
2542
|
+
sx: {
|
|
2543
|
+
display: "inline-block",
|
|
2544
|
+
px: 0.75,
|
|
2545
|
+
py: 0.25,
|
|
2546
|
+
borderRadius: 1,
|
|
2547
|
+
fontSize: 11,
|
|
2548
|
+
fontWeight: 600,
|
|
2549
|
+
textTransform: "capitalize",
|
|
2550
|
+
bgcolor: alpha5(modeColor(m.mode), 0.1),
|
|
2551
|
+
color: modeColor(m.mode)
|
|
2552
|
+
}
|
|
2553
|
+
},
|
|
2554
|
+
m.mode
|
|
2555
|
+
)), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React7.createElement(TableCell4, null, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React7.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React7.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
|
|
2556
|
+
/* @__PURE__ */ React7.createElement(
|
|
2557
|
+
Snackbar,
|
|
2558
|
+
{
|
|
2559
|
+
open: copiedSnackbar,
|
|
2560
|
+
autoHideDuration: 2e3,
|
|
2561
|
+
onClose: () => setCopiedSnackbar(false),
|
|
2562
|
+
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
2563
|
+
},
|
|
2564
|
+
/* @__PURE__ */ React7.createElement(Alert2, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
2565
|
+
)
|
|
2566
|
+
);
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
});
|
|
2570
|
+
|
|
2571
|
+
// src/components/AuditLog.tsx
|
|
2572
|
+
import React8, { useState as useState6, useCallback as useCallback2 } from "react";
|
|
2573
|
+
import Box8 from "@mui/material/Box";
|
|
2574
|
+
import Typography8 from "@mui/material/Typography";
|
|
2575
|
+
import Table5 from "@mui/material/Table";
|
|
2576
|
+
import TableBody5 from "@mui/material/TableBody";
|
|
2577
|
+
import TableCell5 from "@mui/material/TableCell";
|
|
2578
|
+
import TableContainer5 from "@mui/material/TableContainer";
|
|
2579
|
+
import TableHead5 from "@mui/material/TableHead";
|
|
2580
|
+
import TableRow5 from "@mui/material/TableRow";
|
|
2413
2581
|
import TablePagination from "@mui/material/TablePagination";
|
|
2414
2582
|
import TextField4 from "@mui/material/TextField";
|
|
2415
|
-
import
|
|
2583
|
+
import MenuItem4 from "@mui/material/MenuItem";
|
|
2416
2584
|
import Skeleton5 from "@mui/material/Skeleton";
|
|
2417
2585
|
import Collapse2 from "@mui/material/Collapse";
|
|
2418
2586
|
import IconButton4 from "@mui/material/IconButton";
|
|
2419
|
-
import
|
|
2420
|
-
import { alpha as
|
|
2587
|
+
import Alert3 from "@mui/material/Alert";
|
|
2588
|
+
import { alpha as alpha6 } from "@mui/material/styles";
|
|
2421
2589
|
import { KeyboardArrowDown } from "@mui/icons-material";
|
|
2422
2590
|
import { useAsync } from "react-use";
|
|
2423
2591
|
function actionTone(action) {
|
|
@@ -2440,10 +2608,10 @@ function formatDateTime(iso) {
|
|
|
2440
2608
|
}
|
|
2441
2609
|
function renderAuditLogBody(loading, entries) {
|
|
2442
2610
|
if (loading) {
|
|
2443
|
-
return /* @__PURE__ */
|
|
2611
|
+
return /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React8.createElement(Skeleton5, { variant: "rounded", height: 160 })));
|
|
2444
2612
|
}
|
|
2445
2613
|
if (entries.length === 0) {
|
|
2446
|
-
return /* @__PURE__ */
|
|
2614
|
+
return /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { colSpan: 6 }, /* @__PURE__ */ React8.createElement(
|
|
2447
2615
|
EmptyState,
|
|
2448
2616
|
{
|
|
2449
2617
|
message: "No audit events found",
|
|
@@ -2451,7 +2619,7 @@ function renderAuditLogBody(loading, entries) {
|
|
|
2451
2619
|
}
|
|
2452
2620
|
)));
|
|
2453
2621
|
}
|
|
2454
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
2622
|
+
return entries.map((entry) => /* @__PURE__ */ React8.createElement(DetailRow, { key: entry.id, entry }));
|
|
2455
2623
|
}
|
|
2456
2624
|
var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
2457
2625
|
var init_AuditLog = __esm({
|
|
@@ -2471,9 +2639,9 @@ var init_AuditLog = __esm({
|
|
|
2471
2639
|
LiteLLM_UserTable: "User"
|
|
2472
2640
|
};
|
|
2473
2641
|
DetailRow = ({ entry }) => {
|
|
2474
|
-
const [open, setOpen] =
|
|
2642
|
+
const [open, setOpen] = useState6(false);
|
|
2475
2643
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
2476
|
-
return /* @__PURE__ */
|
|
2644
|
+
return /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React8.createElement(
|
|
2477
2645
|
IconButton4,
|
|
2478
2646
|
{
|
|
2479
2647
|
size: "small",
|
|
@@ -2486,9 +2654,9 @@ var init_AuditLog = __esm({
|
|
|
2486
2654
|
transition: theme.transitions.create("transform")
|
|
2487
2655
|
})
|
|
2488
2656
|
},
|
|
2489
|
-
/* @__PURE__ */
|
|
2490
|
-
)), /* @__PURE__ */
|
|
2491
|
-
|
|
2657
|
+
/* @__PURE__ */ React8.createElement(KeyboardArrowDown, { fontSize: "small" })
|
|
2658
|
+
)), /* @__PURE__ */ React8.createElement(TableCell5, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React8.createElement(TableCell5, null, entry.action && /* @__PURE__ */ React8.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(
|
|
2659
|
+
Typography8,
|
|
2492
2660
|
{
|
|
2493
2661
|
variant: "body2",
|
|
2494
2662
|
component: "code",
|
|
@@ -2497,8 +2665,8 @@ var init_AuditLog = __esm({
|
|
|
2497
2665
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
2498
2666
|
},
|
|
2499
2667
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
2500
|
-
)), /* @__PURE__ */
|
|
2501
|
-
|
|
2668
|
+
)), /* @__PURE__ */ React8.createElement(TableCell5, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React8.createElement(TableRow5, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React8.createElement(TableCell5, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React8.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React8.createElement(
|
|
2669
|
+
Box8,
|
|
2502
2670
|
{
|
|
2503
2671
|
display: "flex",
|
|
2504
2672
|
gap: 2,
|
|
@@ -2507,11 +2675,11 @@ var init_AuditLog = __esm({
|
|
|
2507
2675
|
p: 2,
|
|
2508
2676
|
mb: 1.5,
|
|
2509
2677
|
borderRadius: 1.5,
|
|
2510
|
-
bgcolor:
|
|
2678
|
+
bgcolor: alpha6(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
2511
2679
|
})
|
|
2512
2680
|
},
|
|
2513
|
-
entry.before_value && /* @__PURE__ */
|
|
2514
|
-
|
|
2681
|
+
entry.before_value && /* @__PURE__ */ React8.createElement(Box8, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React8.createElement(
|
|
2682
|
+
Typography8,
|
|
2515
2683
|
{
|
|
2516
2684
|
variant: "caption",
|
|
2517
2685
|
color: "text.secondary",
|
|
@@ -2520,9 +2688,9 @@ var init_AuditLog = __esm({
|
|
|
2520
2688
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
2521
2689
|
},
|
|
2522
2690
|
"Before"
|
|
2523
|
-
), /* @__PURE__ */
|
|
2524
|
-
entry.updated_values && /* @__PURE__ */
|
|
2525
|
-
|
|
2691
|
+
), /* @__PURE__ */ React8.createElement(Typography8, { 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))),
|
|
2692
|
+
entry.updated_values && /* @__PURE__ */ React8.createElement(Box8, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React8.createElement(
|
|
2693
|
+
Typography8,
|
|
2526
2694
|
{
|
|
2527
2695
|
variant: "caption",
|
|
2528
2696
|
color: "text.secondary",
|
|
@@ -2531,14 +2699,14 @@ var init_AuditLog = __esm({
|
|
|
2531
2699
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
2532
2700
|
},
|
|
2533
2701
|
"After"
|
|
2534
|
-
), /* @__PURE__ */
|
|
2702
|
+
), /* @__PURE__ */ React8.createElement(Typography8, { 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)))
|
|
2535
2703
|
)))));
|
|
2536
2704
|
};
|
|
2537
2705
|
AuditLog = ({ api }) => {
|
|
2538
|
-
const [page, setPage] =
|
|
2539
|
-
const [pageSize, setPageSize] =
|
|
2540
|
-
const [filters, setFilters] =
|
|
2541
|
-
const fetchParams =
|
|
2706
|
+
const [page, setPage] = useState6(0);
|
|
2707
|
+
const [pageSize, setPageSize] = useState6(25);
|
|
2708
|
+
const [filters, setFilters] = useState6({});
|
|
2709
|
+
const fetchParams = useCallback2(
|
|
2542
2710
|
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
2543
2711
|
[page, pageSize, filters]
|
|
2544
2712
|
)();
|
|
@@ -2548,13 +2716,13 @@ var init_AuditLog = __esm({
|
|
|
2548
2716
|
);
|
|
2549
2717
|
const entries = value?.audit_logs ?? [];
|
|
2550
2718
|
const total = value?.total ?? 0;
|
|
2551
|
-
return /* @__PURE__ */
|
|
2719
|
+
return /* @__PURE__ */ React8.createElement(
|
|
2552
2720
|
SectionCard,
|
|
2553
2721
|
{
|
|
2554
2722
|
title: "Audit Log",
|
|
2555
2723
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
2556
2724
|
flush: true,
|
|
2557
|
-
actions: /* @__PURE__ */
|
|
2725
|
+
actions: /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
|
|
2558
2726
|
TextField4,
|
|
2559
2727
|
{
|
|
2560
2728
|
size: "small",
|
|
@@ -2567,12 +2735,12 @@ var init_AuditLog = __esm({
|
|
|
2567
2735
|
},
|
|
2568
2736
|
sx: { minWidth: 150 }
|
|
2569
2737
|
},
|
|
2570
|
-
/* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
2572
|
-
/* @__PURE__ */
|
|
2573
|
-
/* @__PURE__ */
|
|
2574
|
-
/* @__PURE__ */
|
|
2575
|
-
), /* @__PURE__ */
|
|
2738
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "" }, "All actions"),
|
|
2739
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "created" }, "Created"),
|
|
2740
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "updated" }, "Updated"),
|
|
2741
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "deleted" }, "Deleted"),
|
|
2742
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "blocked" }, "Blocked")
|
|
2743
|
+
), /* @__PURE__ */ React8.createElement(
|
|
2576
2744
|
TextField4,
|
|
2577
2745
|
{
|
|
2578
2746
|
size: "small",
|
|
@@ -2585,11 +2753,11 @@ var init_AuditLog = __esm({
|
|
|
2585
2753
|
},
|
|
2586
2754
|
sx: { minWidth: 150 }
|
|
2587
2755
|
},
|
|
2588
|
-
/* @__PURE__ */
|
|
2589
|
-
/* @__PURE__ */
|
|
2590
|
-
/* @__PURE__ */
|
|
2591
|
-
/* @__PURE__ */
|
|
2592
|
-
), /* @__PURE__ */
|
|
2756
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "" }, "All tables"),
|
|
2757
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
2758
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
2759
|
+
/* @__PURE__ */ React8.createElement(MenuItem4, { value: "LiteLLM_UserTable" }, "User")
|
|
2760
|
+
), /* @__PURE__ */ React8.createElement(
|
|
2593
2761
|
TextField4,
|
|
2594
2762
|
{
|
|
2595
2763
|
size: "small",
|
|
@@ -2603,9 +2771,9 @@ var init_AuditLog = __esm({
|
|
|
2603
2771
|
}
|
|
2604
2772
|
))
|
|
2605
2773
|
},
|
|
2606
|
-
error && /* @__PURE__ */
|
|
2607
|
-
/* @__PURE__ */
|
|
2608
|
-
/* @__PURE__ */
|
|
2774
|
+
error && /* @__PURE__ */ React8.createElement(Box8, { px: 2.5, pb: 2 }, /* @__PURE__ */ React8.createElement(Alert3, { severity: "error" }, error.message)),
|
|
2775
|
+
/* @__PURE__ */ React8.createElement(TableContainer5, null, /* @__PURE__ */ React8.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead5, null, /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { sx: { width: 40 } }), /* @__PURE__ */ React8.createElement(TableCell5, null, "Time"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Action"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Table"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Object ID"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Changed By"))), /* @__PURE__ */ React8.createElement(TableBody5, null, renderAuditLogBody(loading, entries)))),
|
|
2776
|
+
/* @__PURE__ */ React8.createElement(
|
|
2609
2777
|
TablePagination,
|
|
2610
2778
|
{
|
|
2611
2779
|
component: "div",
|
|
@@ -2631,12 +2799,12 @@ var LiteLLMPage_exports = {};
|
|
|
2631
2799
|
__export(LiteLLMPage_exports, {
|
|
2632
2800
|
LiteLLMPage: () => LiteLLMPage
|
|
2633
2801
|
});
|
|
2634
|
-
import
|
|
2635
|
-
import
|
|
2636
|
-
import
|
|
2637
|
-
import
|
|
2802
|
+
import React9, { useState as useState7, useCallback as useCallback3, useMemo as useMemo6 } from "react";
|
|
2803
|
+
import Box9 from "@mui/material/Box";
|
|
2804
|
+
import Snackbar2 from "@mui/material/Snackbar";
|
|
2805
|
+
import Alert4 from "@mui/material/Alert";
|
|
2638
2806
|
import CircularProgress4 from "@mui/material/CircularProgress";
|
|
2639
|
-
import
|
|
2807
|
+
import Typography9 from "@mui/material/Typography";
|
|
2640
2808
|
import Paper5 from "@mui/material/Paper";
|
|
2641
2809
|
import Tabs2 from "@mui/material/Tabs";
|
|
2642
2810
|
import Tab2 from "@mui/material/Tab";
|
|
@@ -2665,17 +2833,18 @@ var init_LiteLLMPage = __esm({
|
|
|
2665
2833
|
init_GenerateKeyDialog();
|
|
2666
2834
|
init_UsageStats();
|
|
2667
2835
|
init_TeamUsage();
|
|
2836
|
+
init_ModelsTable();
|
|
2668
2837
|
init_AuditLog();
|
|
2669
2838
|
init_api();
|
|
2670
2839
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
2671
2840
|
LiteLLMPage = () => {
|
|
2672
2841
|
const api = useApi(liteLlmApiRef);
|
|
2673
|
-
const [dateRange, setDateRange] =
|
|
2674
|
-
const [activeTab, setActiveTab] =
|
|
2675
|
-
const [snackbar, setSnackbar] =
|
|
2676
|
-
const [generateDialogOpen, setGenerateDialogOpen] =
|
|
2677
|
-
const [teamUsageCache, setTeamUsageCache] =
|
|
2678
|
-
const [teamUsageLoading, setTeamUsageLoading] =
|
|
2842
|
+
const [dateRange, setDateRange] = useState7(initDateRange);
|
|
2843
|
+
const [activeTab, setActiveTab] = useState7("overview");
|
|
2844
|
+
const [snackbar, setSnackbar] = useState7(null);
|
|
2845
|
+
const [generateDialogOpen, setGenerateDialogOpen] = useState7(false);
|
|
2846
|
+
const [teamUsageCache, setTeamUsageCache] = useState7({});
|
|
2847
|
+
const [teamUsageLoading, setTeamUsageLoading] = useState7({});
|
|
2679
2848
|
const { value: userInfo, loading: userLoading, error: userError } = useAsync2(
|
|
2680
2849
|
() => api.getUserInfo(),
|
|
2681
2850
|
[api]
|
|
@@ -2700,7 +2869,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2700
2869
|
[api]
|
|
2701
2870
|
);
|
|
2702
2871
|
const { value: liteLlmConfig } = useAsync2(() => api.getConfig(), [api]);
|
|
2703
|
-
const teams =
|
|
2872
|
+
const teams = useMemo6(() => {
|
|
2704
2873
|
if (!allTeams?.length) return [];
|
|
2705
2874
|
if (!userInfo) return allTeams;
|
|
2706
2875
|
const userId = userInfo.user_id;
|
|
@@ -2717,12 +2886,12 @@ var init_LiteLLMPage = __esm({
|
|
|
2717
2886
|
const endDate = dateRange.end.toISOString().split("T")[0];
|
|
2718
2887
|
return api.getUsage(startDate, endDate);
|
|
2719
2888
|
}, [api, dateRange]);
|
|
2720
|
-
const handleDateRangeChange =
|
|
2889
|
+
const handleDateRangeChange = useCallback3((range) => {
|
|
2721
2890
|
setDateRange(range);
|
|
2722
2891
|
setTeamUsageCache({});
|
|
2723
2892
|
setTeamUsageLoading({});
|
|
2724
2893
|
}, [setDateRange]);
|
|
2725
|
-
const loadTeamUsage =
|
|
2894
|
+
const loadTeamUsage = useCallback3(async (teamId) => {
|
|
2726
2895
|
if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
|
|
2727
2896
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
|
|
2728
2897
|
try {
|
|
@@ -2736,7 +2905,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2736
2905
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
|
|
2737
2906
|
}
|
|
2738
2907
|
}, [api, dateRange, teamUsageCache, teamUsageLoading]);
|
|
2739
|
-
const allowedModels =
|
|
2908
|
+
const allowedModels = useMemo6(() => {
|
|
2740
2909
|
if (!allModels?.length) return [];
|
|
2741
2910
|
const userModels = userInfo?.models;
|
|
2742
2911
|
const teamModels = teams?.flatMap((t) => t.models ?? []);
|
|
@@ -2749,7 +2918,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2749
2918
|
]);
|
|
2750
2919
|
return allModels.filter((m) => allowed.has(m.model_name));
|
|
2751
2920
|
}, [allModels, userInfo, teams]);
|
|
2752
|
-
const handleGenerateKey =
|
|
2921
|
+
const handleGenerateKey = useCallback3(
|
|
2753
2922
|
async (request) => {
|
|
2754
2923
|
try {
|
|
2755
2924
|
const response = await api.generateKey(request);
|
|
@@ -2763,7 +2932,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2763
2932
|
},
|
|
2764
2933
|
[api, refreshKeys]
|
|
2765
2934
|
);
|
|
2766
|
-
const handleUpdateKey =
|
|
2935
|
+
const handleUpdateKey = useCallback3(
|
|
2767
2936
|
async (keyId, request) => {
|
|
2768
2937
|
try {
|
|
2769
2938
|
await api.updateKey(keyId, request);
|
|
@@ -2776,7 +2945,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2776
2945
|
},
|
|
2777
2946
|
[api, refreshKeys]
|
|
2778
2947
|
);
|
|
2779
|
-
const handleBlockKey =
|
|
2948
|
+
const handleBlockKey = useCallback3(
|
|
2780
2949
|
async (keyId) => {
|
|
2781
2950
|
try {
|
|
2782
2951
|
await api.blockKey(keyId);
|
|
@@ -2788,7 +2957,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2788
2957
|
},
|
|
2789
2958
|
[api, refreshKeys]
|
|
2790
2959
|
);
|
|
2791
|
-
const handleUnblockKey =
|
|
2960
|
+
const handleUnblockKey = useCallback3(
|
|
2792
2961
|
async (keyId) => {
|
|
2793
2962
|
try {
|
|
2794
2963
|
await api.unblockKey(keyId);
|
|
@@ -2800,7 +2969,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2800
2969
|
},
|
|
2801
2970
|
[api, refreshKeys]
|
|
2802
2971
|
);
|
|
2803
|
-
const handleResetKeySpend =
|
|
2972
|
+
const handleResetKeySpend = useCallback3(
|
|
2804
2973
|
async (keyId) => {
|
|
2805
2974
|
try {
|
|
2806
2975
|
await api.resetKeySpend(keyId);
|
|
@@ -2812,7 +2981,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2812
2981
|
},
|
|
2813
2982
|
[api, refreshKeys]
|
|
2814
2983
|
);
|
|
2815
|
-
const handleDeleteKey =
|
|
2984
|
+
const handleDeleteKey = useCallback3(
|
|
2816
2985
|
async (keyId) => {
|
|
2817
2986
|
try {
|
|
2818
2987
|
await api.deleteKey(keyId);
|
|
@@ -2829,7 +2998,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2829
2998
|
},
|
|
2830
2999
|
[api, refreshKeys]
|
|
2831
3000
|
);
|
|
2832
|
-
const handlePruneExpiredKeys =
|
|
3001
|
+
const handlePruneExpiredKeys = useCallback3(
|
|
2833
3002
|
async () => {
|
|
2834
3003
|
try {
|
|
2835
3004
|
const result = await api.pruneExpiredKeys();
|
|
@@ -2844,14 +3013,14 @@ var init_LiteLLMPage = __esm({
|
|
|
2844
3013
|
);
|
|
2845
3014
|
const isInitialLoading = userLoading && !userInfo;
|
|
2846
3015
|
if (isInitialLoading) {
|
|
2847
|
-
return /* @__PURE__ */
|
|
3016
|
+
return /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React9.createElement(CircularProgress4, null));
|
|
2848
3017
|
}
|
|
2849
3018
|
if (userError || !userInfo) {
|
|
2850
3019
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
2851
3020
|
const hint = userError?.body?.hint;
|
|
2852
|
-
return /* @__PURE__ */
|
|
3021
|
+
return /* @__PURE__ */ React9.createElement(Box9, { p: 3 }, /* @__PURE__ */ React9.createElement(Paper5, { sx: { p: 3 } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React9.createElement(Typography9, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React9.createElement(Typography9, { 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.")));
|
|
2853
3022
|
}
|
|
2854
|
-
const pageTabs = /* @__PURE__ */
|
|
3023
|
+
const pageTabs = /* @__PURE__ */ React9.createElement(
|
|
2855
3024
|
Tabs2,
|
|
2856
3025
|
{
|
|
2857
3026
|
value: activeTab,
|
|
@@ -2864,12 +3033,13 @@ var init_LiteLLMPage = __esm({
|
|
|
2864
3033
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
2865
3034
|
}
|
|
2866
3035
|
},
|
|
2867
|
-
/* @__PURE__ */
|
|
2868
|
-
/* @__PURE__ */
|
|
2869
|
-
/* @__PURE__ */
|
|
2870
|
-
|
|
3036
|
+
/* @__PURE__ */ React9.createElement(Tab2, { label: "Overview", value: "overview" }),
|
|
3037
|
+
/* @__PURE__ */ React9.createElement(Tab2, { label: "Keys", value: "keys" }),
|
|
3038
|
+
/* @__PURE__ */ React9.createElement(Tab2, { label: "Teams", value: "teams" }),
|
|
3039
|
+
/* @__PURE__ */ React9.createElement(Tab2, { label: "Models", value: "models" }),
|
|
3040
|
+
userInfo.can_view_audit && /* @__PURE__ */ React9.createElement(Tab2, { label: "Audit Log", value: "audit" })
|
|
2871
3041
|
);
|
|
2872
|
-
return /* @__PURE__ */
|
|
3042
|
+
return /* @__PURE__ */ React9.createElement(Box9, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React9.createElement(
|
|
2873
3043
|
DashboardHeader,
|
|
2874
3044
|
{
|
|
2875
3045
|
userInfo,
|
|
@@ -2879,7 +3049,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2879
3049
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
2880
3050
|
tabs: pageTabs
|
|
2881
3051
|
}
|
|
2882
|
-
), activeTab === "overview" && /* @__PURE__ */
|
|
3052
|
+
), activeTab === "overview" && /* @__PURE__ */ React9.createElement(
|
|
2883
3053
|
UsageStats,
|
|
2884
3054
|
{
|
|
2885
3055
|
usage: usage ?? null,
|
|
@@ -2889,7 +3059,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2889
3059
|
loading: usageLoading,
|
|
2890
3060
|
userInfo
|
|
2891
3061
|
}
|
|
2892
|
-
), activeTab === "keys" && /* @__PURE__ */
|
|
3062
|
+
), activeTab === "keys" && /* @__PURE__ */ React9.createElement(
|
|
2893
3063
|
KeysTable,
|
|
2894
3064
|
{
|
|
2895
3065
|
keys: keys ?? [],
|
|
@@ -2903,7 +3073,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2903
3073
|
onDeleteKey: handleDeleteKey,
|
|
2904
3074
|
onPruneExpiredKeys: handlePruneExpiredKeys
|
|
2905
3075
|
}
|
|
2906
|
-
), activeTab === "teams" && /* @__PURE__ */
|
|
3076
|
+
), activeTab === "teams" && /* @__PURE__ */ React9.createElement(
|
|
2907
3077
|
TeamUsage,
|
|
2908
3078
|
{
|
|
2909
3079
|
teams: teams ?? [],
|
|
@@ -2914,7 +3084,14 @@ var init_LiteLLMPage = __esm({
|
|
|
2914
3084
|
},
|
|
2915
3085
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false
|
|
2916
3086
|
}
|
|
2917
|
-
), activeTab === "
|
|
3087
|
+
), activeTab === "models" && /* @__PURE__ */ React9.createElement(
|
|
3088
|
+
ModelsTable,
|
|
3089
|
+
{
|
|
3090
|
+
allModels: allModels ?? [],
|
|
3091
|
+
teams: teams ?? [],
|
|
3092
|
+
loading: modelsLoading
|
|
3093
|
+
}
|
|
3094
|
+
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React9.createElement(AuditLog, { api }), /* @__PURE__ */ React9.createElement(
|
|
2918
3095
|
GenerateKeyDialog,
|
|
2919
3096
|
{
|
|
2920
3097
|
open: generateDialogOpen,
|
|
@@ -2927,15 +3104,15 @@ var init_LiteLLMPage = __esm({
|
|
|
2927
3104
|
onGenerateKey: handleGenerateKey,
|
|
2928
3105
|
onGetConfig: () => api.getConfig()
|
|
2929
3106
|
}
|
|
2930
|
-
), /* @__PURE__ */
|
|
2931
|
-
|
|
3107
|
+
), /* @__PURE__ */ React9.createElement(
|
|
3108
|
+
Snackbar2,
|
|
2932
3109
|
{
|
|
2933
3110
|
open: !!snackbar,
|
|
2934
3111
|
autoHideDuration: 5e3,
|
|
2935
3112
|
onClose: () => setSnackbar(null),
|
|
2936
3113
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
2937
3114
|
},
|
|
2938
|
-
snackbar ? /* @__PURE__ */
|
|
3115
|
+
snackbar ? /* @__PURE__ */ React9.createElement(Alert4, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
2939
3116
|
));
|
|
2940
3117
|
};
|
|
2941
3118
|
}
|
|
@@ -2943,7 +3120,7 @@ var init_LiteLLMPage = __esm({
|
|
|
2943
3120
|
|
|
2944
3121
|
// src/plugin.tsx
|
|
2945
3122
|
init_api();
|
|
2946
|
-
import
|
|
3123
|
+
import React10 from "react";
|
|
2947
3124
|
import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
|
|
2948
3125
|
import {
|
|
2949
3126
|
createFrontendPlugin,
|
|
@@ -2962,10 +3139,10 @@ var liteLlmPage = PageBlueprint.make({
|
|
|
2962
3139
|
params: {
|
|
2963
3140
|
path: "/litellm",
|
|
2964
3141
|
title: "LiteLLM",
|
|
2965
|
-
icon: /* @__PURE__ */
|
|
3142
|
+
icon: /* @__PURE__ */ React10.createElement(TrendingUpIcon, null),
|
|
2966
3143
|
loader: async () => {
|
|
2967
3144
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
2968
|
-
return /* @__PURE__ */
|
|
3145
|
+
return /* @__PURE__ */ React10.createElement(LiteLLMPage2, null);
|
|
2969
3146
|
}
|
|
2970
3147
|
}
|
|
2971
3148
|
});
|
|
@@ -2984,16 +3161,16 @@ init_TeamUsage();
|
|
|
2984
3161
|
// src/components/LiteLLMHomeWidget.tsx
|
|
2985
3162
|
init_api();
|
|
2986
3163
|
init_format();
|
|
2987
|
-
import
|
|
3164
|
+
import React11, { useState as useState8, useEffect as useEffect2 } from "react";
|
|
2988
3165
|
import Paper6 from "@mui/material/Paper";
|
|
2989
|
-
import
|
|
2990
|
-
import
|
|
2991
|
-
import
|
|
2992
|
-
import
|
|
2993
|
-
import
|
|
3166
|
+
import Box10 from "@mui/material/Box";
|
|
3167
|
+
import Typography10 from "@mui/material/Typography";
|
|
3168
|
+
import FormControl2 from "@mui/material/FormControl";
|
|
3169
|
+
import Select2 from "@mui/material/Select";
|
|
3170
|
+
import MenuItem5 from "@mui/material/MenuItem";
|
|
2994
3171
|
import Grid2 from "@mui/material/Grid";
|
|
2995
3172
|
import CircularProgress5 from "@mui/material/CircularProgress";
|
|
2996
|
-
import
|
|
3173
|
+
import Alert5 from "@mui/material/Alert";
|
|
2997
3174
|
import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
|
|
2998
3175
|
import { useApi as useApi2 } from "@backstage/core-plugin-api";
|
|
2999
3176
|
function presetToDateRange(preset) {
|
|
@@ -3008,18 +3185,18 @@ function presetToDateRange(preset) {
|
|
|
3008
3185
|
}
|
|
3009
3186
|
return { start, end };
|
|
3010
3187
|
}
|
|
3011
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
3188
|
+
var Kpi = ({ label, value }) => /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Typography10, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React11.createElement(Typography10, { variant: "subtitle1", fontWeight: 600 }, value));
|
|
3012
3189
|
var LiteLLMHomeWidget = ({
|
|
3013
3190
|
defaultPeriod = "7d",
|
|
3014
3191
|
title = "LiteLLM Usage"
|
|
3015
3192
|
}) => {
|
|
3016
3193
|
const api = useApi2(liteLlmApiRef);
|
|
3017
|
-
const [period, setPeriod] =
|
|
3018
|
-
const [loading, setLoading] =
|
|
3019
|
-
const [usageError, setUsageError] =
|
|
3020
|
-
const [keysError, setKeysError] =
|
|
3021
|
-
const [usage, setUsage] =
|
|
3022
|
-
const [keys, setKeys] =
|
|
3194
|
+
const [period, setPeriod] = useState8(defaultPeriod);
|
|
3195
|
+
const [loading, setLoading] = useState8(true);
|
|
3196
|
+
const [usageError, setUsageError] = useState8(null);
|
|
3197
|
+
const [keysError, setKeysError] = useState8(null);
|
|
3198
|
+
const [usage, setUsage] = useState8(null);
|
|
3199
|
+
const [keys, setKeys] = useState8([]);
|
|
3023
3200
|
useEffect2(() => {
|
|
3024
3201
|
let cancelled = false;
|
|
3025
3202
|
setLoading(true);
|
|
@@ -3057,17 +3234,17 @@ var LiteLLMHomeWidget = ({
|
|
|
3057
3234
|
spend: d.spend
|
|
3058
3235
|
}));
|
|
3059
3236
|
const hasSparkline = dailyData.length > 0;
|
|
3060
|
-
return /* @__PURE__ */
|
|
3061
|
-
|
|
3237
|
+
return /* @__PURE__ */ React11.createElement(Paper6, { sx: { p: 2 } }, /* @__PURE__ */ React11.createElement(Box10, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React11.createElement(Typography10, { variant: "h6" }, title), /* @__PURE__ */ React11.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React11.createElement(
|
|
3238
|
+
Select2,
|
|
3062
3239
|
{
|
|
3063
3240
|
value: period,
|
|
3064
3241
|
onChange: (e) => setPeriod(e.target.value),
|
|
3065
3242
|
displayEmpty: true
|
|
3066
3243
|
},
|
|
3067
|
-
/* @__PURE__ */
|
|
3068
|
-
/* @__PURE__ */
|
|
3069
|
-
/* @__PURE__ */
|
|
3070
|
-
))), loading && /* @__PURE__ */
|
|
3244
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "today" }, "Today"),
|
|
3245
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "7d" }, "7d"),
|
|
3246
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "30d" }, "30d")
|
|
3247
|
+
))), loading && /* @__PURE__ */ React11.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React11.createElement(CircularProgress5, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React11.createElement(Alert5, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React11.createElement(React11.Fragment, null, partialFailure && /* @__PURE__ */ React11.createElement(Alert5, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React11.createElement(Grid2, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React11.createElement(Box10, { height: 120 }, /* @__PURE__ */ React11.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React11.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React11.createElement(
|
|
3071
3248
|
Area3,
|
|
3072
3249
|
{
|
|
3073
3250
|
type: "monotone",
|