@acarmisc/backstage-plugin-litellm 0.37.0 → 0.39.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 +6 -1
- package/dist/components/InsightsTab.d.ts +12 -0
- package/dist/components/ui/surfaces.d.ts +2 -0
- package/dist/index.cjs.js +455 -289
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.esm.js +496 -330
- 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,25 @@ 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
|
+
}
|
|
309
|
+
async getQuality(params) {
|
|
310
|
+
const strParams = {
|
|
311
|
+
start_date: params.startDate,
|
|
312
|
+
end_date: params.endDate
|
|
313
|
+
};
|
|
314
|
+
if (params.userId) strParams.userId = params.userId;
|
|
315
|
+
return this.get("/insights/quality", strParams);
|
|
316
|
+
}
|
|
298
317
|
async getConfig() {
|
|
299
318
|
return this.get("/config");
|
|
300
319
|
}
|
|
@@ -693,7 +712,7 @@ var init_surfaces = __esm({
|
|
|
693
712
|
"src/components/ui/surfaces.tsx"() {
|
|
694
713
|
"use strict";
|
|
695
714
|
init_tokens();
|
|
696
|
-
SectionCard = ({ title, subtitle, actions, flush = false, children }) => /* @__PURE__ */ jsxs3(Paper2, { sx: { borderRadius: 2, overflow: "hidden" }, children: [
|
|
715
|
+
SectionCard = ({ title, subtitle, description, actions, flush = false, children }) => /* @__PURE__ */ jsxs3(Paper2, { sx: { borderRadius: 2, overflow: "hidden" }, children: [
|
|
697
716
|
(title || actions) && /* @__PURE__ */ jsxs3(
|
|
698
717
|
Box3,
|
|
699
718
|
{
|
|
@@ -715,6 +734,7 @@ var init_surfaces = __esm({
|
|
|
715
734
|
]
|
|
716
735
|
}
|
|
717
736
|
),
|
|
737
|
+
description && /* @__PURE__ */ jsx3(Typography2, { variant: "body2", color: "text.secondary", sx: { px: 2.5, pb: 1.5 }, children: description }),
|
|
718
738
|
/* @__PURE__ */ jsx3(Box3, { sx: flush ? void 0 : { px: 2.5, pb: 2.5, pt: title ? 0 : 2.5 }, children })
|
|
719
739
|
] });
|
|
720
740
|
ChartCard = ({ title, meta, height = 240, children }) => (
|
|
@@ -5664,271 +5684,101 @@ var init_ModelsTable = __esm({
|
|
|
5664
5684
|
}
|
|
5665
5685
|
});
|
|
5666
5686
|
|
|
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"() {
|
|
5687
|
+
// src/insights.ts
|
|
5688
|
+
var costPer1k, summarizeUsage, scorePractices, modelOpportunities;
|
|
5689
|
+
var init_insights = __esm({
|
|
5690
|
+
"src/insights.ts"() {
|
|
5724
5691
|
"use strict";
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
blocked: "danger",
|
|
5731
|
-
unblocked: "success"
|
|
5692
|
+
costPer1k = (model, spend, tokens, models) => {
|
|
5693
|
+
const info = models?.find((m) => m.model_name === model);
|
|
5694
|
+
const listed = info?.input_cost_per_token !== void 0 && info?.input_cost_per_token !== null && info?.output_cost_per_token !== void 0 && info?.output_cost_per_token !== null ? (info.input_cost_per_token + info.output_cost_per_token) / 2 * 1e3 : null;
|
|
5695
|
+
if (listed !== null) return listed;
|
|
5696
|
+
return tokens > 0 ? spend / tokens * 1e3 : null;
|
|
5732
5697
|
};
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5698
|
+
summarizeUsage = (usage, models, premiumThresholdPer1k = 0.01) => {
|
|
5699
|
+
if (!usage) {
|
|
5700
|
+
return {
|
|
5701
|
+
costPer1kTokens: null,
|
|
5702
|
+
costPerRequest: null,
|
|
5703
|
+
outputInputRatio: null,
|
|
5704
|
+
retryWasteUsd: 0,
|
|
5705
|
+
retryWasteShare: 0,
|
|
5706
|
+
successRate: null,
|
|
5707
|
+
premiumShare: 0,
|
|
5708
|
+
premiumSpend: 0,
|
|
5709
|
+
topModelConcentration: 0,
|
|
5710
|
+
projectedMonthSpend: null,
|
|
5711
|
+
burnMultiple: null
|
|
5712
|
+
};
|
|
5713
|
+
}
|
|
5714
|
+
const {
|
|
5715
|
+
total_spend: spend,
|
|
5716
|
+
total_tokens: tokens,
|
|
5717
|
+
prompt_tokens: prompt,
|
|
5718
|
+
completion_tokens: completion,
|
|
5719
|
+
api_requests: reqs,
|
|
5720
|
+
successful_requests: ok,
|
|
5721
|
+
failed_requests: failed,
|
|
5722
|
+
usage_by_model: byModel
|
|
5723
|
+
} = usage;
|
|
5724
|
+
const wasteShare = reqs > 0 ? failed / reqs : 0;
|
|
5725
|
+
const entries = Object.entries(byModel ?? {});
|
|
5726
|
+
const topSpend = entries.reduce((m, [, d]) => Math.max(m, d.total_spend), 0);
|
|
5727
|
+
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);
|
|
5728
|
+
const days = usage.daily_usage?.length ?? 0;
|
|
5729
|
+
const projected = days > 0 ? spend / days * 30 : null;
|
|
5730
|
+
return {
|
|
5731
|
+
costPer1kTokens: tokens > 0 ? spend / tokens * 1e3 : null,
|
|
5732
|
+
costPerRequest: reqs > 0 ? spend / reqs : null,
|
|
5733
|
+
outputInputRatio: prompt > 0 ? completion / prompt : null,
|
|
5734
|
+
retryWasteUsd: spend * wasteShare,
|
|
5735
|
+
retryWasteShare: wasteShare,
|
|
5736
|
+
successRate: reqs > 0 ? ok / reqs : null,
|
|
5737
|
+
premiumShare: spend > 0 ? premiumSpend / spend : 0,
|
|
5738
|
+
premiumSpend,
|
|
5739
|
+
topModelConcentration: spend > 0 ? topSpend / spend : 0,
|
|
5740
|
+
projectedMonthSpend: projected,
|
|
5741
|
+
burnMultiple: projected !== null && spend > 0 ? projected / spend : null
|
|
5742
|
+
};
|
|
5739
5743
|
};
|
|
5740
|
-
|
|
5741
|
-
const
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
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
|
-
] });
|
|
5744
|
+
scorePractices = (summary, keyStats) => {
|
|
5745
|
+
const band = (v, limits) => {
|
|
5746
|
+
for (const [max, pts] of limits) {
|
|
5747
|
+
if (v <= max) return pts;
|
|
5748
|
+
}
|
|
5749
|
+
return 0;
|
|
5750
|
+
};
|
|
5751
|
+
const waste = band(summary.retryWasteShare, [[0.01, 30], [0.05, 20], [0.1, 10]]);
|
|
5752
|
+
const premium = band(summary.premiumShare, [[0.2, 25], [0.5, 15], [0.8, 5]]);
|
|
5753
|
+
const conc = band(summary.topModelConcentration, [[0.5, 20], [0.8, 10]]);
|
|
5754
|
+
const bad = keyStats.totalKeys > 0 ? (keyStats.idleKeys + keyStats.uncappedKeys) / keyStats.totalKeys : 0;
|
|
5755
|
+
const hygiene = keyStats.totalKeys === 0 ? 25 : band(bad, [[0, 25], [0.2, 15], [0.5, 5]]);
|
|
5756
|
+
const checks = [
|
|
5757
|
+
{ id: "retry-waste", label: "Low retry waste", points: waste, maxPoints: 30, detail: `${(summary.retryWasteShare * 100).toFixed(1)}% of requests failed` },
|
|
5758
|
+
{ id: "premium-mix", label: "Balanced model mix", points: premium, maxPoints: 25, detail: `${(summary.premiumShare * 100).toFixed(1)}% of spend on premium models` },
|
|
5759
|
+
{ id: "concentration", label: "No single-model lock-in", points: conc, maxPoints: 20, detail: `Top model is ${(summary.topModelConcentration * 100).toFixed(1)}% of spend` },
|
|
5760
|
+
{ id: "key-hygiene", label: "Key hygiene", points: hygiene, maxPoints: 25, detail: `${keyStats.idleKeys} idle, ${keyStats.uncappedKeys} uncapped of ${keyStats.totalKeys} keys` }
|
|
5761
|
+
];
|
|
5762
|
+
return { total: checks.reduce((s, c) => s + c.points, 0), checks };
|
|
5821
5763
|
};
|
|
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
|
-
]
|
|
5764
|
+
modelOpportunities = (usage, models, premiumThresholdPer1k = 0.01) => {
|
|
5765
|
+
if (!usage) return [];
|
|
5766
|
+
const entries = Object.entries(usage.usage_by_model ?? {});
|
|
5767
|
+
const overallRate = usage.api_requests > 0 ? usage.failed_requests / usage.api_requests : 0;
|
|
5768
|
+
return entries.map(([model, d]) => {
|
|
5769
|
+
const failureRate = d.api_requests > 0 ? d.failed_requests / d.api_requests : null;
|
|
5770
|
+
const avgCostPerRequest = d.api_requests > 0 ? d.total_spend / d.api_requests : 0;
|
|
5771
|
+
const completionShare = d.total_tokens > 0 ? d.completion_tokens / d.total_tokens : 0;
|
|
5772
|
+
const per1k = costPer1k(model, d.total_spend, d.total_tokens, models) ?? 0;
|
|
5773
|
+
const spendShare = usage.total_spend > 0 ? d.total_spend / usage.total_spend : 0;
|
|
5774
|
+
if (failureRate !== null && overallRate > 0 && failureRate > 2 * overallRate && d.api_requests >= 10) {
|
|
5775
|
+
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
5776
|
}
|
|
5931
|
-
|
|
5777
|
+
if (per1k > premiumThresholdPer1k && completionShare < 0.3 && spendShare > 0.05) {
|
|
5778
|
+
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` };
|
|
5779
|
+
}
|
|
5780
|
+
return { model, spend: d.total_spend, requests: d.api_requests, failureRate, avgCostPerRequest, flag: "ok", hint: "No action" };
|
|
5781
|
+
});
|
|
5932
5782
|
};
|
|
5933
5783
|
}
|
|
5934
5784
|
});
|
|
@@ -5953,6 +5803,312 @@ var init_dates = __esm({
|
|
|
5953
5803
|
}
|
|
5954
5804
|
});
|
|
5955
5805
|
|
|
5806
|
+
// src/components/InsightsTab.tsx
|
|
5807
|
+
import { useMemo as useMemo8 } from "react";
|
|
5808
|
+
import Box16 from "@mui/material/Box";
|
|
5809
|
+
import Grid2 from "@mui/material/Grid";
|
|
5810
|
+
import Typography14 from "@mui/material/Typography";
|
|
5811
|
+
import Alert5 from "@mui/material/Alert";
|
|
5812
|
+
import Button7 from "@mui/material/Button";
|
|
5813
|
+
import Table6 from "@mui/material/Table";
|
|
5814
|
+
import TableBody6 from "@mui/material/TableBody";
|
|
5815
|
+
import TableCell6 from "@mui/material/TableCell";
|
|
5816
|
+
import TableContainer6 from "@mui/material/TableContainer";
|
|
5817
|
+
import TableHead6 from "@mui/material/TableHead";
|
|
5818
|
+
import TableRow6 from "@mui/material/TableRow";
|
|
5819
|
+
import Skeleton6 from "@mui/material/Skeleton";
|
|
5820
|
+
import { useAsyncRetry } from "react-use";
|
|
5821
|
+
import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
5822
|
+
var FLAG_TONE, FLAG_LABEL, fmtRatio, fmtPct1, failedRow, InsightsTab;
|
|
5823
|
+
var init_InsightsTab = __esm({
|
|
5824
|
+
"src/components/InsightsTab.tsx"() {
|
|
5825
|
+
"use strict";
|
|
5826
|
+
init_insights();
|
|
5827
|
+
init_format();
|
|
5828
|
+
init_dates();
|
|
5829
|
+
init_ui();
|
|
5830
|
+
FLAG_TONE = {
|
|
5831
|
+
"failure-outlier": "danger",
|
|
5832
|
+
"downgrade-candidate": "warning",
|
|
5833
|
+
ok: "success"
|
|
5834
|
+
};
|
|
5835
|
+
FLAG_LABEL = {
|
|
5836
|
+
"failure-outlier": "Failure outlier",
|
|
5837
|
+
"downgrade-candidate": "Downgrade candidate",
|
|
5838
|
+
ok: "OK"
|
|
5839
|
+
};
|
|
5840
|
+
fmtRatio = (n) => n === null || Number.isNaN(n) ? "\u2014" : `${n.toFixed(2)}\xD7`;
|
|
5841
|
+
fmtPct1 = (n) => n === null || Number.isNaN(n) ? "\u2014" : `${(n * 100).toFixed(1)}%`;
|
|
5842
|
+
failedRow = (status) => /fail|error/i.test(status ?? "");
|
|
5843
|
+
InsightsTab = ({
|
|
5844
|
+
api,
|
|
5845
|
+
usage,
|
|
5846
|
+
models,
|
|
5847
|
+
keys,
|
|
5848
|
+
dateRange,
|
|
5849
|
+
loading
|
|
5850
|
+
}) => {
|
|
5851
|
+
const summary = useMemo8(() => summarizeUsage(usage, models), [usage, models]);
|
|
5852
|
+
const keyStats = useMemo8(
|
|
5853
|
+
() => ({
|
|
5854
|
+
idleKeys: keys.filter((k) => (k.spend ?? 0) === 0).length,
|
|
5855
|
+
uncappedKeys: keys.filter((k) => k.max_budget === void 0 || k.max_budget === null).length,
|
|
5856
|
+
totalKeys: keys.length
|
|
5857
|
+
}),
|
|
5858
|
+
[keys]
|
|
5859
|
+
);
|
|
5860
|
+
const score = useMemo8(
|
|
5861
|
+
() => scorePractices(summary, keyStats),
|
|
5862
|
+
[summary, keyStats]
|
|
5863
|
+
);
|
|
5864
|
+
const opportunities = useMemo8(() => {
|
|
5865
|
+
const opps = modelOpportunities(usage, models);
|
|
5866
|
+
return [...opps].sort(
|
|
5867
|
+
(a, b) => (a.flag === "ok" ? 1 : 0) - (b.flag === "ok" ? 1 : 0)
|
|
5868
|
+
);
|
|
5869
|
+
}, [usage, models]);
|
|
5870
|
+
const {
|
|
5871
|
+
value: spendLogs,
|
|
5872
|
+
loading: logsLoading,
|
|
5873
|
+
error: logsError,
|
|
5874
|
+
retry: retryLogs
|
|
5875
|
+
} = useAsyncRetry(async () => {
|
|
5876
|
+
return api.getSpendLogs({
|
|
5877
|
+
startDate: toLocalDay(dateRange.start),
|
|
5878
|
+
endDate: toLocalDay(dateRange.end),
|
|
5879
|
+
pageSize: 1e3
|
|
5880
|
+
});
|
|
5881
|
+
}, [api, dateRange]);
|
|
5882
|
+
const {
|
|
5883
|
+
value: quality,
|
|
5884
|
+
loading: qualityLoading,
|
|
5885
|
+
error: qualityError,
|
|
5886
|
+
retry: retryQuality
|
|
5887
|
+
} = useAsyncRetry(async () => {
|
|
5888
|
+
if (typeof api.getQuality !== "function") return { configured: false };
|
|
5889
|
+
return api.getQuality({
|
|
5890
|
+
startDate: toLocalDay(dateRange.start),
|
|
5891
|
+
endDate: toLocalDay(dateRange.end)
|
|
5892
|
+
});
|
|
5893
|
+
}, [api, dateRange]);
|
|
5894
|
+
const renderQualityBody = () => {
|
|
5895
|
+
if (qualityLoading) return /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 120 });
|
|
5896
|
+
if (qualityError) {
|
|
5897
|
+
return /* @__PURE__ */ jsxs16(Alert5, { severity: "error", children: [
|
|
5898
|
+
"Failed to load quality scores: ",
|
|
5899
|
+
qualityError.message || "Unknown error",
|
|
5900
|
+
/* @__PURE__ */ jsx17(Box16, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx17(Button7, { size: "small", onClick: retryQuality, children: "Retry" }) })
|
|
5901
|
+
] });
|
|
5902
|
+
}
|
|
5903
|
+
if (!quality || quality.configured === false) {
|
|
5904
|
+
return /* @__PURE__ */ jsx17(
|
|
5905
|
+
EmptyState,
|
|
5906
|
+
{
|
|
5907
|
+
message: "Quality scores \u2014 connect Langfuse",
|
|
5908
|
+
hint: "LLM-as-judge evaluation is not configured for this deployment."
|
|
5909
|
+
}
|
|
5910
|
+
);
|
|
5911
|
+
}
|
|
5912
|
+
if (quality.byModel.length === 0 && quality.scores.length === 0) {
|
|
5913
|
+
return /* @__PURE__ */ jsx17(EmptyState, { message: "No quality data in this period" });
|
|
5914
|
+
}
|
|
5915
|
+
return /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
5916
|
+
quality.byModel.length > 0 && /* @__PURE__ */ jsx17(Box16, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: quality.byModel.map((m) => /* @__PURE__ */ jsx17(Box16, { children: /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", justifyContent: "space-between", gap: 1, mb: 0.5 }, children: [
|
|
5917
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "body2", sx: { fontWeight: 600 }, children: m.model }),
|
|
5918
|
+
/* @__PURE__ */ jsxs16(Typography14, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" }, children: [
|
|
5919
|
+
fmtUsd(m.cost),
|
|
5920
|
+
" \xB7 ",
|
|
5921
|
+
fmtInt(m.tokens),
|
|
5922
|
+
" tokens"
|
|
5923
|
+
] })
|
|
5924
|
+
] }) }, m.model)) }),
|
|
5925
|
+
quality.scores.length > 0 && /* @__PURE__ */ jsx17(Box16, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: quality.scores.map((s) => /* @__PURE__ */ jsxs16(Box16, { children: [
|
|
5926
|
+
/* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", justifyContent: "space-between", gap: 1, mb: 0.5 }, children: [
|
|
5927
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "body2", sx: { fontWeight: 600 }, children: s.name }),
|
|
5928
|
+
/* @__PURE__ */ jsxs16(Typography14, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" }, children: [
|
|
5929
|
+
s.avg.toFixed(2),
|
|
5930
|
+
s.count > 0 ? ` \xB7 n=${fmtInt(s.count)}` : ""
|
|
5931
|
+
] })
|
|
5932
|
+
] }),
|
|
5933
|
+
/* @__PURE__ */ jsx17(
|
|
5934
|
+
Meter,
|
|
5935
|
+
{
|
|
5936
|
+
value: s.avg <= 1 ? s.avg * 100 : s.avg,
|
|
5937
|
+
ariaLabel: `${s.name}: average ${s.avg.toFixed(2)}`
|
|
5938
|
+
}
|
|
5939
|
+
)
|
|
5940
|
+
] }, s.name)) })
|
|
5941
|
+
] });
|
|
5942
|
+
};
|
|
5943
|
+
const logStats = useMemo8(() => {
|
|
5944
|
+
const rows = spendLogs?.data ?? [];
|
|
5945
|
+
const hits = rows.filter((r) => r.cacheHit);
|
|
5946
|
+
const cachedPromptTokens = hits.reduce((s, r) => s + (r.promptTokens ?? 0), 0);
|
|
5947
|
+
const priced = models.map((m) => m.input_cost_per_token).filter((p) => p !== null && p !== void 0 && p > 0);
|
|
5948
|
+
const avgInputPrice = priced.length > 0 ? priced.reduce((s, p) => s + p, 0) / priced.length : null;
|
|
5949
|
+
return {
|
|
5950
|
+
total: spendLogs?.total ?? rows.length,
|
|
5951
|
+
hitRate: rows.length > 0 ? hits.length / rows.length : null,
|
|
5952
|
+
// Estimate: cached prompt tokens billed at the average listed input price.
|
|
5953
|
+
savedUsd: avgInputPrice !== null ? cachedPromptTokens * avgInputPrice : null,
|
|
5954
|
+
failedWasteUsd: rows.filter((r) => failedRow(r.status)).reduce((s, r) => s + (r.spend ?? 0), 0),
|
|
5955
|
+
truncated: spendLogs?.truncated ?? false
|
|
5956
|
+
};
|
|
5957
|
+
}, [spendLogs, models]);
|
|
5958
|
+
const renderSpendLogsBody = () => {
|
|
5959
|
+
if (logsLoading) return /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 120 });
|
|
5960
|
+
if (logsError) {
|
|
5961
|
+
return /* @__PURE__ */ jsxs16(Alert5, { severity: "error", children: [
|
|
5962
|
+
"Failed to load spend logs: ",
|
|
5963
|
+
logsError.message || "Unknown error",
|
|
5964
|
+
/* @__PURE__ */ jsx17(Box16, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx17(Button7, { size: "small", onClick: retryLogs, children: "Retry" }) })
|
|
5965
|
+
] });
|
|
5966
|
+
}
|
|
5967
|
+
if ((spendLogs?.data ?? []).length === 0) {
|
|
5968
|
+
return /* @__PURE__ */ jsx17(EmptyState, { message: "No spend logs in this period" });
|
|
5969
|
+
}
|
|
5970
|
+
return /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", gap: 3, flexWrap: "wrap" }, children: [
|
|
5971
|
+
/* @__PURE__ */ jsx17(Stat, { label: "Cache-hit rate", value: fmtPct1(logStats.hitRate) }),
|
|
5972
|
+
/* @__PURE__ */ jsx17(
|
|
5973
|
+
Stat,
|
|
5974
|
+
{
|
|
5975
|
+
label: "Cache savings (est.)",
|
|
5976
|
+
value: logStats.savedUsd !== null ? fmtUsd(logStats.savedUsd) : "\u2014"
|
|
5977
|
+
}
|
|
5978
|
+
),
|
|
5979
|
+
/* @__PURE__ */ jsx17(Stat, { label: "Failed-request waste", value: fmtUsd(logStats.failedWasteUsd) }),
|
|
5980
|
+
/* @__PURE__ */ jsx17(Stat, { label: "Log rows", value: fmtInt(logStats.total) }),
|
|
5981
|
+
logStats.truncated && /* @__PURE__ */ jsx17(Typography14, { variant: "caption", color: "text.secondary", children: "Showing the first 1,000 rows \u2014 figures are a lower bound." })
|
|
5982
|
+
] });
|
|
5983
|
+
};
|
|
5984
|
+
const kpis = [
|
|
5985
|
+
{
|
|
5986
|
+
label: "Cost / 1k tokens",
|
|
5987
|
+
value: summary.costPer1kTokens !== null ? fmtUsd(summary.costPer1kTokens) : "\u2014",
|
|
5988
|
+
tone: "accent"
|
|
5989
|
+
},
|
|
5990
|
+
{
|
|
5991
|
+
label: "Cost / request",
|
|
5992
|
+
value: summary.costPerRequest !== null ? fmtUsd(summary.costPerRequest) : "\u2014",
|
|
5993
|
+
tone: "info"
|
|
5994
|
+
},
|
|
5995
|
+
{
|
|
5996
|
+
label: "Out : in ratio",
|
|
5997
|
+
value: fmtRatio(summary.outputInputRatio),
|
|
5998
|
+
tone: "neutral"
|
|
5999
|
+
},
|
|
6000
|
+
{
|
|
6001
|
+
label: "Retry waste",
|
|
6002
|
+
value: fmtUsd(summary.retryWasteUsd),
|
|
6003
|
+
hint: `${fmtPct1(summary.retryWasteShare)} of spend`,
|
|
6004
|
+
tone: "warning"
|
|
6005
|
+
},
|
|
6006
|
+
{
|
|
6007
|
+
label: "Success rate",
|
|
6008
|
+
value: fmtPct1(summary.successRate),
|
|
6009
|
+
tone: "success"
|
|
6010
|
+
}
|
|
6011
|
+
];
|
|
6012
|
+
const renderEfficiencyBody = () => {
|
|
6013
|
+
if (loading) return /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 96 });
|
|
6014
|
+
if (!usage) return /* @__PURE__ */ jsx17(EmptyState, { message: "No usage in this period" });
|
|
6015
|
+
return /* @__PURE__ */ jsx17(MetricStrip, { metrics: kpis });
|
|
6016
|
+
};
|
|
6017
|
+
return /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
6018
|
+
/* @__PURE__ */ jsx17(
|
|
6019
|
+
SectionCard,
|
|
6020
|
+
{
|
|
6021
|
+
title: "Efficiency",
|
|
6022
|
+
subtitle: `${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
6023
|
+
description: "What each token and request costs, and how much failed requests waste.",
|
|
6024
|
+
children: renderEfficiencyBody()
|
|
6025
|
+
}
|
|
6026
|
+
),
|
|
6027
|
+
/* @__PURE__ */ jsxs16(Grid2, { container: true, spacing: 2, alignItems: "flex-start", children: [
|
|
6028
|
+
/* @__PURE__ */ jsx17(Grid2, { item: true, xs: 12, md: 5, children: /* @__PURE__ */ jsx17(
|
|
6029
|
+
SectionCard,
|
|
6030
|
+
{
|
|
6031
|
+
title: "Practice score",
|
|
6032
|
+
subtitle: "Out of 100",
|
|
6033
|
+
description: "Five everyday habits in one number \u2014 higher means cheaper, safer usage.",
|
|
6034
|
+
children: loading ? /* @__PURE__ */ jsx17(Skeleton6, { variant: "rounded", height: 160 }) : /* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
|
|
6035
|
+
/* @__PURE__ */ jsxs16(Typography14, { sx: { fontSize: 30, fontWeight: 700, fontVariantNumeric: "tabular-nums" }, children: [
|
|
6036
|
+
score.total,
|
|
6037
|
+
/* @__PURE__ */ jsx17(Typography14, { component: "span", color: "text.secondary", sx: { fontSize: 16 }, children: "/100" })
|
|
6038
|
+
] }),
|
|
6039
|
+
score.checks.map((c) => /* @__PURE__ */ jsxs16(Box16, { children: [
|
|
6040
|
+
/* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex", justifyContent: "space-between", gap: 1, mb: 0.5 }, children: [
|
|
6041
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "body2", sx: { fontWeight: 600 }, children: c.label }),
|
|
6042
|
+
/* @__PURE__ */ jsxs16(Typography14, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" }, children: [
|
|
6043
|
+
c.points,
|
|
6044
|
+
"/",
|
|
6045
|
+
c.maxPoints
|
|
6046
|
+
] })
|
|
6047
|
+
] }),
|
|
6048
|
+
/* @__PURE__ */ jsx17(
|
|
6049
|
+
Meter,
|
|
6050
|
+
{
|
|
6051
|
+
value: c.maxPoints > 0 ? c.points / c.maxPoints * 100 : 0,
|
|
6052
|
+
ariaLabel: `${c.label}: ${c.points} of ${c.maxPoints}`
|
|
6053
|
+
}
|
|
6054
|
+
),
|
|
6055
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "caption", color: "text.secondary", children: c.detail })
|
|
6056
|
+
] }, c.id))
|
|
6057
|
+
] })
|
|
6058
|
+
}
|
|
6059
|
+
) }),
|
|
6060
|
+
/* @__PURE__ */ jsx17(Grid2, { item: true, xs: 12, md: 7, children: /* @__PURE__ */ jsx17(
|
|
6061
|
+
SectionCard,
|
|
6062
|
+
{
|
|
6063
|
+
title: "Cache & errors",
|
|
6064
|
+
subtitle: "From spend logs (estimate)",
|
|
6065
|
+
description: "Repeated prompts served from cache save money; failures still bill.",
|
|
6066
|
+
children: renderSpendLogsBody()
|
|
6067
|
+
}
|
|
6068
|
+
) })
|
|
6069
|
+
] }),
|
|
6070
|
+
/* @__PURE__ */ jsx17(
|
|
6071
|
+
SectionCard,
|
|
6072
|
+
{
|
|
6073
|
+
title: "Model opportunities",
|
|
6074
|
+
description: "Where a cheaper model or a reliability fix would save the most.",
|
|
6075
|
+
flush: true,
|
|
6076
|
+
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: [
|
|
6077
|
+
/* @__PURE__ */ jsx17(TableHead6, { children: /* @__PURE__ */ jsxs16(TableRow6, { children: [
|
|
6078
|
+
/* @__PURE__ */ jsx17(TableCell6, { children: "Model" }),
|
|
6079
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Spend" }),
|
|
6080
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Requests" }),
|
|
6081
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Failure" }),
|
|
6082
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Avg / req" }),
|
|
6083
|
+
/* @__PURE__ */ jsx17(TableCell6, { children: "Signal" })
|
|
6084
|
+
] }) }),
|
|
6085
|
+
/* @__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: [
|
|
6086
|
+
/* @__PURE__ */ jsx17(TableCell6, { children: /* @__PURE__ */ jsx17(Typography14, { variant: "body2", sx: { fontWeight: 600 }, children: o.model }) }),
|
|
6087
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtUsd(o.spend) }),
|
|
6088
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtInt(o.requests) }),
|
|
6089
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtPct1(o.failureRate) }),
|
|
6090
|
+
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtUsd(o.avgCostPerRequest) }),
|
|
6091
|
+
/* @__PURE__ */ jsxs16(TableCell6, { children: [
|
|
6092
|
+
/* @__PURE__ */ jsx17(StatusPill, { label: FLAG_LABEL[o.flag], tone: FLAG_TONE[o.flag], title: o.hint }),
|
|
6093
|
+
/* @__PURE__ */ jsx17(Typography14, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.25 }, children: o.hint })
|
|
6094
|
+
] })
|
|
6095
|
+
] }, o.model)) })
|
|
6096
|
+
] }) })
|
|
6097
|
+
}
|
|
6098
|
+
),
|
|
6099
|
+
/* @__PURE__ */ jsx17(
|
|
6100
|
+
SectionCard,
|
|
6101
|
+
{
|
|
6102
|
+
title: "Quality scores",
|
|
6103
|
+
description: "Independent answer-quality scores from Langfuse, when connected.",
|
|
6104
|
+
children: renderQualityBody()
|
|
6105
|
+
}
|
|
6106
|
+
)
|
|
6107
|
+
] });
|
|
6108
|
+
};
|
|
6109
|
+
}
|
|
6110
|
+
});
|
|
6111
|
+
|
|
5956
6112
|
// src/feedback.ts
|
|
5957
6113
|
function toastFor(kind, errorMsg) {
|
|
5958
6114
|
switch (kind) {
|
|
@@ -6047,20 +6203,20 @@ var LiteLLMPage_exports = {};
|
|
|
6047
6203
|
__export(LiteLLMPage_exports, {
|
|
6048
6204
|
LiteLLMPage: () => LiteLLMPage
|
|
6049
6205
|
});
|
|
6050
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo9, useState as
|
|
6206
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo9, useState as useState11 } from "react";
|
|
6051
6207
|
import Box17 from "@mui/material/Box";
|
|
6052
6208
|
import Alert6 from "@mui/material/Alert";
|
|
6053
|
-
import
|
|
6209
|
+
import Button8 from "@mui/material/Button";
|
|
6054
6210
|
import Typography15 from "@mui/material/Typography";
|
|
6055
6211
|
import Tabs2 from "@mui/material/Tabs";
|
|
6056
6212
|
import Tab2 from "@mui/material/Tab";
|
|
6057
|
-
import
|
|
6213
|
+
import Grid3 from "@mui/material/Grid";
|
|
6058
6214
|
import Accordion2 from "@mui/material/Accordion";
|
|
6059
6215
|
import AccordionSummary2 from "@mui/material/AccordionSummary";
|
|
6060
6216
|
import AccordionDetails2 from "@mui/material/AccordionDetails";
|
|
6061
6217
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
6062
6218
|
import { useSearchParams } from "react-router-dom";
|
|
6063
|
-
import { useAsync as
|
|
6219
|
+
import { useAsync, useAsyncRetry as useAsyncRetry2 } from "react-use";
|
|
6064
6220
|
import { useApi as useApi4, alertApiRef as alertApiRef2 } from "@backstage/core-plugin-api";
|
|
6065
6221
|
import { Content, Progress } from "@backstage/core-components";
|
|
6066
6222
|
import { usePermission as usePermission3 } from "@backstage/plugin-permission-react";
|
|
@@ -6098,20 +6254,20 @@ var init_LiteLLMPage = __esm({
|
|
|
6098
6254
|
init_LiteLLMBudgetWidget();
|
|
6099
6255
|
init_TeamUsage();
|
|
6100
6256
|
init_ModelsTable();
|
|
6101
|
-
|
|
6257
|
+
init_InsightsTab();
|
|
6102
6258
|
init_api();
|
|
6103
6259
|
init_dates();
|
|
6104
6260
|
init_feedback();
|
|
6105
6261
|
init_teamMemberManager();
|
|
6106
6262
|
init_unprovisionedCopy();
|
|
6107
6263
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
6108
|
-
PAGE_TABS = ["overview", "keys", "teams", "models", "
|
|
6264
|
+
PAGE_TABS = ["overview", "keys", "teams", "models", "insights"];
|
|
6109
6265
|
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
6110
6266
|
LiteLLMPage = () => {
|
|
6111
6267
|
const api = useApi4(liteLlmApiRef);
|
|
6112
6268
|
const alertApi = useApi4(alertApiRef2);
|
|
6113
|
-
const [dateRange, setDateRange] =
|
|
6114
|
-
const [currentPreset, setCurrentPreset] =
|
|
6269
|
+
const [dateRange, setDateRange] = useState11(initDateRange);
|
|
6270
|
+
const [currentPreset, setCurrentPreset] = useState11(() => {
|
|
6115
6271
|
try {
|
|
6116
6272
|
return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
|
|
6117
6273
|
} catch {
|
|
@@ -6119,15 +6275,15 @@ var init_LiteLLMPage = __esm({
|
|
|
6119
6275
|
}
|
|
6120
6276
|
});
|
|
6121
6277
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
6122
|
-
const [activeTab, setActiveTab] =
|
|
6278
|
+
const [activeTab, setActiveTab] = useState11(() => {
|
|
6123
6279
|
const t = searchParams.get("tab");
|
|
6124
6280
|
return isPageTab(t) ? t : "overview";
|
|
6125
6281
|
});
|
|
6126
|
-
const [filter, setFilter] =
|
|
6282
|
+
const [filter, setFilter] = useState11(() => {
|
|
6127
6283
|
const f = searchParams.get("filter");
|
|
6128
6284
|
return f === "expired" || f === "expiring" ? f : void 0;
|
|
6129
6285
|
});
|
|
6130
|
-
const [generateDialogOpen, setGenerateDialogOpen] =
|
|
6286
|
+
const [generateDialogOpen, setGenerateDialogOpen] = useState11(
|
|
6131
6287
|
() => searchParams.get("generate") === "1"
|
|
6132
6288
|
);
|
|
6133
6289
|
useEffect6(() => {
|
|
@@ -6183,31 +6339,31 @@ var init_LiteLLMPage = __esm({
|
|
|
6183
6339
|
{ replace: true }
|
|
6184
6340
|
);
|
|
6185
6341
|
}, [setSearchParams]);
|
|
6186
|
-
const [manageTeam, setManageTeam] =
|
|
6187
|
-
const [keyToEdit, setKeyToEdit] =
|
|
6342
|
+
const [manageTeam, setManageTeam] = useState11(null);
|
|
6343
|
+
const [keyToEdit, setKeyToEdit] = useState11(null);
|
|
6188
6344
|
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 } =
|
|
6345
|
+
const [teamUsageCache, setTeamUsageCache] = useState11({});
|
|
6346
|
+
const [teamUsageLoading, setTeamUsageLoading] = useState11({});
|
|
6347
|
+
const [memberUsageCache, setMemberUsageCache] = useState11({});
|
|
6348
|
+
const [memberUsageLoading, setMemberUsageLoading] = useState11({});
|
|
6349
|
+
const { value: userInfo, loading: userLoading, error: userError } = useAsync(
|
|
6194
6350
|
() => api.getUserInfo(),
|
|
6195
6351
|
[api]
|
|
6196
6352
|
);
|
|
6197
|
-
const { value: keys, loading: keysLoading, error: keysError, retry: refreshKeys } =
|
|
6353
|
+
const { value: keys, loading: keysLoading, error: keysError, retry: refreshKeys } = useAsyncRetry2(
|
|
6198
6354
|
() => api.listKeys(),
|
|
6199
6355
|
[api]
|
|
6200
6356
|
);
|
|
6201
|
-
const { value: allModels, loading: modelsLoading, error: modelsError, retry: refreshModels } =
|
|
6357
|
+
const { value: allModels, loading: modelsLoading, error: modelsError, retry: refreshModels } = useAsyncRetry2(
|
|
6202
6358
|
() => api.listModels(),
|
|
6203
6359
|
[api]
|
|
6204
6360
|
);
|
|
6205
|
-
const { value: allTeams, loading: teamsLoading, error: teamsError, retry: refreshTeams } =
|
|
6361
|
+
const { value: allTeams, loading: teamsLoading, error: teamsError, retry: refreshTeams } = useAsyncRetry2(
|
|
6206
6362
|
() => api.getTeams(),
|
|
6207
6363
|
[api]
|
|
6208
6364
|
);
|
|
6209
|
-
const { value: liteLlmConfig } =
|
|
6210
|
-
const { value: managedTeams } =
|
|
6365
|
+
const { value: liteLlmConfig } = useAsync(() => api.getConfig(), [api]);
|
|
6366
|
+
const { value: managedTeams } = useAsync(
|
|
6211
6367
|
async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
|
|
6212
6368
|
[api, liteLlmConfig]
|
|
6213
6369
|
);
|
|
@@ -6218,11 +6374,11 @@ var init_LiteLLMPage = __esm({
|
|
|
6218
6374
|
const { allowed: canManageMcpServers } = usePermission3({ permission: litellmTeamMcpManagePermission });
|
|
6219
6375
|
const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
|
|
6220
6376
|
const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
|
|
6221
|
-
const { value: vectorStores } =
|
|
6377
|
+
const { value: vectorStores } = useAsync(
|
|
6222
6378
|
async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
|
|
6223
6379
|
[api, objectPermsEnabled]
|
|
6224
6380
|
);
|
|
6225
|
-
const { value: mcpServers } =
|
|
6381
|
+
const { value: mcpServers } = useAsync(
|
|
6226
6382
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
6227
6383
|
[api, objectPermsEnabled]
|
|
6228
6384
|
);
|
|
@@ -6238,7 +6394,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6238
6394
|
(t) => t.members_with_roles?.some((m) => m.user_id === userId)
|
|
6239
6395
|
);
|
|
6240
6396
|
}, [allTeams, userInfo]);
|
|
6241
|
-
const { value: usage, loading: usageLoading, error: usageError, retry: refreshUsage } =
|
|
6397
|
+
const { value: usage, loading: usageLoading, error: usageError, retry: refreshUsage } = useAsyncRetry2(async () => {
|
|
6242
6398
|
const startDate = toLocalDay(dateRange.start);
|
|
6243
6399
|
const endDate = toLocalDay(dateRange.end);
|
|
6244
6400
|
return api.getUsage(startDate, endDate);
|
|
@@ -6446,7 +6602,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6446
6602
|
/* @__PURE__ */ jsx18(Tab2, { label: "Keys", value: "keys" }),
|
|
6447
6603
|
/* @__PURE__ */ jsx18(Tab2, { label: "Teams", value: "teams" }),
|
|
6448
6604
|
/* @__PURE__ */ jsx18(Tab2, { label: "Models", value: "models" }),
|
|
6449
|
-
|
|
6605
|
+
/* @__PURE__ */ jsx18(Tab2, { label: "Insights", value: "insights" })
|
|
6450
6606
|
]
|
|
6451
6607
|
}
|
|
6452
6608
|
);
|
|
@@ -6500,7 +6656,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6500
6656
|
}
|
|
6501
6657
|
),
|
|
6502
6658
|
/* @__PURE__ */ jsx18(
|
|
6503
|
-
|
|
6659
|
+
Button8,
|
|
6504
6660
|
{
|
|
6505
6661
|
variant: "contained",
|
|
6506
6662
|
color: "primary",
|
|
@@ -6512,8 +6668,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6512
6668
|
}
|
|
6513
6669
|
) });
|
|
6514
6670
|
}
|
|
6515
|
-
return /* @__PURE__ */ jsxs17(
|
|
6516
|
-
/* @__PURE__ */ jsx18(
|
|
6671
|
+
return /* @__PURE__ */ jsxs17(Grid3, { container: true, spacing: 2, alignItems: "flex-start", children: [
|
|
6672
|
+
/* @__PURE__ */ jsx18(Grid3, { item: true, xs: 12, lg: 8, children: /* @__PURE__ */ jsx18(
|
|
6517
6673
|
UsageStats,
|
|
6518
6674
|
{
|
|
6519
6675
|
usage: usage ?? null,
|
|
@@ -6527,7 +6683,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6527
6683
|
userInfo
|
|
6528
6684
|
}
|
|
6529
6685
|
) }),
|
|
6530
|
-
/* @__PURE__ */ jsx18(
|
|
6686
|
+
/* @__PURE__ */ jsx18(Grid3, { item: true, xs: 12, lg: 4, children: /* @__PURE__ */ jsx18(LiteLLMBudgetWidget, { compact: true, collapsible: true, userInfo: userInfo ?? null, teams, keys: keys ?? [] }) })
|
|
6531
6687
|
] });
|
|
6532
6688
|
})(),
|
|
6533
6689
|
activeTab === "keys" && (() => {
|
|
@@ -6535,7 +6691,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6535
6691
|
return /* @__PURE__ */ jsxs17(Alert6, { severity: "error", children: [
|
|
6536
6692
|
"Failed to load keys: ",
|
|
6537
6693
|
keysError.message || "Unknown error",
|
|
6538
|
-
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(
|
|
6694
|
+
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(Button8, { size: "small", onClick: refreshKeys, children: "Retry" }) })
|
|
6539
6695
|
] });
|
|
6540
6696
|
}
|
|
6541
6697
|
return /* @__PURE__ */ jsx18(
|
|
@@ -6559,7 +6715,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6559
6715
|
return /* @__PURE__ */ jsxs17(Alert6, { severity: "error", children: [
|
|
6560
6716
|
"Failed to load teams: ",
|
|
6561
6717
|
teamsError.message || "Unknown error",
|
|
6562
|
-
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(
|
|
6718
|
+
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(Button8, { size: "small", onClick: refreshTeams, children: "Retry" }) })
|
|
6563
6719
|
] });
|
|
6564
6720
|
}
|
|
6565
6721
|
const teamsById = /* @__PURE__ */ new Map();
|
|
@@ -6596,7 +6752,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6596
6752
|
return /* @__PURE__ */ jsxs17(Alert6, { severity: "error", children: [
|
|
6597
6753
|
"Failed to load models: ",
|
|
6598
6754
|
modelsError.message || "Unknown error",
|
|
6599
|
-
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(
|
|
6755
|
+
/* @__PURE__ */ jsx18(Box17, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx18(Button8, { size: "small", onClick: refreshModels, children: "Retry" }) })
|
|
6600
6756
|
] });
|
|
6601
6757
|
}
|
|
6602
6758
|
return /* @__PURE__ */ jsx18(
|
|
@@ -6608,7 +6764,17 @@ var init_LiteLLMPage = __esm({
|
|
|
6608
6764
|
}
|
|
6609
6765
|
);
|
|
6610
6766
|
})(),
|
|
6611
|
-
activeTab === "
|
|
6767
|
+
activeTab === "insights" && /* @__PURE__ */ jsx18(
|
|
6768
|
+
InsightsTab,
|
|
6769
|
+
{
|
|
6770
|
+
api,
|
|
6771
|
+
usage: usage ?? null,
|
|
6772
|
+
models: allModels ?? [],
|
|
6773
|
+
keys: keys ?? [],
|
|
6774
|
+
dateRange,
|
|
6775
|
+
loading: usageLoading
|
|
6776
|
+
}
|
|
6777
|
+
)
|
|
6612
6778
|
] }),
|
|
6613
6779
|
/* @__PURE__ */ jsx18(
|
|
6614
6780
|
KeyFormDialog,
|
|
@@ -6782,17 +6948,17 @@ var ConnectPage_exports = {};
|
|
|
6782
6948
|
__export(ConnectPage_exports, {
|
|
6783
6949
|
ConnectPage: () => ConnectPage
|
|
6784
6950
|
});
|
|
6785
|
-
import { useState as
|
|
6951
|
+
import { useState as useState12 } from "react";
|
|
6786
6952
|
import Alert7 from "@mui/material/Alert";
|
|
6787
6953
|
import Box18 from "@mui/material/Box";
|
|
6788
|
-
import
|
|
6954
|
+
import Button9 from "@mui/material/Button";
|
|
6789
6955
|
import Checkbox3 from "@mui/material/Checkbox";
|
|
6790
6956
|
import FormControlLabel3 from "@mui/material/FormControlLabel";
|
|
6791
6957
|
import FormGroup from "@mui/material/FormGroup";
|
|
6792
6958
|
import Paper9 from "@mui/material/Paper";
|
|
6793
6959
|
import Typography16 from "@mui/material/Typography";
|
|
6794
6960
|
import { useSearchParams as useSearchParams2 } from "react-router-dom";
|
|
6795
|
-
import { useAsync as
|
|
6961
|
+
import { useAsync as useAsync2 } from "react-use";
|
|
6796
6962
|
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
6797
6963
|
import { Content as Content2, Progress as Progress2 } from "@backstage/core-components";
|
|
6798
6964
|
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
@@ -6809,10 +6975,10 @@ var init_ConnectPage = __esm({
|
|
|
6809
6975
|
const state = params.get("state") ?? "";
|
|
6810
6976
|
const client = params.get("client") || "a local application";
|
|
6811
6977
|
const have = new Set((params.get("have") ?? "").split(",").filter(Boolean));
|
|
6812
|
-
const teams =
|
|
6813
|
-
const [selected, setSelected] =
|
|
6814
|
-
const [busy, setBusy] =
|
|
6815
|
-
const [error, setError] =
|
|
6978
|
+
const teams = useAsync2(() => api.getTeams(), [api]);
|
|
6979
|
+
const [selected, setSelected] = useState12();
|
|
6980
|
+
const [busy, setBusy] = useState12(false);
|
|
6981
|
+
const [error, setError] = useState12();
|
|
6816
6982
|
if (!isLoopbackCallback(redirectUri)) {
|
|
6817
6983
|
return /* @__PURE__ */ jsx19(Content2, { children: /* @__PURE__ */ jsx19(Alert7, { severity: "error", children: "Invalid connection link: redirect_uri must be http://localhost:<port>/callback." }) });
|
|
6818
6984
|
}
|
|
@@ -6860,7 +7026,7 @@ var init_ConnectPage = __esm({
|
|
|
6860
7026
|
)) }),
|
|
6861
7027
|
error && /* @__PURE__ */ jsx19(Alert7, { severity: "error", sx: { mb: 2 }, children: error }),
|
|
6862
7028
|
/* @__PURE__ */ jsx19(
|
|
6863
|
-
|
|
7029
|
+
Button9,
|
|
6864
7030
|
{
|
|
6865
7031
|
variant: "contained",
|
|
6866
7032
|
onClick: connect,
|
|
@@ -6937,13 +7103,13 @@ init_format();
|
|
|
6937
7103
|
init_dates();
|
|
6938
7104
|
init_ui();
|
|
6939
7105
|
init_widgetState();
|
|
6940
|
-
import { useEffect as useEffect7, useMemo as useMemo10, useState as
|
|
7106
|
+
import { useEffect as useEffect7, useMemo as useMemo10, useState as useState13 } from "react";
|
|
6941
7107
|
import Paper10 from "@mui/material/Paper";
|
|
6942
7108
|
import Box19 from "@mui/material/Box";
|
|
6943
7109
|
import Typography17 from "@mui/material/Typography";
|
|
6944
7110
|
import FormControl2 from "@mui/material/FormControl";
|
|
6945
7111
|
import Select2 from "@mui/material/Select";
|
|
6946
|
-
import
|
|
7112
|
+
import MenuItem5 from "@mui/material/MenuItem";
|
|
6947
7113
|
import Skeleton7 from "@mui/material/Skeleton";
|
|
6948
7114
|
import { AreaChart as AreaChart3, Area as Area3, XAxis as XAxis3, Tooltip as Tooltip8, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
|
|
6949
7115
|
import { useApi as useApi6 } from "@backstage/core-plugin-api";
|
|
@@ -6993,7 +7159,7 @@ function usageSummary(usage) {
|
|
|
6993
7159
|
}
|
|
6994
7160
|
|
|
6995
7161
|
// src/components/LiteLLMHomeWidget.tsx
|
|
6996
|
-
import { Fragment as
|
|
7162
|
+
import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
6997
7163
|
function presetToDateRange(preset) {
|
|
6998
7164
|
const end = /* @__PURE__ */ new Date();
|
|
6999
7165
|
const start = /* @__PURE__ */ new Date();
|
|
@@ -7014,10 +7180,10 @@ var LiteLLMHomeWidget = ({
|
|
|
7014
7180
|
const api = useApi6(liteLlmApiRef);
|
|
7015
7181
|
const moduleRouteRef = useRouteRef2(rootRouteRef);
|
|
7016
7182
|
const { userInfo, keys, loading: profileLoading, error: profileError } = useLiteLLMProfile();
|
|
7017
|
-
const [period, setPeriod] =
|
|
7018
|
-
const [usageLoading, setUsageLoading] =
|
|
7019
|
-
const [usageError, setUsageError] =
|
|
7020
|
-
const [usage, setUsage] =
|
|
7183
|
+
const [period, setPeriod] = useState13(defaultPeriod);
|
|
7184
|
+
const [usageLoading, setUsageLoading] = useState13(true);
|
|
7185
|
+
const [usageError, setUsageError] = useState13(null);
|
|
7186
|
+
const [usage, setUsage] = useState13(null);
|
|
7021
7187
|
useEffect7(() => {
|
|
7022
7188
|
let cancelled = false;
|
|
7023
7189
|
setUsageLoading(true);
|
|
@@ -7073,9 +7239,9 @@ var LiteLLMHomeWidget = ({
|
|
|
7073
7239
|
displayEmpty: true,
|
|
7074
7240
|
inputProps: { "aria-label": "Usage period" },
|
|
7075
7241
|
children: [
|
|
7076
|
-
/* @__PURE__ */ jsx21(
|
|
7077
|
-
/* @__PURE__ */ jsx21(
|
|
7078
|
-
/* @__PURE__ */ jsx21(
|
|
7242
|
+
/* @__PURE__ */ jsx21(MenuItem5, { value: "today", children: "Today" }),
|
|
7243
|
+
/* @__PURE__ */ jsx21(MenuItem5, { value: "7d", children: "7d" }),
|
|
7244
|
+
/* @__PURE__ */ jsx21(MenuItem5, { value: "30d", children: "30d" })
|
|
7079
7245
|
]
|
|
7080
7246
|
}
|
|
7081
7247
|
) })
|
|
@@ -7087,7 +7253,7 @@ var LiteLLMHomeWidget = ({
|
|
|
7087
7253
|
viewState.kind === "error" && /* @__PURE__ */ jsx21(Typography17, { variant: "body2", color: "text.secondary", sx: { mt: 1 }, children: viewState.message }),
|
|
7088
7254
|
viewState.kind === "unprovisioned" && /* @__PURE__ */ jsx21(Typography17, { variant: "body2", color: "text.secondary", sx: { mt: 1 }, children: UNPROVISIONED_MSG }),
|
|
7089
7255
|
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 }) }),
|
|
7090
|
-
viewState.kind === "ready" && /* @__PURE__ */ jsxs19(
|
|
7256
|
+
viewState.kind === "ready" && /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
7091
7257
|
/* @__PURE__ */ jsx21(
|
|
7092
7258
|
Typography17,
|
|
7093
7259
|
{
|
|
@@ -7100,7 +7266,7 @@ var LiteLLMHomeWidget = ({
|
|
|
7100
7266
|
children: viewState.usageUnavailable ? USAGE_UNAVAILABLE_MSG : usageSummary(usage)
|
|
7101
7267
|
}
|
|
7102
7268
|
),
|
|
7103
|
-
hasSparkline && !viewState.usageUnavailable && /* @__PURE__ */ jsxs19(
|
|
7269
|
+
hasSparkline && !viewState.usageUnavailable && /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
7104
7270
|
/* @__PURE__ */ jsx21(
|
|
7105
7271
|
Box19,
|
|
7106
7272
|
{
|
|
@@ -7146,14 +7312,14 @@ init_useLiteLLMProfile();
|
|
|
7146
7312
|
init_api();
|
|
7147
7313
|
init_format();
|
|
7148
7314
|
init_dates();
|
|
7149
|
-
import { useCallback as useCallback5, useEffect as useEffect8, useId as useId2, useMemo as useMemo11, useState as
|
|
7315
|
+
import { useCallback as useCallback5, useEffect as useEffect8, useId as useId2, useMemo as useMemo11, useState as useState14 } from "react";
|
|
7150
7316
|
import { useRouteRef as useRouteRef3 } from "@backstage/frontend-plugin-api";
|
|
7151
7317
|
import Paper11 from "@mui/material/Paper";
|
|
7152
7318
|
import Box20 from "@mui/material/Box";
|
|
7153
|
-
import
|
|
7319
|
+
import Button10 from "@mui/material/Button";
|
|
7154
7320
|
import Typography18 from "@mui/material/Typography";
|
|
7155
7321
|
import Skeleton8 from "@mui/material/Skeleton";
|
|
7156
|
-
import
|
|
7322
|
+
import Collapse3 from "@mui/material/Collapse";
|
|
7157
7323
|
import { useTheme as useTheme4 } from "@mui/material/styles";
|
|
7158
7324
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
7159
7325
|
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
@@ -7186,7 +7352,7 @@ init_GenerateKeyButton();
|
|
|
7186
7352
|
init_BudgetLimitList();
|
|
7187
7353
|
init_budget();
|
|
7188
7354
|
init_routes();
|
|
7189
|
-
import { Fragment as
|
|
7355
|
+
import { Fragment as Fragment10, jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
7190
7356
|
var LEVEL = {
|
|
7191
7357
|
key: { name: "Key", none: "No cap (unlimited)" },
|
|
7192
7358
|
user: { name: "User", none: "No personal budget" },
|
|
@@ -7339,7 +7505,7 @@ var LevelRow = ({
|
|
|
7339
7505
|
),
|
|
7340
7506
|
/* @__PURE__ */ jsxs20(Box20, { sx: { minWidth: 0 }, children: [
|
|
7341
7507
|
/* @__PURE__ */ jsx22(Tag, { tone: tone === "warning" || tone === "danger" ? tone : void 0, children: name }),
|
|
7342
|
-
limit ? /* @__PURE__ */ jsxs20(
|
|
7508
|
+
limit ? /* @__PURE__ */ jsxs20(Fragment10, { children: [
|
|
7343
7509
|
/* @__PURE__ */ jsx22(
|
|
7344
7510
|
Typography18,
|
|
7345
7511
|
{
|
|
@@ -7570,17 +7736,17 @@ var LiteLLMBudgetGauges = ({
|
|
|
7570
7736
|
const moduleRouteRef = useRouteRef3(rootRouteRef);
|
|
7571
7737
|
const moduleHref = propModuleHref ?? moduleRouteRef?.();
|
|
7572
7738
|
const { userInfo: hookUserInfo, teams: hookTeams, keys: hookKeys, loading: profileLoading, error: profileError } = useLiteLLMProfile();
|
|
7573
|
-
const [usageLoading, setUsageLoading] =
|
|
7574
|
-
const [usageError, setUsageError] =
|
|
7575
|
-
const [dailySpend, setDailySpend] =
|
|
7576
|
-
const [mtdSpend, setMtdSpend] =
|
|
7739
|
+
const [usageLoading, setUsageLoading] = useState14(true);
|
|
7740
|
+
const [usageError, setUsageError] = useState14(null);
|
|
7741
|
+
const [dailySpend, setDailySpend] = useState14([]);
|
|
7742
|
+
const [mtdSpend, setMtdSpend] = useState14(0);
|
|
7577
7743
|
const user = propUserInfo !== void 0 ? propUserInfo : hookUserInfo;
|
|
7578
7744
|
const teams = propTeams !== void 0 ? propTeams : hookTeams;
|
|
7579
7745
|
const keys = propKeys !== void 0 ? propKeys : hookKeys;
|
|
7580
7746
|
const loading = propUserInfo === void 0 ? profileLoading : usageLoading;
|
|
7581
7747
|
const error = propUserInfo === void 0 ? profileError : usageError;
|
|
7582
7748
|
const isControlled = expanded !== void 0;
|
|
7583
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] =
|
|
7749
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = useState14(defaultExpanded);
|
|
7584
7750
|
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
7585
7751
|
const toggleExpanded = useCallback5(() => {
|
|
7586
7752
|
const next = !isExpanded;
|
|
@@ -7664,10 +7830,10 @@ var LiteLLMBudgetGauges = ({
|
|
|
7664
7830
|
return onCreateKey ? /* @__PURE__ */ jsx22(GenerateKeyButton, { size: "small", label, onClick: onCreateKey }, key) : /* @__PURE__ */ jsx22(GenerateKeyButton, { size: "small", label, to: `${moduleHref}?generate=1` }, key);
|
|
7665
7831
|
case "module":
|
|
7666
7832
|
if (!moduleHref) return null;
|
|
7667
|
-
return /* @__PURE__ */ jsx22(
|
|
7833
|
+
return /* @__PURE__ */ jsx22(Button10, { size: "small", variant: "text", component: Link4, to: moduleHref, children: label }, key);
|
|
7668
7834
|
case "all-limits":
|
|
7669
7835
|
return /* @__PURE__ */ jsx22(
|
|
7670
|
-
|
|
7836
|
+
Button10,
|
|
7671
7837
|
{
|
|
7672
7838
|
size: "small",
|
|
7673
7839
|
variant: "text",
|
|
@@ -7716,7 +7882,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
7716
7882
|
] }, i)) }),
|
|
7717
7883
|
viewState.kind === "error" && /* @__PURE__ */ jsx22(Typography18, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: viewState.message }),
|
|
7718
7884
|
viewState.kind === "unprovisioned" && /* @__PURE__ */ jsx22(Typography18, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: UNPROVISIONED_MSG }),
|
|
7719
|
-
viewState.kind === "ready" && /* @__PURE__ */ jsxs20(
|
|
7885
|
+
viewState.kind === "ready" && /* @__PURE__ */ jsxs20(Fragment10, { children: [
|
|
7720
7886
|
/* @__PURE__ */ jsxs20(
|
|
7721
7887
|
Box20,
|
|
7722
7888
|
{
|
|
@@ -7770,7 +7936,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
7770
7936
|
maxBudget: user?.max_budget ?? void 0
|
|
7771
7937
|
}
|
|
7772
7938
|
) }),
|
|
7773
|
-
/* @__PURE__ */ jsx22(
|
|
7939
|
+
/* @__PURE__ */ jsx22(Collapse3, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ jsx22(
|
|
7774
7940
|
Box20,
|
|
7775
7941
|
{
|
|
7776
7942
|
id: expandedRegionId,
|