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