@acarmisc/backstage-plugin-litellm 0.36.0 → 0.38.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/api.d.ts +3 -1
- package/dist/components/InsightsTab.d.ts +12 -0
- package/dist/connect.d.ts +2 -0
- package/dist/index.cjs.js +363 -294
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.esm.js +401 -332
- package/dist/index.esm.js.map +4 -4
- package/dist/insights.d.ts +8 -0
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.esm.js
CHANGED
|
@@ -295,6 +295,17 @@ var init_api = __esm({
|
|
|
295
295
|
end_date: endDate
|
|
296
296
|
});
|
|
297
297
|
}
|
|
298
|
+
async getSpendLogs(params) {
|
|
299
|
+
const strParams = {
|
|
300
|
+
start_date: params.startDate,
|
|
301
|
+
end_date: params.endDate
|
|
302
|
+
};
|
|
303
|
+
if (params.userId) strParams.userId = params.userId;
|
|
304
|
+
if (params.teamId) strParams.teamId = params.teamId;
|
|
305
|
+
if (params.page !== void 0) strParams.page = String(params.page);
|
|
306
|
+
if (params.pageSize !== void 0) strParams.page_size = String(params.pageSize);
|
|
307
|
+
return this.get("/insights/spend-logs", strParams);
|
|
308
|
+
}
|
|
298
309
|
async getConfig() {
|
|
299
310
|
return this.get("/config");
|
|
300
311
|
}
|
|
@@ -5664,271 +5675,95 @@ var init_ModelsTable = __esm({
|
|
|
5664
5675
|
}
|
|
5665
5676
|
});
|
|
5666
5677
|
|
|
5667
|
-
// src/
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
import Table6 from "@mui/material/Table";
|
|
5672
|
-
import TableBody6 from "@mui/material/TableBody";
|
|
5673
|
-
import TableCell6 from "@mui/material/TableCell";
|
|
5674
|
-
import TableContainer6 from "@mui/material/TableContainer";
|
|
5675
|
-
import TableHead6 from "@mui/material/TableHead";
|
|
5676
|
-
import TableRow6 from "@mui/material/TableRow";
|
|
5677
|
-
import TablePagination from "@mui/material/TablePagination";
|
|
5678
|
-
import TextField6 from "@mui/material/TextField";
|
|
5679
|
-
import MenuItem5 from "@mui/material/MenuItem";
|
|
5680
|
-
import Skeleton6 from "@mui/material/Skeleton";
|
|
5681
|
-
import Collapse3 from "@mui/material/Collapse";
|
|
5682
|
-
import IconButton7 from "@mui/material/IconButton";
|
|
5683
|
-
import Alert5 from "@mui/material/Alert";
|
|
5684
|
-
import { alpha as alpha12 } from "@mui/material/styles";
|
|
5685
|
-
import { KeyboardArrowDown } from "@mui/icons-material";
|
|
5686
|
-
import { useAsync } from "react-use";
|
|
5687
|
-
import { Fragment as Fragment9, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
5688
|
-
function actionTone(action) {
|
|
5689
|
-
if (!action) return "neutral";
|
|
5690
|
-
for (const [key, tone] of Object.entries(ACTION_TONES)) {
|
|
5691
|
-
if (action.toLowerCase().includes(key)) return tone;
|
|
5692
|
-
}
|
|
5693
|
-
return "info";
|
|
5694
|
-
}
|
|
5695
|
-
function prettyTableName(name) {
|
|
5696
|
-
if (!name) return "\u2014";
|
|
5697
|
-
return TABLE_LABELS[name] ?? name;
|
|
5698
|
-
}
|
|
5699
|
-
function formatDateTime(iso) {
|
|
5700
|
-
try {
|
|
5701
|
-
return new Date(iso).toLocaleString();
|
|
5702
|
-
} catch {
|
|
5703
|
-
return iso;
|
|
5704
|
-
}
|
|
5705
|
-
}
|
|
5706
|
-
function renderAuditLogBody(loading, entries) {
|
|
5707
|
-
if (loading) {
|
|
5708
|
-
return /* @__PURE__ */ jsx17(TableRow6, { children: /* @__PURE__ */ jsx17(TableCell6, { colSpan: 6, sx: { py: 2 }, children: /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 160 }) }) });
|
|
5709
|
-
}
|
|
5710
|
-
if (entries.length === 0) {
|
|
5711
|
-
return /* @__PURE__ */ jsx17(TableRow6, { children: /* @__PURE__ */ jsx17(TableCell6, { colSpan: 6, children: /* @__PURE__ */ jsx17(
|
|
5712
|
-
EmptyState,
|
|
5713
|
-
{
|
|
5714
|
-
message: "No audit events found",
|
|
5715
|
-
hint: "Try widening the filters or a different time window."
|
|
5716
|
-
}
|
|
5717
|
-
) }) });
|
|
5718
|
-
}
|
|
5719
|
-
return entries.map((entry) => /* @__PURE__ */ jsx17(DetailRow, { entry }, entry.id));
|
|
5720
|
-
}
|
|
5721
|
-
var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
5722
|
-
var init_AuditLog = __esm({
|
|
5723
|
-
"src/components/AuditLog.tsx"() {
|
|
5678
|
+
// src/insights.ts
|
|
5679
|
+
var costPer1k, summarizeUsage, scorePractices, modelOpportunities;
|
|
5680
|
+
var init_insights = __esm({
|
|
5681
|
+
"src/insights.ts"() {
|
|
5724
5682
|
"use strict";
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
blocked: "danger",
|
|
5731
|
-
unblocked: "success"
|
|
5683
|
+
costPer1k = (model, spend, tokens, models) => {
|
|
5684
|
+
const info = models?.find((m) => m.model_name === model);
|
|
5685
|
+
const listed = info?.input_cost_per_token != null && info?.output_cost_per_token != null ? (info.input_cost_per_token + info.output_cost_per_token) / 2 * 1e3 : null;
|
|
5686
|
+
if (listed !== null) return listed;
|
|
5687
|
+
return tokens > 0 ? spend / tokens * 1e3 : null;
|
|
5732
5688
|
};
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5689
|
+
summarizeUsage = (usage, models, premiumThresholdPer1k = 0.01) => {
|
|
5690
|
+
if (!usage) {
|
|
5691
|
+
return {
|
|
5692
|
+
costPer1kTokens: null,
|
|
5693
|
+
costPerRequest: null,
|
|
5694
|
+
outputInputRatio: null,
|
|
5695
|
+
retryWasteUsd: 0,
|
|
5696
|
+
retryWasteShare: 0,
|
|
5697
|
+
successRate: null,
|
|
5698
|
+
premiumShare: 0,
|
|
5699
|
+
premiumSpend: 0,
|
|
5700
|
+
topModelConcentration: 0,
|
|
5701
|
+
projectedMonthSpend: null,
|
|
5702
|
+
burnMultiple: null
|
|
5703
|
+
};
|
|
5704
|
+
}
|
|
5705
|
+
const {
|
|
5706
|
+
total_spend: spend,
|
|
5707
|
+
total_tokens: tokens,
|
|
5708
|
+
prompt_tokens: prompt,
|
|
5709
|
+
completion_tokens: completion,
|
|
5710
|
+
api_requests: reqs,
|
|
5711
|
+
successful_requests: ok,
|
|
5712
|
+
failed_requests: failed,
|
|
5713
|
+
usage_by_model: byModel
|
|
5714
|
+
} = usage;
|
|
5715
|
+
const wasteShare = reqs > 0 ? failed / reqs : 0;
|
|
5716
|
+
const entries = Object.entries(byModel ?? {});
|
|
5717
|
+
const topSpend = entries.reduce((m, [, d]) => Math.max(m, d.total_spend), 0);
|
|
5718
|
+
const premiumSpend = entries.filter(([name, d]) => (costPer1k(name, d.total_spend, d.total_tokens, models) ?? 0) > premiumThresholdPer1k).reduce((s, [, d]) => s + d.total_spend, 0);
|
|
5719
|
+
const days = usage.daily_usage?.length ?? 0;
|
|
5720
|
+
const projected = days > 0 ? spend / days * 30 : null;
|
|
5721
|
+
return {
|
|
5722
|
+
costPer1kTokens: tokens > 0 ? spend / tokens * 1e3 : null,
|
|
5723
|
+
costPerRequest: reqs > 0 ? spend / reqs : null,
|
|
5724
|
+
outputInputRatio: prompt > 0 ? completion / prompt : null,
|
|
5725
|
+
retryWasteUsd: spend * wasteShare,
|
|
5726
|
+
retryWasteShare: wasteShare,
|
|
5727
|
+
successRate: reqs > 0 ? ok / reqs : null,
|
|
5728
|
+
premiumShare: spend > 0 ? premiumSpend / spend : 0,
|
|
5729
|
+
premiumSpend,
|
|
5730
|
+
topModelConcentration: spend > 0 ? topSpend / spend : 0,
|
|
5731
|
+
projectedMonthSpend: projected,
|
|
5732
|
+
burnMultiple: projected !== null && spend > 0 ? projected / spend : null
|
|
5733
|
+
};
|
|
5739
5734
|
};
|
|
5740
|
-
|
|
5741
|
-
const
|
|
5742
|
-
const
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
color: theme.palette.text.secondary,
|
|
5754
|
-
transform: open ? "rotate(180deg)" : "none",
|
|
5755
|
-
transition: theme.transitions.create("transform")
|
|
5756
|
-
}),
|
|
5757
|
-
children: /* @__PURE__ */ jsx17(KeyboardArrowDown, { fontSize: "small" })
|
|
5758
|
-
}
|
|
5759
|
-
) }),
|
|
5760
|
-
/* @__PURE__ */ jsx17(TableCell6, { sx: { whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsx17(Typography14, { variant: "body2", color: "text.secondary", children: formatDateTime(entry.updated_at) }) }),
|
|
5761
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: entry.action && /* @__PURE__ */ jsx17(StatusPill, { label: entry.action, tone: actionTone(entry.action) }) }),
|
|
5762
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: /* @__PURE__ */ jsx17(Typography14, { variant: "body2", children: prettyTableName(entry.table_name) }) }),
|
|
5763
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: /* @__PURE__ */ jsx17(
|
|
5764
|
-
Typography14,
|
|
5765
|
-
{
|
|
5766
|
-
variant: "body2",
|
|
5767
|
-
component: "code",
|
|
5768
|
-
color: "text.secondary",
|
|
5769
|
-
title: entry.object_id ?? void 0,
|
|
5770
|
-
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 },
|
|
5771
|
-
children: entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
5772
|
-
}
|
|
5773
|
-
) }),
|
|
5774
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: entry.changed_by ?? "\u2014" })
|
|
5775
|
-
] }),
|
|
5776
|
-
hasDetail && /* @__PURE__ */ jsx17(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } }, children: /* @__PURE__ */ jsx17(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } }, children: /* @__PURE__ */ jsx17(Collapse3, { in: open, unmountOnExit: true, children: /* @__PURE__ */ jsxs16(
|
|
5777
|
-
Box16,
|
|
5778
|
-
{
|
|
5779
|
-
display: "flex",
|
|
5780
|
-
gap: 2,
|
|
5781
|
-
flexWrap: "wrap",
|
|
5782
|
-
sx: (theme) => ({
|
|
5783
|
-
p: 2,
|
|
5784
|
-
mb: 1.5,
|
|
5785
|
-
borderRadius: 1.5,
|
|
5786
|
-
bgcolor: alpha12(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
5787
|
-
}),
|
|
5788
|
-
children: [
|
|
5789
|
-
entry.before_value && /* @__PURE__ */ jsxs16(Box16, { flex: 1, minWidth: 200, children: [
|
|
5790
|
-
/* @__PURE__ */ jsx17(
|
|
5791
|
-
Typography14,
|
|
5792
|
-
{
|
|
5793
|
-
variant: "caption",
|
|
5794
|
-
color: "text.secondary",
|
|
5795
|
-
display: "block",
|
|
5796
|
-
mb: 0.75,
|
|
5797
|
-
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" },
|
|
5798
|
-
children: "Before"
|
|
5799
|
-
}
|
|
5800
|
-
),
|
|
5801
|
-
/* @__PURE__ */ jsx17(Typography14, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 }, children: JSON.stringify(entry.before_value, null, 2) })
|
|
5802
|
-
] }),
|
|
5803
|
-
entry.updated_values && /* @__PURE__ */ jsxs16(Box16, { flex: 1, minWidth: 200, children: [
|
|
5804
|
-
/* @__PURE__ */ jsx17(
|
|
5805
|
-
Typography14,
|
|
5806
|
-
{
|
|
5807
|
-
variant: "caption",
|
|
5808
|
-
color: "text.secondary",
|
|
5809
|
-
display: "block",
|
|
5810
|
-
mb: 0.75,
|
|
5811
|
-
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" },
|
|
5812
|
-
children: "After"
|
|
5813
|
-
}
|
|
5814
|
-
),
|
|
5815
|
-
/* @__PURE__ */ jsx17(Typography14, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 }, children: JSON.stringify(entry.updated_values, null, 2) })
|
|
5816
|
-
] })
|
|
5817
|
-
]
|
|
5818
|
-
}
|
|
5819
|
-
) }) }) })
|
|
5820
|
-
] });
|
|
5735
|
+
scorePractices = (summary, keyStats) => {
|
|
5736
|
+
const waste = summary.retryWasteShare <= 0.01 ? 30 : summary.retryWasteShare <= 0.05 ? 20 : summary.retryWasteShare <= 0.1 ? 10 : 0;
|
|
5737
|
+
const premium = summary.premiumShare <= 0.2 ? 25 : summary.premiumShare <= 0.5 ? 15 : summary.premiumShare <= 0.8 ? 5 : 0;
|
|
5738
|
+
const conc = summary.topModelConcentration <= 0.5 ? 20 : summary.topModelConcentration <= 0.8 ? 10 : 0;
|
|
5739
|
+
const bad = keyStats.totalKeys > 0 ? (keyStats.idleKeys + keyStats.uncappedKeys) / keyStats.totalKeys : 0;
|
|
5740
|
+
const hygiene = keyStats.totalKeys === 0 ? 25 : bad === 0 ? 25 : bad <= 0.2 ? 15 : bad <= 0.5 ? 5 : 0;
|
|
5741
|
+
const checks = [
|
|
5742
|
+
{ id: "retry-waste", label: "Low retry waste", points: waste, maxPoints: 30, detail: `${(summary.retryWasteShare * 100).toFixed(1)}% of requests failed` },
|
|
5743
|
+
{ id: "premium-mix", label: "Balanced model mix", points: premium, maxPoints: 25, detail: `${(summary.premiumShare * 100).toFixed(1)}% of spend on premium models` },
|
|
5744
|
+
{ id: "concentration", label: "No single-model lock-in", points: conc, maxPoints: 20, detail: `Top model is ${(summary.topModelConcentration * 100).toFixed(1)}% of spend` },
|
|
5745
|
+
{ id: "key-hygiene", label: "Key hygiene", points: hygiene, maxPoints: 25, detail: `${keyStats.idleKeys} idle, ${keyStats.uncappedKeys} uncapped of ${keyStats.totalKeys} keys` }
|
|
5746
|
+
];
|
|
5747
|
+
return { total: checks.reduce((s, c) => s + c.points, 0), checks };
|
|
5821
5748
|
};
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
const
|
|
5825
|
-
const
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
const entries = value?.audit_logs ?? [];
|
|
5835
|
-
const total = value?.total ?? 0;
|
|
5836
|
-
return /* @__PURE__ */ jsxs16(
|
|
5837
|
-
SectionCard,
|
|
5838
|
-
{
|
|
5839
|
-
title: "Audit Log",
|
|
5840
|
-
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
5841
|
-
flush: true,
|
|
5842
|
-
actions: /* @__PURE__ */ jsxs16(Fragment9, { children: [
|
|
5843
|
-
/* @__PURE__ */ jsxs16(
|
|
5844
|
-
TextField6,
|
|
5845
|
-
{
|
|
5846
|
-
size: "small",
|
|
5847
|
-
label: "Action",
|
|
5848
|
-
select: true,
|
|
5849
|
-
value: filters.action ?? "",
|
|
5850
|
-
onChange: (e) => {
|
|
5851
|
-
setFilters((f) => ({ ...f, action: e.target.value || void 0 }));
|
|
5852
|
-
setPage(0);
|
|
5853
|
-
},
|
|
5854
|
-
sx: { minWidth: 150 },
|
|
5855
|
-
children: [
|
|
5856
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "", children: "All actions" }),
|
|
5857
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "created", children: "Created" }),
|
|
5858
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "updated", children: "Updated" }),
|
|
5859
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "deleted", children: "Deleted" }),
|
|
5860
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "blocked", children: "Blocked" })
|
|
5861
|
-
]
|
|
5862
|
-
}
|
|
5863
|
-
),
|
|
5864
|
-
/* @__PURE__ */ jsxs16(
|
|
5865
|
-
TextField6,
|
|
5866
|
-
{
|
|
5867
|
-
size: "small",
|
|
5868
|
-
label: "Table",
|
|
5869
|
-
select: true,
|
|
5870
|
-
value: filters.table_name ?? "",
|
|
5871
|
-
onChange: (e) => {
|
|
5872
|
-
setFilters((f) => ({ ...f, table_name: e.target.value || void 0 }));
|
|
5873
|
-
setPage(0);
|
|
5874
|
-
},
|
|
5875
|
-
sx: { minWidth: 150 },
|
|
5876
|
-
children: [
|
|
5877
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "", children: "All tables" }),
|
|
5878
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "LiteLLM_VerificationToken", children: "Key" }),
|
|
5879
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "LiteLLM_TeamTable", children: "Team" }),
|
|
5880
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "LiteLLM_TeamMembership", children: "Team member" }),
|
|
5881
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "LiteLLM_ObjectPermissionTable", children: "Team access (KB / MCP)" }),
|
|
5882
|
-
/* @__PURE__ */ jsx17(MenuItem5, { value: "LiteLLM_UserTable", children: "User" })
|
|
5883
|
-
]
|
|
5884
|
-
}
|
|
5885
|
-
),
|
|
5886
|
-
/* @__PURE__ */ jsx17(
|
|
5887
|
-
TextField6,
|
|
5888
|
-
{
|
|
5889
|
-
size: "small",
|
|
5890
|
-
label: "Changed by",
|
|
5891
|
-
value: filters.changed_by ?? "",
|
|
5892
|
-
onChange: (e) => {
|
|
5893
|
-
setFilters((f) => ({ ...f, changed_by: e.target.value || void 0 }));
|
|
5894
|
-
setPage(0);
|
|
5895
|
-
},
|
|
5896
|
-
sx: { minWidth: 180 }
|
|
5897
|
-
}
|
|
5898
|
-
)
|
|
5899
|
-
] }),
|
|
5900
|
-
children: [
|
|
5901
|
-
error && /* @__PURE__ */ jsx17(Box16, { px: 2.5, pb: 2, children: /* @__PURE__ */ jsx17(Alert5, { severity: "error", children: error.message }) }),
|
|
5902
|
-
/* @__PURE__ */ jsx17(TableContainer6, { children: /* @__PURE__ */ jsxs16(Table6, { size: "small", sx: dataTableSx, children: [
|
|
5903
|
-
/* @__PURE__ */ jsx17(TableHead6, { children: /* @__PURE__ */ jsxs16(TableRow6, { children: [
|
|
5904
|
-
/* @__PURE__ */ jsx17(TableCell6, { sx: { width: 40 } }),
|
|
5905
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: "Time" }),
|
|
5906
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: "Action" }),
|
|
5907
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: "Table" }),
|
|
5908
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: "Object ID" }),
|
|
5909
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: "Changed By" })
|
|
5910
|
-
] }) }),
|
|
5911
|
-
/* @__PURE__ */ jsx17(TableBody6, { children: renderAuditLogBody(loading, entries) })
|
|
5912
|
-
] }) }),
|
|
5913
|
-
/* @__PURE__ */ jsx17(
|
|
5914
|
-
TablePagination,
|
|
5915
|
-
{
|
|
5916
|
-
component: "div",
|
|
5917
|
-
count: total,
|
|
5918
|
-
page,
|
|
5919
|
-
onPageChange: (_, p) => setPage(p),
|
|
5920
|
-
rowsPerPage: pageSize,
|
|
5921
|
-
onRowsPerPageChange: (e) => {
|
|
5922
|
-
setPageSize(Number(e.target.value));
|
|
5923
|
-
setPage(0);
|
|
5924
|
-
},
|
|
5925
|
-
rowsPerPageOptions: [10, 25, 50],
|
|
5926
|
-
sx: (theme) => ({ borderTop: `1px solid ${theme.palette.divider}` })
|
|
5927
|
-
}
|
|
5928
|
-
)
|
|
5929
|
-
]
|
|
5749
|
+
modelOpportunities = (usage, models, premiumThresholdPer1k = 0.01) => {
|
|
5750
|
+
if (!usage) return [];
|
|
5751
|
+
const entries = Object.entries(usage.usage_by_model ?? {});
|
|
5752
|
+
const overallRate = usage.api_requests > 0 ? usage.failed_requests / usage.api_requests : 0;
|
|
5753
|
+
return entries.map(([model, d]) => {
|
|
5754
|
+
const failureRate = d.api_requests > 0 ? d.failed_requests / d.api_requests : null;
|
|
5755
|
+
const avgCostPerRequest = d.api_requests > 0 ? d.total_spend / d.api_requests : 0;
|
|
5756
|
+
const completionShare = d.total_tokens > 0 ? d.completion_tokens / d.total_tokens : 0;
|
|
5757
|
+
const per1k = costPer1k(model, d.total_spend, d.total_tokens, models) ?? 0;
|
|
5758
|
+
const spendShare = usage.total_spend > 0 ? d.total_spend / usage.total_spend : 0;
|
|
5759
|
+
if (failureRate !== null && overallRate > 0 && failureRate > 2 * overallRate && d.api_requests >= 10) {
|
|
5760
|
+
return { model, spend: d.total_spend, requests: d.api_requests, failureRate, avgCostPerRequest, flag: "failure-outlier", hint: `Fails ${(failureRate * 100).toFixed(1)}% vs ${(overallRate * 100).toFixed(1)}% overall` };
|
|
5930
5761
|
}
|
|
5931
|
-
|
|
5762
|
+
if (per1k > premiumThresholdPer1k && completionShare < 0.3 && spendShare > 0.05) {
|
|
5763
|
+
return { model, spend: d.total_spend, requests: d.api_requests, failureRate, avgCostPerRequest, flag: "downgrade-candidate", hint: `$${per1k.toFixed(3)}/1k with ${(completionShare * 100).toFixed(0)}% output \u2014 try a cheaper model` };
|
|
5764
|
+
}
|
|
5765
|
+
return { model, spend: d.total_spend, requests: d.api_requests, failureRate, avgCostPerRequest, flag: "ok", hint: "No action" };
|
|
5766
|
+
});
|
|
5932
5767
|
};
|
|
5933
5768
|
}
|
|
5934
5769
|
});
|
|
@@ -5953,6 +5788,225 @@ var init_dates = __esm({
|
|
|
5953
5788
|
}
|
|
5954
5789
|
});
|
|
5955
5790
|
|
|
5791
|
+
// src/components/InsightsTab.tsx
|
|
5792
|
+
import { useMemo as useMemo8 } from "react";
|
|
5793
|
+
import Box16 from "@mui/material/Box";
|
|
5794
|
+
import Grid2 from "@mui/material/Grid";
|
|
5795
|
+
import Typography14 from "@mui/material/Typography";
|
|
5796
|
+
import Alert5 from "@mui/material/Alert";
|
|
5797
|
+
import Button7 from "@mui/material/Button";
|
|
5798
|
+
import Table6 from "@mui/material/Table";
|
|
5799
|
+
import TableBody6 from "@mui/material/TableBody";
|
|
5800
|
+
import TableCell6 from "@mui/material/TableCell";
|
|
5801
|
+
import TableContainer6 from "@mui/material/TableContainer";
|
|
5802
|
+
import TableHead6 from "@mui/material/TableHead";
|
|
5803
|
+
import TableRow6 from "@mui/material/TableRow";
|
|
5804
|
+
import Skeleton6 from "@mui/material/Skeleton";
|
|
5805
|
+
import { useAsyncRetry } from "react-use";
|
|
5806
|
+
import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
5807
|
+
var FLAG_TONE, FLAG_LABEL, fmtRatio, fmtPct1, failedRow, InsightsTab;
|
|
5808
|
+
var init_InsightsTab = __esm({
|
|
5809
|
+
"src/components/InsightsTab.tsx"() {
|
|
5810
|
+
"use strict";
|
|
5811
|
+
init_insights();
|
|
5812
|
+
init_format();
|
|
5813
|
+
init_dates();
|
|
5814
|
+
init_ui();
|
|
5815
|
+
FLAG_TONE = {
|
|
5816
|
+
"failure-outlier": "danger",
|
|
5817
|
+
"downgrade-candidate": "warning",
|
|
5818
|
+
ok: "success"
|
|
5819
|
+
};
|
|
5820
|
+
FLAG_LABEL = {
|
|
5821
|
+
"failure-outlier": "Failure outlier",
|
|
5822
|
+
"downgrade-candidate": "Downgrade candidate",
|
|
5823
|
+
ok: "OK"
|
|
5824
|
+
};
|
|
5825
|
+
fmtRatio = (n) => n === null || Number.isNaN(n) ? "\u2014" : `${n.toFixed(2)}\xD7`;
|
|
5826
|
+
fmtPct1 = (n) => n === null || Number.isNaN(n) ? "\u2014" : `${(n * 100).toFixed(1)}%`;
|
|
5827
|
+
failedRow = (status) => /fail|error/i.test(status ?? "");
|
|
5828
|
+
InsightsTab = ({
|
|
5829
|
+
api,
|
|
5830
|
+
usage,
|
|
5831
|
+
models,
|
|
5832
|
+
keys,
|
|
5833
|
+
dateRange,
|
|
5834
|
+
loading
|
|
5835
|
+
}) => {
|
|
5836
|
+
const summary = useMemo8(() => summarizeUsage(usage, models), [usage, models]);
|
|
5837
|
+
const keyStats = useMemo8(
|
|
5838
|
+
() => ({
|
|
5839
|
+
idleKeys: keys.filter((k) => (k.spend ?? 0) === 0).length,
|
|
5840
|
+
uncappedKeys: keys.filter((k) => k.max_budget === void 0 || k.max_budget === null).length,
|
|
5841
|
+
totalKeys: keys.length
|
|
5842
|
+
}),
|
|
5843
|
+
[keys]
|
|
5844
|
+
);
|
|
5845
|
+
const score = useMemo8(
|
|
5846
|
+
() => scorePractices(summary, keyStats),
|
|
5847
|
+
[summary, keyStats]
|
|
5848
|
+
);
|
|
5849
|
+
const opportunities = useMemo8(() => {
|
|
5850
|
+
const opps = modelOpportunities(usage, models);
|
|
5851
|
+
return [...opps].sort(
|
|
5852
|
+
(a, b) => (a.flag === "ok" ? 1 : 0) - (b.flag === "ok" ? 1 : 0)
|
|
5853
|
+
);
|
|
5854
|
+
}, [usage, models]);
|
|
5855
|
+
const {
|
|
5856
|
+
value: spendLogs,
|
|
5857
|
+
loading: logsLoading,
|
|
5858
|
+
error: logsError,
|
|
5859
|
+
retry: retryLogs
|
|
5860
|
+
} = useAsyncRetry(async () => {
|
|
5861
|
+
return api.getSpendLogs({
|
|
5862
|
+
startDate: toLocalDay(dateRange.start),
|
|
5863
|
+
endDate: toLocalDay(dateRange.end),
|
|
5864
|
+
pageSize: 1e3
|
|
5865
|
+
});
|
|
5866
|
+
}, [api, dateRange]);
|
|
5867
|
+
const logStats = useMemo8(() => {
|
|
5868
|
+
const rows = spendLogs?.data ?? [];
|
|
5869
|
+
const hits = rows.filter((r) => r.cacheHit);
|
|
5870
|
+
const cachedPromptTokens = hits.reduce((s, r) => s + (r.promptTokens ?? 0), 0);
|
|
5871
|
+
const priced = models.map((m) => m.input_cost_per_token).filter((p) => p !== null && p !== void 0 && p > 0);
|
|
5872
|
+
const avgInputPrice = priced.length > 0 ? priced.reduce((s, p) => s + p, 0) / priced.length : null;
|
|
5873
|
+
return {
|
|
5874
|
+
total: spendLogs?.total ?? rows.length,
|
|
5875
|
+
hitRate: rows.length > 0 ? hits.length / rows.length : null,
|
|
5876
|
+
// Estimate: cached prompt tokens billed at the average listed input price.
|
|
5877
|
+
savedUsd: avgInputPrice !== null ? cachedPromptTokens * avgInputPrice : null,
|
|
5878
|
+
failedWasteUsd: rows.filter((r) => failedRow(r.status)).reduce((s, r) => s + (r.spend ?? 0), 0),
|
|
5879
|
+
truncated: spendLogs?.truncated ?? false
|
|
5880
|
+
};
|
|
5881
|
+
}, [spendLogs, models]);
|
|
5882
|
+
const renderSpendLogsBody = () => {
|
|
5883
|
+
if (logsLoading) return /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 120 });
|
|
5884
|
+
if (logsError) {
|
|
5885
|
+
return /* @__PURE__ */ jsxs16(Alert5, { severity: "error", children: [
|
|
5886
|
+
"Failed to load spend logs: ",
|
|
5887
|
+
logsError.message || "Unknown error",
|
|
5888
|
+
/* @__PURE__ */ jsx17(Box16, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx17(Button7, { size: "small", onClick: retryLogs, children: "Retry" }) })
|
|
5889
|
+
] });
|
|
5890
|
+
}
|
|
5891
|
+
if ((spendLogs?.data ?? []).length === 0) {
|
|
5892
|
+
return /* @__PURE__ */ jsx17(EmptyState, { message: "No spend logs in this period" });
|
|
5893
|
+
}
|
|
5894
|
+
return /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", gap: 3, flexWrap: "wrap" }, children: [
|
|
5895
|
+
/* @__PURE__ */ jsx17(Stat, { label: "Cache-hit rate", value: fmtPct1(logStats.hitRate) }),
|
|
5896
|
+
/* @__PURE__ */ jsx17(
|
|
5897
|
+
Stat,
|
|
5898
|
+
{
|
|
5899
|
+
label: "Cache savings (est.)",
|
|
5900
|
+
value: logStats.savedUsd !== null ? fmtUsd(logStats.savedUsd) : "\u2014"
|
|
5901
|
+
}
|
|
5902
|
+
),
|
|
5903
|
+
/* @__PURE__ */ jsx17(Stat, { label: "Failed-request waste", value: fmtUsd(logStats.failedWasteUsd) }),
|
|
5904
|
+
/* @__PURE__ */ jsx17(Stat, { label: "Log rows", value: fmtInt(logStats.total) }),
|
|
5905
|
+
logStats.truncated && /* @__PURE__ */ jsx17(Typography14, { variant: "caption", color: "text.secondary", children: "Showing the first 1,000 rows \u2014 figures are a lower bound." })
|
|
5906
|
+
] });
|
|
5907
|
+
};
|
|
5908
|
+
const kpis = [
|
|
5909
|
+
{
|
|
5910
|
+
label: "Cost / 1k tokens",
|
|
5911
|
+
value: summary.costPer1kTokens !== null ? fmtUsd(summary.costPer1kTokens) : "\u2014",
|
|
5912
|
+
tone: "accent"
|
|
5913
|
+
},
|
|
5914
|
+
{
|
|
5915
|
+
label: "Cost / request",
|
|
5916
|
+
value: summary.costPerRequest !== null ? fmtUsd(summary.costPerRequest) : "\u2014",
|
|
5917
|
+
tone: "info"
|
|
5918
|
+
},
|
|
5919
|
+
{
|
|
5920
|
+
label: "Out : in ratio",
|
|
5921
|
+
value: fmtRatio(summary.outputInputRatio),
|
|
5922
|
+
tone: "neutral"
|
|
5923
|
+
},
|
|
5924
|
+
{
|
|
5925
|
+
label: "Retry waste",
|
|
5926
|
+
value: fmtUsd(summary.retryWasteUsd),
|
|
5927
|
+
hint: `${fmtPct1(summary.retryWasteShare)} of spend`,
|
|
5928
|
+
tone: "warning"
|
|
5929
|
+
},
|
|
5930
|
+
{
|
|
5931
|
+
label: "Success rate",
|
|
5932
|
+
value: fmtPct1(summary.successRate),
|
|
5933
|
+
tone: "success"
|
|
5934
|
+
}
|
|
5935
|
+
];
|
|
5936
|
+
const renderEfficiencyBody = () => {
|
|
5937
|
+
if (loading) return /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 96 });
|
|
5938
|
+
if (!usage) return /* @__PURE__ */ jsx17(EmptyState, { message: "No usage in this period" });
|
|
5939
|
+
return /* @__PURE__ */ jsx17(MetricStrip, { metrics: kpis });
|
|
5940
|
+
};
|
|
5941
|
+
return /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
5942
|
+
/* @__PURE__ */ jsx17(
|
|
5943
|
+
SectionCard,
|
|
5944
|
+
{
|
|
5945
|
+
title: "Efficiency",
|
|
5946
|
+
subtitle: `${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
5947
|
+
children: renderEfficiencyBody()
|
|
5948
|
+
}
|
|
5949
|
+
),
|
|
5950
|
+
/* @__PURE__ */ jsxs16(Grid2, { container: true, spacing: 2, alignItems: "flex-start", children: [
|
|
5951
|
+
/* @__PURE__ */ jsx17(Grid2, { item: true, xs: 12, md: 5, children: /* @__PURE__ */ jsx17(SectionCard, { title: "Practice score", subtitle: "Out of 100", children: loading ? /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 160 }) : /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
|
|
5952
|
+
/* @__PURE__ */ jsxs16(Typography14, { sx: { fontSize: 30, fontWeight: 700, fontVariantNumeric: "tabular-nums" }, children: [
|
|
5953
|
+
score.total,
|
|
5954
|
+
/* @__PURE__ */ jsx17(Typography14, { component: "span", color: "text.secondary", sx: { fontSize: 16 }, children: "/100" })
|
|
5955
|
+
] }),
|
|
5956
|
+
score.checks.map((c) => /* @__PURE__ */ jsxs16(Box16, { children: [
|
|
5957
|
+
/* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", justifyContent: "space-between", gap: 1, mb: 0.5 }, children: [
|
|
5958
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "body2", sx: { fontWeight: 600 }, children: c.label }),
|
|
5959
|
+
/* @__PURE__ */ jsxs16(Typography14, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" }, children: [
|
|
5960
|
+
c.points,
|
|
5961
|
+
"/",
|
|
5962
|
+
c.maxPoints
|
|
5963
|
+
] })
|
|
5964
|
+
] }),
|
|
5965
|
+
/* @__PURE__ */ jsx17(
|
|
5966
|
+
Meter,
|
|
5967
|
+
{
|
|
5968
|
+
value: c.maxPoints > 0 ? c.points / c.maxPoints * 100 : 0,
|
|
5969
|
+
ariaLabel: `${c.label}: ${c.points} of ${c.maxPoints}`
|
|
5970
|
+
}
|
|
5971
|
+
),
|
|
5972
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "caption", color: "text.secondary", children: c.detail })
|
|
5973
|
+
] }, c.id))
|
|
5974
|
+
] }) }) }),
|
|
5975
|
+
/* @__PURE__ */ jsx17(Grid2, { item: true, xs: 12, md: 7, children: /* @__PURE__ */ jsx17(SectionCard, { title: "Cache & errors", subtitle: "From spend logs (estimate)", children: renderSpendLogsBody() }) })
|
|
5976
|
+
] }),
|
|
5977
|
+
/* @__PURE__ */ jsx17(SectionCard, { title: "Model opportunities", flush: true, children: loading ? /* @__PURE__ */ jsx17(Box16, { sx: { p: 2.5 }, children: /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 120 }) }) : /* @__PURE__ */ jsx17(TableContainer6, { children: /* @__PURE__ */ jsxs16(Table6, { size: "small", sx: dataTableSx, children: [
|
|
5978
|
+
/* @__PURE__ */ jsx17(TableHead6, { children: /* @__PURE__ */ jsxs16(TableRow6, { children: [
|
|
5979
|
+
/* @__PURE__ */ jsx17(TableCell6, { children: "Model" }),
|
|
5980
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Spend" }),
|
|
5981
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Requests" }),
|
|
5982
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Failure" }),
|
|
5983
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Avg / req" }),
|
|
5984
|
+
/* @__PURE__ */ jsx17(TableCell6, { children: "Signal" })
|
|
5985
|
+
] }) }),
|
|
5986
|
+
/* @__PURE__ */ jsx17(TableBody6, { children: opportunities.length === 0 ? /* @__PURE__ */ jsx17(TableRow6, { children: /* @__PURE__ */ jsx17(TableCell6, { colSpan: 6, children: /* @__PURE__ */ jsx17(EmptyState, { message: "No model activity in this period" }) }) }) : opportunities.map((o) => /* @__PURE__ */ jsxs16(TableRow6, { children: [
|
|
5987
|
+
/* @__PURE__ */ jsx17(TableCell6, { children: /* @__PURE__ */ jsx17(Typography14, { variant: "body2", sx: { fontWeight: 600 }, children: o.model }) }),
|
|
5988
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtUsd(o.spend) }),
|
|
5989
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtInt(o.requests) }),
|
|
5990
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtPct1(o.failureRate) }),
|
|
5991
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtUsd(o.avgCostPerRequest) }),
|
|
5992
|
+
/* @__PURE__ */ jsxs16(TableCell6, { children: [
|
|
5993
|
+
/* @__PURE__ */ jsx17(StatusPill, { label: FLAG_LABEL[o.flag], tone: FLAG_TONE[o.flag], title: o.hint }),
|
|
5994
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.25 }, children: o.hint })
|
|
5995
|
+
] })
|
|
5996
|
+
] }, o.model)) })
|
|
5997
|
+
] }) }) }),
|
|
5998
|
+
/* @__PURE__ */ jsx17(SectionCard, { title: "Quality scores", children: /* @__PURE__ */ jsx17(
|
|
5999
|
+
EmptyState,
|
|
6000
|
+
{
|
|
6001
|
+
message: "Quality scores \u2014 connect Langfuse",
|
|
6002
|
+
hint: "LLM-as-judge evaluation is not configured for this deployment."
|
|
6003
|
+
}
|
|
6004
|
+
) })
|
|
6005
|
+
] });
|
|
6006
|
+
};
|
|
6007
|
+
}
|
|
6008
|
+
});
|
|
6009
|
+
|
|
5956
6010
|
// src/feedback.ts
|
|
5957
6011
|
function toastFor(kind, errorMsg) {
|
|
5958
6012
|
switch (kind) {
|
|
@@ -6047,20 +6101,20 @@ var LiteLLMPage_exports = {};
|
|
|
6047
6101
|
__export(LiteLLMPage_exports, {
|
|
6048
6102
|
LiteLLMPage: () => LiteLLMPage
|
|
6049
6103
|
});
|
|
6050
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo9, useState as
|
|
6104
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo9, useState as useState11 } from "react";
|
|
6051
6105
|
import Box17 from "@mui/material/Box";
|
|
6052
6106
|
import Alert6 from "@mui/material/Alert";
|
|
6053
|
-
import
|
|
6107
|
+
import Button8 from "@mui/material/Button";
|
|
6054
6108
|
import Typography15 from "@mui/material/Typography";
|
|
6055
6109
|
import Tabs2 from "@mui/material/Tabs";
|
|
6056
6110
|
import Tab2 from "@mui/material/Tab";
|
|
6057
|
-
import
|
|
6111
|
+
import Grid3 from "@mui/material/Grid";
|
|
6058
6112
|
import Accordion2 from "@mui/material/Accordion";
|
|
6059
6113
|
import AccordionSummary2 from "@mui/material/AccordionSummary";
|
|
6060
6114
|
import AccordionDetails2 from "@mui/material/AccordionDetails";
|
|
6061
6115
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
6062
6116
|
import { useSearchParams } from "react-router-dom";
|
|
6063
|
-
import { useAsync as
|
|
6117
|
+
import { useAsync, useAsyncRetry as useAsyncRetry2 } from "react-use";
|
|
6064
6118
|
import { useApi as useApi4, alertApiRef as alertApiRef2 } from "@backstage/core-plugin-api";
|
|
6065
6119
|
import { Content, Progress } from "@backstage/core-components";
|
|
6066
6120
|
import { usePermission as usePermission3 } from "@backstage/plugin-permission-react";
|
|
@@ -6098,20 +6152,20 @@ var init_LiteLLMPage = __esm({
|
|
|
6098
6152
|
init_LiteLLMBudgetWidget();
|
|
6099
6153
|
init_TeamUsage();
|
|
6100
6154
|
init_ModelsTable();
|
|
6101
|
-
|
|
6155
|
+
init_InsightsTab();
|
|
6102
6156
|
init_api();
|
|
6103
6157
|
init_dates();
|
|
6104
6158
|
init_feedback();
|
|
6105
6159
|
init_teamMemberManager();
|
|
6106
6160
|
init_unprovisionedCopy();
|
|
6107
6161
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
6108
|
-
PAGE_TABS = ["overview", "keys", "teams", "models", "
|
|
6162
|
+
PAGE_TABS = ["overview", "keys", "teams", "models", "insights"];
|
|
6109
6163
|
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
6110
6164
|
LiteLLMPage = () => {
|
|
6111
6165
|
const api = useApi4(liteLlmApiRef);
|
|
6112
6166
|
const alertApi = useApi4(alertApiRef2);
|
|
6113
|
-
const [dateRange, setDateRange] =
|
|
6114
|
-
const [currentPreset, setCurrentPreset] =
|
|
6167
|
+
const [dateRange, setDateRange] = useState11(initDateRange);
|
|
6168
|
+
const [currentPreset, setCurrentPreset] = useState11(() => {
|
|
6115
6169
|
try {
|
|
6116
6170
|
return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
|
|
6117
6171
|
} catch {
|
|
@@ -6119,15 +6173,15 @@ var init_LiteLLMPage = __esm({
|
|
|
6119
6173
|
}
|
|
6120
6174
|
});
|
|
6121
6175
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
6122
|
-
const [activeTab, setActiveTab] =
|
|
6176
|
+
const [activeTab, setActiveTab] = useState11(() => {
|
|
6123
6177
|
const t = searchParams.get("tab");
|
|
6124
6178
|
return isPageTab(t) ? t : "overview";
|
|
6125
6179
|
});
|
|
6126
|
-
const [filter, setFilter] =
|
|
6180
|
+
const [filter, setFilter] = useState11(() => {
|
|
6127
6181
|
const f = searchParams.get("filter");
|
|
6128
6182
|
return f === "expired" || f === "expiring" ? f : void 0;
|
|
6129
6183
|
});
|
|
6130
|
-
const [generateDialogOpen, setGenerateDialogOpen] =
|
|
6184
|
+
const [generateDialogOpen, setGenerateDialogOpen] = useState11(
|
|
6131
6185
|
() => searchParams.get("generate") === "1"
|
|
6132
6186
|
);
|
|
6133
6187
|
useEffect6(() => {
|
|
@@ -6183,31 +6237,31 @@ var init_LiteLLMPage = __esm({
|
|
|
6183
6237
|
{ replace: true }
|
|
6184
6238
|
);
|
|
6185
6239
|
}, [setSearchParams]);
|
|
6186
|
-
const [manageTeam, setManageTeam] =
|
|
6187
|
-
const [keyToEdit, setKeyToEdit] =
|
|
6240
|
+
const [manageTeam, setManageTeam] = useState11(null);
|
|
6241
|
+
const [keyToEdit, setKeyToEdit] = useState11(null);
|
|
6188
6242
|
const keyFormOpen = generateDialogOpen || !!keyToEdit;
|
|
6189
|
-
const [teamUsageCache, setTeamUsageCache] =
|
|
6190
|
-
const [teamUsageLoading, setTeamUsageLoading] =
|
|
6191
|
-
const [memberUsageCache, setMemberUsageCache] =
|
|
6192
|
-
const [memberUsageLoading, setMemberUsageLoading] =
|
|
6193
|
-
const { value: userInfo, loading: userLoading, error: userError } =
|
|
6243
|
+
const [teamUsageCache, setTeamUsageCache] = useState11({});
|
|
6244
|
+
const [teamUsageLoading, setTeamUsageLoading] = useState11({});
|
|
6245
|
+
const [memberUsageCache, setMemberUsageCache] = useState11({});
|
|
6246
|
+
const [memberUsageLoading, setMemberUsageLoading] = useState11({});
|
|
6247
|
+
const { value: userInfo, loading: userLoading, error: userError } = useAsync(
|
|
6194
6248
|
() => api.getUserInfo(),
|
|
6195
6249
|
[api]
|
|
6196
6250
|
);
|
|
6197
|
-
const { value: keys, loading: keysLoading, error: keysError, retry: refreshKeys } =
|
|
6251
|
+
const { value: keys, loading: keysLoading, error: keysError, retry: refreshKeys } = useAsyncRetry2(
|
|
6198
6252
|
() => api.listKeys(),
|
|
6199
6253
|
[api]
|
|
6200
6254
|
);
|
|
6201
|
-
const { value: allModels, loading: modelsLoading, error: modelsError, retry: refreshModels } =
|
|
6255
|
+
const { value: allModels, loading: modelsLoading, error: modelsError, retry: refreshModels } = useAsyncRetry2(
|
|
6202
6256
|
() => api.listModels(),
|
|
6203
6257
|
[api]
|
|
6204
6258
|
);
|
|
6205
|
-
const { value: allTeams, loading: teamsLoading, error: teamsError, retry: refreshTeams } =
|
|
6259
|
+
const { value: allTeams, loading: teamsLoading, error: teamsError, retry: refreshTeams } = useAsyncRetry2(
|
|
6206
6260
|
() => api.getTeams(),
|
|
6207
6261
|
[api]
|
|
6208
6262
|
);
|
|
6209
|
-
const { value: liteLlmConfig } =
|
|
6210
|
-
const { value: managedTeams } =
|
|
6263
|
+
const { value: liteLlmConfig } = useAsync(() => api.getConfig(), [api]);
|
|
6264
|
+
const { value: managedTeams } = useAsync(
|
|
6211
6265
|
async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
|
|
6212
6266
|
[api, liteLlmConfig]
|
|
6213
6267
|
);
|
|
@@ -6218,11 +6272,11 @@ var init_LiteLLMPage = __esm({
|
|
|
6218
6272
|
const { allowed: canManageMcpServers } = usePermission3({ permission: litellmTeamMcpManagePermission });
|
|
6219
6273
|
const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
|
|
6220
6274
|
const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
|
|
6221
|
-
const { value: vectorStores } =
|
|
6275
|
+
const { value: vectorStores } = useAsync(
|
|
6222
6276
|
async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
|
|
6223
6277
|
[api, objectPermsEnabled]
|
|
6224
6278
|
);
|
|
6225
|
-
const { value: mcpServers } =
|
|
6279
|
+
const { value: mcpServers } = useAsync(
|
|
6226
6280
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
6227
6281
|
[api, objectPermsEnabled]
|
|
6228
6282
|
);
|
|
@@ -6238,7 +6292,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6238
6292
|
(t) => t.members_with_roles?.some((m) => m.user_id === userId)
|
|
6239
6293
|
);
|
|
6240
6294
|
}, [allTeams, userInfo]);
|
|
6241
|
-
const { value: usage, loading: usageLoading, error: usageError, retry: refreshUsage } =
|
|
6295
|
+
const { value: usage, loading: usageLoading, error: usageError, retry: refreshUsage } = useAsyncRetry2(async () => {
|
|
6242
6296
|
const startDate = toLocalDay(dateRange.start);
|
|
6243
6297
|
const endDate = toLocalDay(dateRange.end);
|
|
6244
6298
|
return api.getUsage(startDate, endDate);
|
|
@@ -6446,7 +6500,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6446
6500
|
/* @__PURE__ */ jsx18(Tab2, { label: "Keys", value: "keys" }),
|
|
6447
6501
|
/* @__PURE__ */ jsx18(Tab2, { label: "Teams", value: "teams" }),
|
|
6448
6502
|
/* @__PURE__ */ jsx18(Tab2, { label: "Models", value: "models" }),
|
|
6449
|
-
|
|
6503
|
+
/* @__PURE__ */ jsx18(Tab2, { label: "Insights", value: "insights" })
|
|
6450
6504
|
]
|
|
6451
6505
|
}
|
|
6452
6506
|
);
|
|
@@ -6500,7 +6554,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6500
6554
|
}
|
|
6501
6555
|
),
|
|
6502
6556
|
/* @__PURE__ */ jsx18(
|
|
6503
|
-
|
|
6557
|
+
Button8,
|
|
6504
6558
|
{
|
|
6505
6559
|
variant: "contained",
|
|
6506
6560
|
color: "primary",
|
|
@@ -6512,8 +6566,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6512
6566
|
}
|
|
6513
6567
|
) });
|
|
6514
6568
|
}
|
|
6515
|
-
return /* @__PURE__ */ jsxs17(
|
|
6516
|
-
/* @__PURE__ */ jsx18(
|
|
6569
|
+
return /* @__PURE__ */ jsxs17(Grid3, { container: true, spacing: 2, alignItems: "flex-start", children: [
|
|
6570
|
+
/* @__PURE__ */ jsx18(Grid3, { item: true, xs: 12, lg: 8, children: /* @__PURE__ */ jsx18(
|
|
6517
6571
|
UsageStats,
|
|
6518
6572
|
{
|
|
6519
6573
|
usage: usage ?? null,
|
|
@@ -6527,7 +6581,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6527
6581
|
userInfo
|
|
6528
6582
|
}
|
|
6529
6583
|
) }),
|
|
6530
|
-
/* @__PURE__ */ jsx18(
|
|
6584
|
+
/* @__PURE__ */ jsx18(Grid3, { item: true, xs: 12, lg: 4, children: /* @__PURE__ */ jsx18(LiteLLMBudgetWidget, { compact: true, collapsible: true, userInfo: userInfo ?? null, teams, keys: keys ?? [] }) })
|
|
6531
6585
|
] });
|
|
6532
6586
|
})(),
|
|
6533
6587
|
activeTab === "keys" && (() => {
|
|
@@ -6535,7 +6589,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6535
6589
|
return /* @__PURE__ */ jsxs17(Alert6, { severity: "error", children: [
|
|
6536
6590
|
"Failed to load keys: ",
|
|
6537
6591
|
keysError.message || "Unknown error",
|
|
6538
|
-
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(
|
|
6592
|
+
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(Button8, { size: "small", onClick: refreshKeys, children: "Retry" }) })
|
|
6539
6593
|
] });
|
|
6540
6594
|
}
|
|
6541
6595
|
return /* @__PURE__ */ jsx18(
|
|
@@ -6559,7 +6613,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6559
6613
|
return /* @__PURE__ */ jsxs17(Alert6, { severity: "error", children: [
|
|
6560
6614
|
"Failed to load teams: ",
|
|
6561
6615
|
teamsError.message || "Unknown error",
|
|
6562
|
-
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(
|
|
6616
|
+
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(Button8, { size: "small", onClick: refreshTeams, children: "Retry" }) })
|
|
6563
6617
|
] });
|
|
6564
6618
|
}
|
|
6565
6619
|
const teamsById = /* @__PURE__ */ new Map();
|
|
@@ -6596,7 +6650,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6596
6650
|
return /* @__PURE__ */ jsxs17(Alert6, { severity: "error", children: [
|
|
6597
6651
|
"Failed to load models: ",
|
|
6598
6652
|
modelsError.message || "Unknown error",
|
|
6599
|
-
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(
|
|
6653
|
+
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(Button8, { size: "small", onClick: refreshModels, children: "Retry" }) })
|
|
6600
6654
|
] });
|
|
6601
6655
|
}
|
|
6602
6656
|
return /* @__PURE__ */ jsx18(
|
|
@@ -6608,7 +6662,17 @@ var init_LiteLLMPage = __esm({
|
|
|
6608
6662
|
}
|
|
6609
6663
|
);
|
|
6610
6664
|
})(),
|
|
6611
|
-
activeTab === "
|
|
6665
|
+
activeTab === "insights" && /* @__PURE__ */ jsx18(
|
|
6666
|
+
InsightsTab,
|
|
6667
|
+
{
|
|
6668
|
+
api,
|
|
6669
|
+
usage: usage ?? null,
|
|
6670
|
+
models: allModels ?? [],
|
|
6671
|
+
keys: keys ?? [],
|
|
6672
|
+
dateRange,
|
|
6673
|
+
loading: usageLoading
|
|
6674
|
+
}
|
|
6675
|
+
)
|
|
6612
6676
|
] }),
|
|
6613
6677
|
/* @__PURE__ */ jsx18(
|
|
6614
6678
|
KeyFormDialog,
|
|
@@ -6767,6 +6831,10 @@ function postToLoopback(redirectUri, fields) {
|
|
|
6767
6831
|
document.body.appendChild(form);
|
|
6768
6832
|
form.submit();
|
|
6769
6833
|
}
|
|
6834
|
+
function initialTeams(all, have) {
|
|
6835
|
+
const missing = all.filter((id) => !have.has(id));
|
|
6836
|
+
return new Set(have.size ? missing : all);
|
|
6837
|
+
}
|
|
6770
6838
|
var init_connect = __esm({
|
|
6771
6839
|
"src/connect.ts"() {
|
|
6772
6840
|
"use strict";
|
|
@@ -6778,17 +6846,17 @@ var ConnectPage_exports = {};
|
|
|
6778
6846
|
__export(ConnectPage_exports, {
|
|
6779
6847
|
ConnectPage: () => ConnectPage
|
|
6780
6848
|
});
|
|
6781
|
-
import { useState as
|
|
6849
|
+
import { useState as useState12 } from "react";
|
|
6782
6850
|
import Alert7 from "@mui/material/Alert";
|
|
6783
6851
|
import Box18 from "@mui/material/Box";
|
|
6784
|
-
import
|
|
6852
|
+
import Button9 from "@mui/material/Button";
|
|
6785
6853
|
import Checkbox3 from "@mui/material/Checkbox";
|
|
6786
6854
|
import FormControlLabel3 from "@mui/material/FormControlLabel";
|
|
6787
6855
|
import FormGroup from "@mui/material/FormGroup";
|
|
6788
6856
|
import Paper9 from "@mui/material/Paper";
|
|
6789
6857
|
import Typography16 from "@mui/material/Typography";
|
|
6790
6858
|
import { useSearchParams as useSearchParams2 } from "react-router-dom";
|
|
6791
|
-
import { useAsync as
|
|
6859
|
+
import { useAsync as useAsync2 } from "react-use";
|
|
6792
6860
|
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
6793
6861
|
import { Content as Content2, Progress as Progress2 } from "@backstage/core-components";
|
|
6794
6862
|
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
@@ -6804,16 +6872,17 @@ var init_ConnectPage = __esm({
|
|
|
6804
6872
|
const redirectUri = params.get("redirect_uri");
|
|
6805
6873
|
const state = params.get("state") ?? "";
|
|
6806
6874
|
const client = params.get("client") || "a local application";
|
|
6807
|
-
const
|
|
6808
|
-
const
|
|
6809
|
-
const [
|
|
6810
|
-
const [
|
|
6875
|
+
const have = new Set((params.get("have") ?? "").split(",").filter(Boolean));
|
|
6876
|
+
const teams = useAsync2(() => api.getTeams(), [api]);
|
|
6877
|
+
const [selected, setSelected] = useState12();
|
|
6878
|
+
const [busy, setBusy] = useState12(false);
|
|
6879
|
+
const [error, setError] = useState12();
|
|
6811
6880
|
if (!isLoopbackCallback(redirectUri)) {
|
|
6812
6881
|
return /* @__PURE__ */ jsx19(Content2, { children: /* @__PURE__ */ jsx19(Alert7, { severity: "error", children: "Invalid connection link: redirect_uri must be http://localhost:<port>/callback." }) });
|
|
6813
6882
|
}
|
|
6814
6883
|
if (teams.loading) return /* @__PURE__ */ jsx19(Progress2, {});
|
|
6815
6884
|
const all = teams.value ?? [];
|
|
6816
|
-
const chosen = selected ??
|
|
6885
|
+
const chosen = selected ?? initialTeams(all.map((t) => t.team_id), have);
|
|
6817
6886
|
const toggle = (id) => {
|
|
6818
6887
|
const next = new Set(chosen);
|
|
6819
6888
|
if (!next.delete(id)) next.add(id);
|
|
@@ -6839,7 +6908,7 @@ var init_ConnectPage = __esm({
|
|
|
6839
6908
|
] }),
|
|
6840
6909
|
/* @__PURE__ */ jsxs18(Typography16, { variant: "body2", color: "text.secondary", paragraph: true, children: [
|
|
6841
6910
|
client,
|
|
6842
|
-
" will receive an API key for each selected team.
|
|
6911
|
+
" will receive an API key for each selected team. Selecting a team that is already connected replaces its key, also on your other devices."
|
|
6843
6912
|
] }),
|
|
6844
6913
|
teams.error && /* @__PURE__ */ jsxs18(Alert7, { severity: "error", children: [
|
|
6845
6914
|
"Could not load your teams: ",
|
|
@@ -6849,13 +6918,13 @@ var init_ConnectPage = __esm({
|
|
|
6849
6918
|
FormControlLabel3,
|
|
6850
6919
|
{
|
|
6851
6920
|
control: /* @__PURE__ */ jsx19(Checkbox3, { checked: chosen.has(t.team_id), onChange: () => toggle(t.team_id) }),
|
|
6852
|
-
label: t.team_alias ?? t.team_id
|
|
6921
|
+
label: `${t.team_alias ?? t.team_id}${have.has(t.team_id) ? " (already connected)" : ""}`
|
|
6853
6922
|
},
|
|
6854
6923
|
t.team_id
|
|
6855
6924
|
)) }),
|
|
6856
6925
|
error && /* @__PURE__ */ jsx19(Alert7, { severity: "error", sx: { mb: 2 }, children: error }),
|
|
6857
6926
|
/* @__PURE__ */ jsx19(
|
|
6858
|
-
|
|
6927
|
+
Button9,
|
|
6859
6928
|
{
|
|
6860
6929
|
variant: "contained",
|
|
6861
6930
|
onClick: connect,
|
|
@@ -6932,13 +7001,13 @@ init_format();
|
|
|
6932
7001
|
init_dates();
|
|
6933
7002
|
init_ui();
|
|
6934
7003
|
init_widgetState();
|
|
6935
|
-
import { useEffect as useEffect7, useMemo as useMemo10, useState as
|
|
7004
|
+
import { useEffect as useEffect7, useMemo as useMemo10, useState as useState13 } from "react";
|
|
6936
7005
|
import Paper10 from "@mui/material/Paper";
|
|
6937
7006
|
import Box19 from "@mui/material/Box";
|
|
6938
7007
|
import Typography17 from "@mui/material/Typography";
|
|
6939
7008
|
import FormControl2 from "@mui/material/FormControl";
|
|
6940
7009
|
import Select2 from "@mui/material/Select";
|
|
6941
|
-
import
|
|
7010
|
+
import MenuItem5 from "@mui/material/MenuItem";
|
|
6942
7011
|
import Skeleton7 from "@mui/material/Skeleton";
|
|
6943
7012
|
import { AreaChart as AreaChart3, Area as Area3, XAxis as XAxis3, Tooltip as Tooltip8, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
|
|
6944
7013
|
import { useApi as useApi6 } from "@backstage/core-plugin-api";
|
|
@@ -6988,7 +7057,7 @@ function usageSummary(usage) {
|
|
|
6988
7057
|
}
|
|
6989
7058
|
|
|
6990
7059
|
// src/components/LiteLLMHomeWidget.tsx
|
|
6991
|
-
import { Fragment as
|
|
7060
|
+
import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
6992
7061
|
function presetToDateRange(preset) {
|
|
6993
7062
|
const end = /* @__PURE__ */ new Date();
|
|
6994
7063
|
const start = /* @__PURE__ */ new Date();
|
|
@@ -7009,10 +7078,10 @@ var LiteLLMHomeWidget = ({
|
|
|
7009
7078
|
const api = useApi6(liteLlmApiRef);
|
|
7010
7079
|
const moduleRouteRef = useRouteRef2(rootRouteRef);
|
|
7011
7080
|
const { userInfo, keys, loading: profileLoading, error: profileError } = useLiteLLMProfile();
|
|
7012
|
-
const [period, setPeriod] =
|
|
7013
|
-
const [usageLoading, setUsageLoading] =
|
|
7014
|
-
const [usageError, setUsageError] =
|
|
7015
|
-
const [usage, setUsage] =
|
|
7081
|
+
const [period, setPeriod] = useState13(defaultPeriod);
|
|
7082
|
+
const [usageLoading, setUsageLoading] = useState13(true);
|
|
7083
|
+
const [usageError, setUsageError] = useState13(null);
|
|
7084
|
+
const [usage, setUsage] = useState13(null);
|
|
7016
7085
|
useEffect7(() => {
|
|
7017
7086
|
let cancelled = false;
|
|
7018
7087
|
setUsageLoading(true);
|
|
@@ -7068,9 +7137,9 @@ var LiteLLMHomeWidget = ({
|
|
|
7068
7137
|
displayEmpty: true,
|
|
7069
7138
|
inputProps: { "aria-label": "Usage period" },
|
|
7070
7139
|
children: [
|
|
7071
|
-
/* @__PURE__ */ jsx21(
|
|
7072
|
-
/* @__PURE__ */ jsx21(
|
|
7073
|
-
/* @__PURE__ */ jsx21(
|
|
7140
|
+
/* @__PURE__ */ jsx21(MenuItem5, { value: "today", children: "Today" }),
|
|
7141
|
+
/* @__PURE__ */ jsx21(MenuItem5, { value: "7d", children: "7d" }),
|
|
7142
|
+
/* @__PURE__ */ jsx21(MenuItem5, { value: "30d", children: "30d" })
|
|
7074
7143
|
]
|
|
7075
7144
|
}
|
|
7076
7145
|
) })
|
|
@@ -7082,7 +7151,7 @@ var LiteLLMHomeWidget = ({
|
|
|
7082
7151
|
viewState.kind === "error" && /* @__PURE__ */ jsx21(Typography17, { variant: "body2", color: "text.secondary", sx: { mt: 1 }, children: viewState.message }),
|
|
7083
7152
|
viewState.kind === "unprovisioned" && /* @__PURE__ */ jsx21(Typography17, { variant: "body2", color: "text.secondary", sx: { mt: 1 }, children: UNPROVISIONED_MSG }),
|
|
7084
7153
|
viewState.kind === "ready" && partialFailure && /* @__PURE__ */ jsx21(Box19, { sx: { mt: 1, mb: 1, p: 1, backgroundColor: "action.hover", borderRadius: 1 }, children: /* @__PURE__ */ jsx21(Typography17, { variant: "body2", color: "text.secondary", children: USAGE_UNAVAILABLE_MSG }) }),
|
|
7085
|
-
viewState.kind === "ready" && /* @__PURE__ */ jsxs19(
|
|
7154
|
+
viewState.kind === "ready" && /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
7086
7155
|
/* @__PURE__ */ jsx21(
|
|
7087
7156
|
Typography17,
|
|
7088
7157
|
{
|
|
@@ -7095,7 +7164,7 @@ var LiteLLMHomeWidget = ({
|
|
|
7095
7164
|
children: viewState.usageUnavailable ? USAGE_UNAVAILABLE_MSG : usageSummary(usage)
|
|
7096
7165
|
}
|
|
7097
7166
|
),
|
|
7098
|
-
hasSparkline && !viewState.usageUnavailable && /* @__PURE__ */ jsxs19(
|
|
7167
|
+
hasSparkline && !viewState.usageUnavailable && /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
7099
7168
|
/* @__PURE__ */ jsx21(
|
|
7100
7169
|
Box19,
|
|
7101
7170
|
{
|
|
@@ -7141,14 +7210,14 @@ init_useLiteLLMProfile();
|
|
|
7141
7210
|
init_api();
|
|
7142
7211
|
init_format();
|
|
7143
7212
|
init_dates();
|
|
7144
|
-
import { useCallback as useCallback5, useEffect as useEffect8, useId as useId2, useMemo as useMemo11, useState as
|
|
7213
|
+
import { useCallback as useCallback5, useEffect as useEffect8, useId as useId2, useMemo as useMemo11, useState as useState14 } from "react";
|
|
7145
7214
|
import { useRouteRef as useRouteRef3 } from "@backstage/frontend-plugin-api";
|
|
7146
7215
|
import Paper11 from "@mui/material/Paper";
|
|
7147
7216
|
import Box20 from "@mui/material/Box";
|
|
7148
|
-
import
|
|
7217
|
+
import Button10 from "@mui/material/Button";
|
|
7149
7218
|
import Typography18 from "@mui/material/Typography";
|
|
7150
7219
|
import Skeleton8 from "@mui/material/Skeleton";
|
|
7151
|
-
import
|
|
7220
|
+
import Collapse3 from "@mui/material/Collapse";
|
|
7152
7221
|
import { useTheme as useTheme4 } from "@mui/material/styles";
|
|
7153
7222
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
7154
7223
|
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
@@ -7181,7 +7250,7 @@ init_GenerateKeyButton();
|
|
|
7181
7250
|
init_BudgetLimitList();
|
|
7182
7251
|
init_budget();
|
|
7183
7252
|
init_routes();
|
|
7184
|
-
import { Fragment as
|
|
7253
|
+
import { Fragment as Fragment10, jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
7185
7254
|
var LEVEL = {
|
|
7186
7255
|
key: { name: "Key", none: "No cap (unlimited)" },
|
|
7187
7256
|
user: { name: "User", none: "No personal budget" },
|
|
@@ -7334,7 +7403,7 @@ var LevelRow = ({
|
|
|
7334
7403
|
),
|
|
7335
7404
|
/* @__PURE__ */ jsxs20(Box20, { sx: { minWidth: 0 }, children: [
|
|
7336
7405
|
/* @__PURE__ */ jsx22(Tag, { tone: tone === "warning" || tone === "danger" ? tone : void 0, children: name }),
|
|
7337
|
-
limit ? /* @__PURE__ */ jsxs20(
|
|
7406
|
+
limit ? /* @__PURE__ */ jsxs20(Fragment10, { children: [
|
|
7338
7407
|
/* @__PURE__ */ jsx22(
|
|
7339
7408
|
Typography18,
|
|
7340
7409
|
{
|
|
@@ -7565,17 +7634,17 @@ var LiteLLMBudgetGauges = ({
|
|
|
7565
7634
|
const moduleRouteRef = useRouteRef3(rootRouteRef);
|
|
7566
7635
|
const moduleHref = propModuleHref ?? moduleRouteRef?.();
|
|
7567
7636
|
const { userInfo: hookUserInfo, teams: hookTeams, keys: hookKeys, loading: profileLoading, error: profileError } = useLiteLLMProfile();
|
|
7568
|
-
const [usageLoading, setUsageLoading] =
|
|
7569
|
-
const [usageError, setUsageError] =
|
|
7570
|
-
const [dailySpend, setDailySpend] =
|
|
7571
|
-
const [mtdSpend, setMtdSpend] =
|
|
7637
|
+
const [usageLoading, setUsageLoading] = useState14(true);
|
|
7638
|
+
const [usageError, setUsageError] = useState14(null);
|
|
7639
|
+
const [dailySpend, setDailySpend] = useState14([]);
|
|
7640
|
+
const [mtdSpend, setMtdSpend] = useState14(0);
|
|
7572
7641
|
const user = propUserInfo !== void 0 ? propUserInfo : hookUserInfo;
|
|
7573
7642
|
const teams = propTeams !== void 0 ? propTeams : hookTeams;
|
|
7574
7643
|
const keys = propKeys !== void 0 ? propKeys : hookKeys;
|
|
7575
7644
|
const loading = propUserInfo === void 0 ? profileLoading : usageLoading;
|
|
7576
7645
|
const error = propUserInfo === void 0 ? profileError : usageError;
|
|
7577
7646
|
const isControlled = expanded !== void 0;
|
|
7578
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] =
|
|
7647
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = useState14(defaultExpanded);
|
|
7579
7648
|
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
7580
7649
|
const toggleExpanded = useCallback5(() => {
|
|
7581
7650
|
const next = !isExpanded;
|
|
@@ -7659,10 +7728,10 @@ var LiteLLMBudgetGauges = ({
|
|
|
7659
7728
|
return onCreateKey ? /* @__PURE__ */ jsx22(GenerateKeyButton, { size: "small", label, onClick: onCreateKey }, key) : /* @__PURE__ */ jsx22(GenerateKeyButton, { size: "small", label, to: `${moduleHref}?generate=1` }, key);
|
|
7660
7729
|
case "module":
|
|
7661
7730
|
if (!moduleHref) return null;
|
|
7662
|
-
return /* @__PURE__ */ jsx22(
|
|
7731
|
+
return /* @__PURE__ */ jsx22(Button10, { size: "small", variant: "text", component: Link4, to: moduleHref, children: label }, key);
|
|
7663
7732
|
case "all-limits":
|
|
7664
7733
|
return /* @__PURE__ */ jsx22(
|
|
7665
|
-
|
|
7734
|
+
Button10,
|
|
7666
7735
|
{
|
|
7667
7736
|
size: "small",
|
|
7668
7737
|
variant: "text",
|
|
@@ -7711,7 +7780,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
7711
7780
|
] }, i)) }),
|
|
7712
7781
|
viewState.kind === "error" && /* @__PURE__ */ jsx22(Typography18, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: viewState.message }),
|
|
7713
7782
|
viewState.kind === "unprovisioned" && /* @__PURE__ */ jsx22(Typography18, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: UNPROVISIONED_MSG }),
|
|
7714
|
-
viewState.kind === "ready" && /* @__PURE__ */ jsxs20(
|
|
7783
|
+
viewState.kind === "ready" && /* @__PURE__ */ jsxs20(Fragment10, { children: [
|
|
7715
7784
|
/* @__PURE__ */ jsxs20(
|
|
7716
7785
|
Box20,
|
|
7717
7786
|
{
|
|
@@ -7765,7 +7834,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
7765
7834
|
maxBudget: user?.max_budget ?? void 0
|
|
7766
7835
|
}
|
|
7767
7836
|
) }),
|
|
7768
|
-
/* @__PURE__ */ jsx22(
|
|
7837
|
+
/* @__PURE__ */ jsx22(Collapse3, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ jsx22(
|
|
7769
7838
|
Box20,
|
|
7770
7839
|
{
|
|
7771
7840
|
id: expandedRegionId,
|