@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.cjs.js
CHANGED
|
@@ -317,6 +317,25 @@ var init_api = __esm({
|
|
|
317
317
|
end_date: endDate
|
|
318
318
|
});
|
|
319
319
|
}
|
|
320
|
+
async getSpendLogs(params) {
|
|
321
|
+
const strParams = {
|
|
322
|
+
start_date: params.startDate,
|
|
323
|
+
end_date: params.endDate
|
|
324
|
+
};
|
|
325
|
+
if (params.userId) strParams.userId = params.userId;
|
|
326
|
+
if (params.teamId) strParams.teamId = params.teamId;
|
|
327
|
+
if (params.page !== void 0) strParams.page = String(params.page);
|
|
328
|
+
if (params.pageSize !== void 0) strParams.page_size = String(params.pageSize);
|
|
329
|
+
return this.get("/insights/spend-logs", strParams);
|
|
330
|
+
}
|
|
331
|
+
async getQuality(params) {
|
|
332
|
+
const strParams = {
|
|
333
|
+
start_date: params.startDate,
|
|
334
|
+
end_date: params.endDate
|
|
335
|
+
};
|
|
336
|
+
if (params.userId) strParams.userId = params.userId;
|
|
337
|
+
return this.get("/insights/quality", strParams);
|
|
338
|
+
}
|
|
320
339
|
async getConfig() {
|
|
321
340
|
return this.get("/config");
|
|
322
341
|
}
|
|
@@ -715,7 +734,7 @@ var init_surfaces = __esm({
|
|
|
715
734
|
import_styles3 = require("@mui/material/styles");
|
|
716
735
|
init_tokens();
|
|
717
736
|
import_jsx_runtime3 = require("react/jsx-runtime");
|
|
718
|
-
SectionCard = ({ title, subtitle, actions, flush = false, children }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_Paper2.default, { sx: { borderRadius: 2, overflow: "hidden" }, children: [
|
|
737
|
+
SectionCard = ({ title, subtitle, description, actions, flush = false, children }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_Paper2.default, { sx: { borderRadius: 2, overflow: "hidden" }, children: [
|
|
719
738
|
(title || actions) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
720
739
|
import_Box3.default,
|
|
721
740
|
{
|
|
@@ -737,6 +756,7 @@ var init_surfaces = __esm({
|
|
|
737
756
|
]
|
|
738
757
|
}
|
|
739
758
|
),
|
|
759
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Typography2.default, { variant: "body2", color: "text.secondary", sx: { px: 2.5, pb: 1.5 }, children: description }),
|
|
740
760
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Box3.default, { sx: flush ? void 0 : { px: 2.5, pb: 2.5, pt: title ? 0 : 2.5 }, children })
|
|
741
761
|
] });
|
|
742
762
|
ChartCard = ({ title, meta, height = 240, children }) => (
|
|
@@ -5661,295 +5681,431 @@ var init_ModelsTable = __esm({
|
|
|
5661
5681
|
}
|
|
5662
5682
|
});
|
|
5663
5683
|
|
|
5664
|
-
// src/
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5684
|
+
// src/insights.ts
|
|
5685
|
+
var costPer1k, summarizeUsage, scorePractices, modelOpportunities;
|
|
5686
|
+
var init_insights = __esm({
|
|
5687
|
+
"src/insights.ts"() {
|
|
5688
|
+
"use strict";
|
|
5689
|
+
costPer1k = (model, spend, tokens, models) => {
|
|
5690
|
+
const info = models?.find((m) => m.model_name === model);
|
|
5691
|
+
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;
|
|
5692
|
+
if (listed !== null) return listed;
|
|
5693
|
+
return tokens > 0 ? spend / tokens * 1e3 : null;
|
|
5694
|
+
};
|
|
5695
|
+
summarizeUsage = (usage, models, premiumThresholdPer1k = 0.01) => {
|
|
5696
|
+
if (!usage) {
|
|
5697
|
+
return {
|
|
5698
|
+
costPer1kTokens: null,
|
|
5699
|
+
costPerRequest: null,
|
|
5700
|
+
outputInputRatio: null,
|
|
5701
|
+
retryWasteUsd: 0,
|
|
5702
|
+
retryWasteShare: 0,
|
|
5703
|
+
successRate: null,
|
|
5704
|
+
premiumShare: 0,
|
|
5705
|
+
premiumSpend: 0,
|
|
5706
|
+
topModelConcentration: 0,
|
|
5707
|
+
projectedMonthSpend: null,
|
|
5708
|
+
burnMultiple: null
|
|
5709
|
+
};
|
|
5710
|
+
}
|
|
5711
|
+
const {
|
|
5712
|
+
total_spend: spend,
|
|
5713
|
+
total_tokens: tokens,
|
|
5714
|
+
prompt_tokens: prompt,
|
|
5715
|
+
completion_tokens: completion,
|
|
5716
|
+
api_requests: reqs,
|
|
5717
|
+
successful_requests: ok,
|
|
5718
|
+
failed_requests: failed,
|
|
5719
|
+
usage_by_model: byModel
|
|
5720
|
+
} = usage;
|
|
5721
|
+
const wasteShare = reqs > 0 ? failed / reqs : 0;
|
|
5722
|
+
const entries = Object.entries(byModel ?? {});
|
|
5723
|
+
const topSpend = entries.reduce((m, [, d]) => Math.max(m, d.total_spend), 0);
|
|
5724
|
+
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);
|
|
5725
|
+
const days = usage.daily_usage?.length ?? 0;
|
|
5726
|
+
const projected = days > 0 ? spend / days * 30 : null;
|
|
5727
|
+
return {
|
|
5728
|
+
costPer1kTokens: tokens > 0 ? spend / tokens * 1e3 : null,
|
|
5729
|
+
costPerRequest: reqs > 0 ? spend / reqs : null,
|
|
5730
|
+
outputInputRatio: prompt > 0 ? completion / prompt : null,
|
|
5731
|
+
retryWasteUsd: spend * wasteShare,
|
|
5732
|
+
retryWasteShare: wasteShare,
|
|
5733
|
+
successRate: reqs > 0 ? ok / reqs : null,
|
|
5734
|
+
premiumShare: spend > 0 ? premiumSpend / spend : 0,
|
|
5735
|
+
premiumSpend,
|
|
5736
|
+
topModelConcentration: spend > 0 ? topSpend / spend : 0,
|
|
5737
|
+
projectedMonthSpend: projected,
|
|
5738
|
+
burnMultiple: projected !== null && spend > 0 ? projected / spend : null
|
|
5739
|
+
};
|
|
5740
|
+
};
|
|
5741
|
+
scorePractices = (summary, keyStats) => {
|
|
5742
|
+
const band = (v, limits) => {
|
|
5743
|
+
for (const [max, pts] of limits) {
|
|
5744
|
+
if (v <= max) return pts;
|
|
5745
|
+
}
|
|
5746
|
+
return 0;
|
|
5747
|
+
};
|
|
5748
|
+
const waste = band(summary.retryWasteShare, [[0.01, 30], [0.05, 20], [0.1, 10]]);
|
|
5749
|
+
const premium = band(summary.premiumShare, [[0.2, 25], [0.5, 15], [0.8, 5]]);
|
|
5750
|
+
const conc = band(summary.topModelConcentration, [[0.5, 20], [0.8, 10]]);
|
|
5751
|
+
const bad = keyStats.totalKeys > 0 ? (keyStats.idleKeys + keyStats.uncappedKeys) / keyStats.totalKeys : 0;
|
|
5752
|
+
const hygiene = keyStats.totalKeys === 0 ? 25 : band(bad, [[0, 25], [0.2, 15], [0.5, 5]]);
|
|
5753
|
+
const checks = [
|
|
5754
|
+
{ id: "retry-waste", label: "Low retry waste", points: waste, maxPoints: 30, detail: `${(summary.retryWasteShare * 100).toFixed(1)}% of requests failed` },
|
|
5755
|
+
{ id: "premium-mix", label: "Balanced model mix", points: premium, maxPoints: 25, detail: `${(summary.premiumShare * 100).toFixed(1)}% of spend on premium models` },
|
|
5756
|
+
{ id: "concentration", label: "No single-model lock-in", points: conc, maxPoints: 20, detail: `Top model is ${(summary.topModelConcentration * 100).toFixed(1)}% of spend` },
|
|
5757
|
+
{ id: "key-hygiene", label: "Key hygiene", points: hygiene, maxPoints: 25, detail: `${keyStats.idleKeys} idle, ${keyStats.uncappedKeys} uncapped of ${keyStats.totalKeys} keys` }
|
|
5758
|
+
];
|
|
5759
|
+
return { total: checks.reduce((s, c) => s + c.points, 0), checks };
|
|
5760
|
+
};
|
|
5761
|
+
modelOpportunities = (usage, models, premiumThresholdPer1k = 0.01) => {
|
|
5762
|
+
if (!usage) return [];
|
|
5763
|
+
const entries = Object.entries(usage.usage_by_model ?? {});
|
|
5764
|
+
const overallRate = usage.api_requests > 0 ? usage.failed_requests / usage.api_requests : 0;
|
|
5765
|
+
return entries.map(([model, d]) => {
|
|
5766
|
+
const failureRate = d.api_requests > 0 ? d.failed_requests / d.api_requests : null;
|
|
5767
|
+
const avgCostPerRequest = d.api_requests > 0 ? d.total_spend / d.api_requests : 0;
|
|
5768
|
+
const completionShare = d.total_tokens > 0 ? d.completion_tokens / d.total_tokens : 0;
|
|
5769
|
+
const per1k = costPer1k(model, d.total_spend, d.total_tokens, models) ?? 0;
|
|
5770
|
+
const spendShare = usage.total_spend > 0 ? d.total_spend / usage.total_spend : 0;
|
|
5771
|
+
if (failureRate !== null && overallRate > 0 && failureRate > 2 * overallRate && d.api_requests >= 10) {
|
|
5772
|
+
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` };
|
|
5773
|
+
}
|
|
5774
|
+
if (per1k > premiumThresholdPer1k && completionShare < 0.3 && spendShare > 0.05) {
|
|
5775
|
+
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` };
|
|
5776
|
+
}
|
|
5777
|
+
return { model, spend: d.total_spend, requests: d.api_requests, failureRate, avgCostPerRequest, flag: "ok", hint: "No action" };
|
|
5778
|
+
});
|
|
5779
|
+
};
|
|
5669
5780
|
}
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5781
|
+
});
|
|
5782
|
+
|
|
5783
|
+
// src/dates.ts
|
|
5784
|
+
function toLocalDay(d) {
|
|
5785
|
+
const year = d.getFullYear();
|
|
5786
|
+
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
5787
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
5788
|
+
return `${year}-${month}-${day}`;
|
|
5675
5789
|
}
|
|
5676
|
-
function
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
}
|
|
5790
|
+
function monthToDateRange(now = /* @__PURE__ */ new Date()) {
|
|
5791
|
+
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
5792
|
+
return {
|
|
5793
|
+
startDate: toLocalDay(start),
|
|
5794
|
+
endDate: toLocalDay(now)
|
|
5795
|
+
};
|
|
5682
5796
|
}
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
}
|
|
5687
|
-
if (entries.length === 0) {
|
|
5688
|
-
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableRow6.default, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { colSpan: 6, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5689
|
-
EmptyState,
|
|
5690
|
-
{
|
|
5691
|
-
message: "No audit events found",
|
|
5692
|
-
hint: "Try widening the filters or a different time window."
|
|
5693
|
-
}
|
|
5694
|
-
) }) });
|
|
5797
|
+
var init_dates = __esm({
|
|
5798
|
+
"src/dates.ts"() {
|
|
5799
|
+
"use strict";
|
|
5695
5800
|
}
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
var
|
|
5700
|
-
|
|
5801
|
+
});
|
|
5802
|
+
|
|
5803
|
+
// src/components/InsightsTab.tsx
|
|
5804
|
+
var import_react12, import_Box16, import_Grid2, import_Typography14, import_Alert5, import_Button7, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_Skeleton6, import_react_use, import_jsx_runtime17, FLAG_TONE, FLAG_LABEL, fmtRatio, fmtPct1, failedRow, InsightsTab;
|
|
5805
|
+
var init_InsightsTab = __esm({
|
|
5806
|
+
"src/components/InsightsTab.tsx"() {
|
|
5701
5807
|
"use strict";
|
|
5702
5808
|
import_react12 = require("react");
|
|
5703
5809
|
import_Box16 = __toESM(require("@mui/material/Box"));
|
|
5810
|
+
import_Grid2 = __toESM(require("@mui/material/Grid"));
|
|
5704
5811
|
import_Typography14 = __toESM(require("@mui/material/Typography"));
|
|
5812
|
+
import_Alert5 = __toESM(require("@mui/material/Alert"));
|
|
5813
|
+
import_Button7 = __toESM(require("@mui/material/Button"));
|
|
5705
5814
|
import_Table6 = __toESM(require("@mui/material/Table"));
|
|
5706
5815
|
import_TableBody6 = __toESM(require("@mui/material/TableBody"));
|
|
5707
5816
|
import_TableCell6 = __toESM(require("@mui/material/TableCell"));
|
|
5708
5817
|
import_TableContainer6 = __toESM(require("@mui/material/TableContainer"));
|
|
5709
5818
|
import_TableHead6 = __toESM(require("@mui/material/TableHead"));
|
|
5710
5819
|
import_TableRow6 = __toESM(require("@mui/material/TableRow"));
|
|
5711
|
-
import_TablePagination = __toESM(require("@mui/material/TablePagination"));
|
|
5712
|
-
import_TextField6 = __toESM(require("@mui/material/TextField"));
|
|
5713
|
-
import_MenuItem5 = __toESM(require("@mui/material/MenuItem"));
|
|
5714
5820
|
import_Skeleton6 = __toESM(require("@mui/material/Skeleton"));
|
|
5715
|
-
import_Collapse3 = __toESM(require("@mui/material/Collapse"));
|
|
5716
|
-
import_IconButton7 = __toESM(require("@mui/material/IconButton"));
|
|
5717
|
-
import_Alert5 = __toESM(require("@mui/material/Alert"));
|
|
5718
|
-
import_styles12 = require("@mui/material/styles");
|
|
5719
|
-
import_icons_material9 = require("@mui/icons-material");
|
|
5720
5821
|
import_react_use = require("react-use");
|
|
5822
|
+
init_insights();
|
|
5823
|
+
init_format();
|
|
5824
|
+
init_dates();
|
|
5721
5825
|
init_ui();
|
|
5722
5826
|
import_jsx_runtime17 = require("react/jsx-runtime");
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
blocked: "danger",
|
|
5728
|
-
unblocked: "success"
|
|
5827
|
+
FLAG_TONE = {
|
|
5828
|
+
"failure-outlier": "danger",
|
|
5829
|
+
"downgrade-candidate": "warning",
|
|
5830
|
+
ok: "success"
|
|
5729
5831
|
};
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
LiteLLM_ObjectPermissionTable: "Team access (KB / MCP)",
|
|
5735
|
-
LiteLLM_UserTable: "User"
|
|
5832
|
+
FLAG_LABEL = {
|
|
5833
|
+
"failure-outlier": "Failure outlier",
|
|
5834
|
+
"downgrade-candidate": "Downgrade candidate",
|
|
5835
|
+
ok: "OK"
|
|
5736
5836
|
};
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5837
|
+
fmtRatio = (n) => n === null || Number.isNaN(n) ? "\u2014" : `${n.toFixed(2)}\xD7`;
|
|
5838
|
+
fmtPct1 = (n) => n === null || Number.isNaN(n) ? "\u2014" : `${(n * 100).toFixed(1)}%`;
|
|
5839
|
+
failedRow = (status) => /fail|error/i.test(status ?? "");
|
|
5840
|
+
InsightsTab = ({
|
|
5841
|
+
api,
|
|
5842
|
+
usage,
|
|
5843
|
+
models,
|
|
5844
|
+
keys,
|
|
5845
|
+
dateRange,
|
|
5846
|
+
loading
|
|
5847
|
+
}) => {
|
|
5848
|
+
const summary = (0, import_react12.useMemo)(() => summarizeUsage(usage, models), [usage, models]);
|
|
5849
|
+
const keyStats = (0, import_react12.useMemo)(
|
|
5850
|
+
() => ({
|
|
5851
|
+
idleKeys: keys.filter((k) => (k.spend ?? 0) === 0).length,
|
|
5852
|
+
uncappedKeys: keys.filter((k) => k.max_budget === void 0 || k.max_budget === null).length,
|
|
5853
|
+
totalKeys: keys.length
|
|
5854
|
+
}),
|
|
5855
|
+
[keys]
|
|
5856
|
+
);
|
|
5857
|
+
const score = (0, import_react12.useMemo)(
|
|
5858
|
+
() => scorePractices(summary, keyStats),
|
|
5859
|
+
[summary, keyStats]
|
|
5860
|
+
);
|
|
5861
|
+
const opportunities = (0, import_react12.useMemo)(() => {
|
|
5862
|
+
const opps = modelOpportunities(usage, models);
|
|
5863
|
+
return [...opps].sort(
|
|
5864
|
+
(a, b) => (a.flag === "ok" ? 1 : 0) - (b.flag === "ok" ? 1 : 0)
|
|
5865
|
+
);
|
|
5866
|
+
}, [usage, models]);
|
|
5867
|
+
const {
|
|
5868
|
+
value: spendLogs,
|
|
5869
|
+
loading: logsLoading,
|
|
5870
|
+
error: logsError,
|
|
5871
|
+
retry: retryLogs
|
|
5872
|
+
} = (0, import_react_use.useAsyncRetry)(async () => {
|
|
5873
|
+
return api.getSpendLogs({
|
|
5874
|
+
startDate: toLocalDay(dateRange.start),
|
|
5875
|
+
endDate: toLocalDay(dateRange.end),
|
|
5876
|
+
pageSize: 1e3
|
|
5877
|
+
});
|
|
5878
|
+
}, [api, dateRange]);
|
|
5879
|
+
const {
|
|
5880
|
+
value: quality,
|
|
5881
|
+
loading: qualityLoading,
|
|
5882
|
+
error: qualityError,
|
|
5883
|
+
retry: retryQuality
|
|
5884
|
+
} = (0, import_react_use.useAsyncRetry)(async () => {
|
|
5885
|
+
if (typeof api.getQuality !== "function") return { configured: false };
|
|
5886
|
+
return api.getQuality({
|
|
5887
|
+
startDate: toLocalDay(dateRange.start),
|
|
5888
|
+
endDate: toLocalDay(dateRange.end)
|
|
5889
|
+
});
|
|
5890
|
+
}, [api, dateRange]);
|
|
5891
|
+
const renderQualityBody = () => {
|
|
5892
|
+
if (qualityLoading) return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Skeleton6.default, { variant: "rounded", height: 120 });
|
|
5893
|
+
if (qualityError) {
|
|
5894
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Alert5.default, { severity: "error", children: [
|
|
5895
|
+
"Failed to load quality scores: ",
|
|
5896
|
+
qualityError.message || "Unknown error",
|
|
5897
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Box16.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Button7.default, { size: "small", onClick: retryQuality, children: "Retry" }) })
|
|
5898
|
+
] });
|
|
5899
|
+
}
|
|
5900
|
+
if (!quality || quality.configured === false) {
|
|
5901
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5902
|
+
EmptyState,
|
|
5762
5903
|
{
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
color: "text.secondary",
|
|
5766
|
-
title: entry.object_id ?? void 0,
|
|
5767
|
-
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 },
|
|
5768
|
-
children: entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
5904
|
+
message: "Quality scores \u2014 connect Langfuse",
|
|
5905
|
+
hint: "LLM-as-judge evaluation is not configured for this deployment."
|
|
5769
5906
|
}
|
|
5770
|
-
)
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
color: "text.secondary",
|
|
5792
|
-
display: "block",
|
|
5793
|
-
mb: 0.75,
|
|
5794
|
-
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" },
|
|
5795
|
-
children: "Before"
|
|
5796
|
-
}
|
|
5797
|
-
),
|
|
5798
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { 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) })
|
|
5799
|
-
] }),
|
|
5800
|
-
entry.updated_values && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { flex: 1, minWidth: 200, children: [
|
|
5801
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5802
|
-
import_Typography14.default,
|
|
5803
|
-
{
|
|
5804
|
-
variant: "caption",
|
|
5805
|
-
color: "text.secondary",
|
|
5806
|
-
display: "block",
|
|
5807
|
-
mb: 0.75,
|
|
5808
|
-
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" },
|
|
5809
|
-
children: "After"
|
|
5810
|
-
}
|
|
5811
|
-
),
|
|
5812
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { 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) })
|
|
5907
|
+
);
|
|
5908
|
+
}
|
|
5909
|
+
if (quality.byModel.length === 0 && quality.scores.length === 0) {
|
|
5910
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(EmptyState, { message: "No quality data in this period" });
|
|
5911
|
+
}
|
|
5912
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
5913
|
+
quality.byModel.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Box16.default, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: quality.byModel.map((m) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Box16.default, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { sx: { display: "flex", justifyContent: "space-between", gap: 1, mb: 0.5 }, children: [
|
|
5914
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { variant: "body2", sx: { fontWeight: 600 }, children: m.model }),
|
|
5915
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" }, children: [
|
|
5916
|
+
fmtUsd(m.cost),
|
|
5917
|
+
" \xB7 ",
|
|
5918
|
+
fmtInt(m.tokens),
|
|
5919
|
+
" tokens"
|
|
5920
|
+
] })
|
|
5921
|
+
] }) }, m.model)) }),
|
|
5922
|
+
quality.scores.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Box16.default, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: quality.scores.map((s) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { children: [
|
|
5923
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { sx: { display: "flex", justifyContent: "space-between", gap: 1, mb: 0.5 }, children: [
|
|
5924
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { variant: "body2", sx: { fontWeight: 600 }, children: s.name }),
|
|
5925
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" }, children: [
|
|
5926
|
+
s.avg.toFixed(2),
|
|
5927
|
+
s.count > 0 ? ` \xB7 n=${fmtInt(s.count)}` : ""
|
|
5813
5928
|
] })
|
|
5814
|
-
]
|
|
5815
|
-
}
|
|
5816
|
-
) }) }) })
|
|
5817
|
-
] });
|
|
5818
|
-
};
|
|
5819
|
-
AuditLog = ({ api }) => {
|
|
5820
|
-
const [page, setPage] = (0, import_react12.useState)(0);
|
|
5821
|
-
const [pageSize, setPageSize] = (0, import_react12.useState)(25);
|
|
5822
|
-
const [filters, setFilters] = (0, import_react12.useState)({});
|
|
5823
|
-
const fetchParams = (0, import_react12.useMemo)(
|
|
5824
|
-
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
5825
|
-
[page, pageSize, filters]
|
|
5826
|
-
);
|
|
5827
|
-
const { value, loading, error } = (0, import_react_use.useAsync)(
|
|
5828
|
-
() => api.getAuditLogs(fetchParams),
|
|
5829
|
-
[api, fetchParams]
|
|
5830
|
-
);
|
|
5831
|
-
const entries = value?.audit_logs ?? [];
|
|
5832
|
-
const total = value?.total ?? 0;
|
|
5833
|
-
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
5834
|
-
SectionCard,
|
|
5835
|
-
{
|
|
5836
|
-
title: "Audit Log",
|
|
5837
|
-
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
5838
|
-
flush: true,
|
|
5839
|
-
actions: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
5840
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
5841
|
-
import_TextField6.default,
|
|
5842
|
-
{
|
|
5843
|
-
size: "small",
|
|
5844
|
-
label: "Action",
|
|
5845
|
-
select: true,
|
|
5846
|
-
value: filters.action ?? "",
|
|
5847
|
-
onChange: (e) => {
|
|
5848
|
-
setFilters((f) => ({ ...f, action: e.target.value || void 0 }));
|
|
5849
|
-
setPage(0);
|
|
5850
|
-
},
|
|
5851
|
-
sx: { minWidth: 150 },
|
|
5852
|
-
children: [
|
|
5853
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "", children: "All actions" }),
|
|
5854
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "created", children: "Created" }),
|
|
5855
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "updated", children: "Updated" }),
|
|
5856
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "deleted", children: "Deleted" }),
|
|
5857
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "blocked", children: "Blocked" })
|
|
5858
|
-
]
|
|
5859
|
-
}
|
|
5860
|
-
),
|
|
5861
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
5862
|
-
import_TextField6.default,
|
|
5863
|
-
{
|
|
5864
|
-
size: "small",
|
|
5865
|
-
label: "Table",
|
|
5866
|
-
select: true,
|
|
5867
|
-
value: filters.table_name ?? "",
|
|
5868
|
-
onChange: (e) => {
|
|
5869
|
-
setFilters((f) => ({ ...f, table_name: e.target.value || void 0 }));
|
|
5870
|
-
setPage(0);
|
|
5871
|
-
},
|
|
5872
|
-
sx: { minWidth: 150 },
|
|
5873
|
-
children: [
|
|
5874
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "", children: "All tables" }),
|
|
5875
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "LiteLLM_VerificationToken", children: "Key" }),
|
|
5876
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "LiteLLM_TeamTable", children: "Team" }),
|
|
5877
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "LiteLLM_TeamMembership", children: "Team member" }),
|
|
5878
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable", children: "Team access (KB / MCP)" }),
|
|
5879
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_MenuItem5.default, { value: "LiteLLM_UserTable", children: "User" })
|
|
5880
|
-
]
|
|
5881
|
-
}
|
|
5882
|
-
),
|
|
5929
|
+
] }),
|
|
5883
5930
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5884
|
-
|
|
5931
|
+
Meter,
|
|
5885
5932
|
{
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
value: filters.changed_by ?? "",
|
|
5889
|
-
onChange: (e) => {
|
|
5890
|
-
setFilters((f) => ({ ...f, changed_by: e.target.value || void 0 }));
|
|
5891
|
-
setPage(0);
|
|
5892
|
-
},
|
|
5893
|
-
sx: { minWidth: 180 }
|
|
5933
|
+
value: s.avg <= 1 ? s.avg * 100 : s.avg,
|
|
5934
|
+
ariaLabel: `${s.name}: average ${s.avg.toFixed(2)}`
|
|
5894
5935
|
}
|
|
5895
5936
|
)
|
|
5896
|
-
] })
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5937
|
+
] }, s.name)) })
|
|
5938
|
+
] });
|
|
5939
|
+
};
|
|
5940
|
+
const logStats = (0, import_react12.useMemo)(() => {
|
|
5941
|
+
const rows = spendLogs?.data ?? [];
|
|
5942
|
+
const hits = rows.filter((r) => r.cacheHit);
|
|
5943
|
+
const cachedPromptTokens = hits.reduce((s, r) => s + (r.promptTokens ?? 0), 0);
|
|
5944
|
+
const priced = models.map((m) => m.input_cost_per_token).filter((p) => p !== null && p !== void 0 && p > 0);
|
|
5945
|
+
const avgInputPrice = priced.length > 0 ? priced.reduce((s, p) => s + p, 0) / priced.length : null;
|
|
5946
|
+
return {
|
|
5947
|
+
total: spendLogs?.total ?? rows.length,
|
|
5948
|
+
hitRate: rows.length > 0 ? hits.length / rows.length : null,
|
|
5949
|
+
// Estimate: cached prompt tokens billed at the average listed input price.
|
|
5950
|
+
savedUsd: avgInputPrice !== null ? cachedPromptTokens * avgInputPrice : null,
|
|
5951
|
+
failedWasteUsd: rows.filter((r) => failedRow(r.status)).reduce((s, r) => s + (r.spend ?? 0), 0),
|
|
5952
|
+
truncated: spendLogs?.truncated ?? false
|
|
5953
|
+
};
|
|
5954
|
+
}, [spendLogs, models]);
|
|
5955
|
+
const renderSpendLogsBody = () => {
|
|
5956
|
+
if (logsLoading) return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Skeleton6.default, { variant: "rounded", height: 120 });
|
|
5957
|
+
if (logsError) {
|
|
5958
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Alert5.default, { severity: "error", children: [
|
|
5959
|
+
"Failed to load spend logs: ",
|
|
5960
|
+
logsError.message || "Unknown error",
|
|
5961
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Box16.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Button7.default, { size: "small", onClick: retryLogs, children: "Retry" }) })
|
|
5962
|
+
] });
|
|
5963
|
+
}
|
|
5964
|
+
if ((spendLogs?.data ?? []).length === 0) {
|
|
5965
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(EmptyState, { message: "No spend logs in this period" });
|
|
5966
|
+
}
|
|
5967
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { sx: { display: "flex", gap: 3, flexWrap: "wrap" }, children: [
|
|
5968
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Stat, { label: "Cache-hit rate", value: fmtPct1(logStats.hitRate) }),
|
|
5969
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5970
|
+
Stat,
|
|
5971
|
+
{
|
|
5972
|
+
label: "Cache savings (est.)",
|
|
5973
|
+
value: logStats.savedUsd !== null ? fmtUsd(logStats.savedUsd) : "\u2014"
|
|
5974
|
+
}
|
|
5975
|
+
),
|
|
5976
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Stat, { label: "Failed-request waste", value: fmtUsd(logStats.failedWasteUsd) }),
|
|
5977
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Stat, { label: "Log rows", value: fmtInt(logStats.total) }),
|
|
5978
|
+
logStats.truncated && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { variant: "caption", color: "text.secondary", children: "Showing the first 1,000 rows \u2014 figures are a lower bound." })
|
|
5979
|
+
] });
|
|
5980
|
+
};
|
|
5981
|
+
const kpis = [
|
|
5982
|
+
{
|
|
5983
|
+
label: "Cost / 1k tokens",
|
|
5984
|
+
value: summary.costPer1kTokens !== null ? fmtUsd(summary.costPer1kTokens) : "\u2014",
|
|
5985
|
+
tone: "accent"
|
|
5986
|
+
},
|
|
5987
|
+
{
|
|
5988
|
+
label: "Cost / request",
|
|
5989
|
+
value: summary.costPerRequest !== null ? fmtUsd(summary.costPerRequest) : "\u2014",
|
|
5990
|
+
tone: "info"
|
|
5991
|
+
},
|
|
5992
|
+
{
|
|
5993
|
+
label: "Out : in ratio",
|
|
5994
|
+
value: fmtRatio(summary.outputInputRatio),
|
|
5995
|
+
tone: "neutral"
|
|
5996
|
+
},
|
|
5997
|
+
{
|
|
5998
|
+
label: "Retry waste",
|
|
5999
|
+
value: fmtUsd(summary.retryWasteUsd),
|
|
6000
|
+
hint: `${fmtPct1(summary.retryWasteShare)} of spend`,
|
|
6001
|
+
tone: "warning"
|
|
6002
|
+
},
|
|
6003
|
+
{
|
|
6004
|
+
label: "Success rate",
|
|
6005
|
+
value: fmtPct1(summary.successRate),
|
|
6006
|
+
tone: "success"
|
|
6007
|
+
}
|
|
6008
|
+
];
|
|
6009
|
+
const renderEfficiencyBody = () => {
|
|
6010
|
+
if (loading) return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Skeleton6.default, { variant: "rounded", height: 96 });
|
|
6011
|
+
if (!usage) return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(EmptyState, { message: "No usage in this period" });
|
|
6012
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(MetricStrip, { metrics: kpis });
|
|
6013
|
+
};
|
|
6014
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
6015
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
6016
|
+
SectionCard,
|
|
6017
|
+
{
|
|
6018
|
+
title: "Efficiency",
|
|
6019
|
+
subtitle: `${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
6020
|
+
description: "What each token and request costs, and how much failed requests waste.",
|
|
6021
|
+
children: renderEfficiencyBody()
|
|
6022
|
+
}
|
|
6023
|
+
),
|
|
6024
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start", children: [
|
|
6025
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Grid2.default, { item: true, xs: 12, md: 5, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
6026
|
+
SectionCard,
|
|
6027
|
+
{
|
|
6028
|
+
title: "Practice score",
|
|
6029
|
+
subtitle: "Out of 100",
|
|
6030
|
+
description: "Five everyday habits in one number \u2014 higher means cheaper, safer usage.",
|
|
6031
|
+
children: loading ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Skeleton6.default, { variant: "rounded", height: 160 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
|
|
6032
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Typography14.default, { sx: { fontSize: 30, fontWeight: 700, fontVariantNumeric: "tabular-nums" }, children: [
|
|
6033
|
+
score.total,
|
|
6034
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { component: "span", color: "text.secondary", sx: { fontSize: 16 }, children: "/100" })
|
|
6035
|
+
] }),
|
|
6036
|
+
score.checks.map((c) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { children: [
|
|
6037
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Box16.default, { sx: { display: "flex", justifyContent: "space-between", gap: 1, mb: 0.5 }, children: [
|
|
6038
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { variant: "body2", sx: { fontWeight: 600 }, children: c.label }),
|
|
6039
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" }, children: [
|
|
6040
|
+
c.points,
|
|
6041
|
+
"/",
|
|
6042
|
+
c.maxPoints
|
|
6043
|
+
] })
|
|
6044
|
+
] }),
|
|
6045
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
6046
|
+
Meter,
|
|
6047
|
+
{
|
|
6048
|
+
value: c.maxPoints > 0 ? c.points / c.maxPoints * 100 : 0,
|
|
6049
|
+
ariaLabel: `${c.label}: ${c.points} of ${c.maxPoints}`
|
|
6050
|
+
}
|
|
6051
|
+
),
|
|
6052
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { variant: "caption", color: "text.secondary", children: c.detail })
|
|
6053
|
+
] }, c.id))
|
|
6054
|
+
] })
|
|
6055
|
+
}
|
|
6056
|
+
) }),
|
|
6057
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Grid2.default, { item: true, xs: 12, md: 7, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
6058
|
+
SectionCard,
|
|
6059
|
+
{
|
|
6060
|
+
title: "Cache & errors",
|
|
6061
|
+
subtitle: "From spend logs (estimate)",
|
|
6062
|
+
description: "Repeated prompts served from cache save money; failures still bill.",
|
|
6063
|
+
children: renderSpendLogsBody()
|
|
6064
|
+
}
|
|
6065
|
+
) })
|
|
6066
|
+
] }),
|
|
6067
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
6068
|
+
SectionCard,
|
|
6069
|
+
{
|
|
6070
|
+
title: "Model opportunities",
|
|
6071
|
+
description: "Where a cheaper model or a reliability fix would save the most.",
|
|
6072
|
+
flush: true,
|
|
6073
|
+
children: loading ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Box16.default, { sx: { p: 2.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Skeleton6.default, { variant: "rounded", height: 120 }) }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableContainer6.default, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_Table6.default, { size: "small", sx: dataTableSx, children: [
|
|
5900
6074
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableHead6.default, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_TableRow6.default, { children: [
|
|
5901
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, {
|
|
5902
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: "
|
|
5903
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: "
|
|
5904
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: "
|
|
5905
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: "
|
|
5906
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: "
|
|
6075
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: "Model" }),
|
|
6076
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: "Spend" }),
|
|
6077
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: "Requests" }),
|
|
6078
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: "Failure" }),
|
|
6079
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: "Avg / req" }),
|
|
6080
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: "Signal" })
|
|
5907
6081
|
] }) }),
|
|
5908
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableBody6.default, { children:
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
6082
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableBody6.default, { children: opportunities.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableRow6.default, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { colSpan: 6, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(EmptyState, { message: "No model activity in this period" }) }) }) : opportunities.map((o) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_TableRow6.default, { children: [
|
|
6083
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { variant: "body2", sx: { fontWeight: 600 }, children: o.model }) }),
|
|
6084
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: fmtUsd(o.spend) }),
|
|
6085
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: fmtInt(o.requests) }),
|
|
6086
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: fmtPct1(o.failureRate) }),
|
|
6087
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_TableCell6.default, { align: "right", children: fmtUsd(o.avgCostPerRequest) }),
|
|
6088
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_TableCell6.default, { children: [
|
|
6089
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusPill, { label: FLAG_LABEL[o.flag], tone: FLAG_TONE[o.flag], title: o.hint }),
|
|
6090
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.25 }, children: o.hint })
|
|
6091
|
+
] })
|
|
6092
|
+
] }, o.model)) })
|
|
6093
|
+
] }) })
|
|
6094
|
+
}
|
|
6095
|
+
),
|
|
6096
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
6097
|
+
SectionCard,
|
|
6098
|
+
{
|
|
6099
|
+
title: "Quality scores",
|
|
6100
|
+
description: "Independent answer-quality scores from Langfuse, when connected.",
|
|
6101
|
+
children: renderQualityBody()
|
|
6102
|
+
}
|
|
6103
|
+
)
|
|
6104
|
+
] });
|
|
5929
6105
|
};
|
|
5930
6106
|
}
|
|
5931
6107
|
});
|
|
5932
6108
|
|
|
5933
|
-
// src/dates.ts
|
|
5934
|
-
function toLocalDay(d) {
|
|
5935
|
-
const year = d.getFullYear();
|
|
5936
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
5937
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
5938
|
-
return `${year}-${month}-${day}`;
|
|
5939
|
-
}
|
|
5940
|
-
function monthToDateRange(now = /* @__PURE__ */ new Date()) {
|
|
5941
|
-
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
5942
|
-
return {
|
|
5943
|
-
startDate: toLocalDay(start),
|
|
5944
|
-
endDate: toLocalDay(now)
|
|
5945
|
-
};
|
|
5946
|
-
}
|
|
5947
|
-
var init_dates = __esm({
|
|
5948
|
-
"src/dates.ts"() {
|
|
5949
|
-
"use strict";
|
|
5950
|
-
}
|
|
5951
|
-
});
|
|
5952
|
-
|
|
5953
6109
|
// src/feedback.ts
|
|
5954
6110
|
function toastFor(kind, errorMsg) {
|
|
5955
6111
|
switch (kind) {
|
|
@@ -6058,18 +6214,18 @@ function initDateRange() {
|
|
|
6058
6214
|
else start.setDate(start.getDate() - 7);
|
|
6059
6215
|
return { start, end };
|
|
6060
6216
|
}
|
|
6061
|
-
var import_react13, import_Box17, import_Alert6,
|
|
6217
|
+
var import_react13, import_Box17, import_Alert6, import_Button8, import_Typography15, import_Tabs2, import_Tab2, import_Grid3, import_Accordion2, import_AccordionSummary2, import_AccordionDetails2, import_ExpandMore, import_react_router_dom, import_react_use2, import_core_plugin_api5, import_core_components3, import_plugin_permission_react3, import_backstage_plugin_litellm_common5, import_jsx_runtime18, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
|
|
6062
6218
|
var init_LiteLLMPage = __esm({
|
|
6063
6219
|
"src/components/LiteLLMPage.tsx"() {
|
|
6064
6220
|
"use strict";
|
|
6065
6221
|
import_react13 = require("react");
|
|
6066
6222
|
import_Box17 = __toESM(require("@mui/material/Box"));
|
|
6067
6223
|
import_Alert6 = __toESM(require("@mui/material/Alert"));
|
|
6068
|
-
|
|
6224
|
+
import_Button8 = __toESM(require("@mui/material/Button"));
|
|
6069
6225
|
import_Typography15 = __toESM(require("@mui/material/Typography"));
|
|
6070
6226
|
import_Tabs2 = __toESM(require("@mui/material/Tabs"));
|
|
6071
6227
|
import_Tab2 = __toESM(require("@mui/material/Tab"));
|
|
6072
|
-
|
|
6228
|
+
import_Grid3 = __toESM(require("@mui/material/Grid"));
|
|
6073
6229
|
import_Accordion2 = __toESM(require("@mui/material/Accordion"));
|
|
6074
6230
|
import_AccordionSummary2 = __toESM(require("@mui/material/AccordionSummary"));
|
|
6075
6231
|
import_AccordionDetails2 = __toESM(require("@mui/material/AccordionDetails"));
|
|
@@ -6087,7 +6243,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6087
6243
|
init_LiteLLMBudgetWidget();
|
|
6088
6244
|
init_TeamUsage();
|
|
6089
6245
|
init_ModelsTable();
|
|
6090
|
-
|
|
6246
|
+
init_InsightsTab();
|
|
6091
6247
|
init_api();
|
|
6092
6248
|
init_dates();
|
|
6093
6249
|
import_backstage_plugin_litellm_common5 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
@@ -6096,7 +6252,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6096
6252
|
init_unprovisionedCopy();
|
|
6097
6253
|
import_jsx_runtime18 = require("react/jsx-runtime");
|
|
6098
6254
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
6099
|
-
PAGE_TABS = ["overview", "keys", "teams", "models", "
|
|
6255
|
+
PAGE_TABS = ["overview", "keys", "teams", "models", "insights"];
|
|
6100
6256
|
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
6101
6257
|
LiteLLMPage = () => {
|
|
6102
6258
|
const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
|
|
@@ -6437,7 +6593,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6437
6593
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Tab2.default, { label: "Keys", value: "keys" }),
|
|
6438
6594
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Tab2.default, { label: "Teams", value: "teams" }),
|
|
6439
6595
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Tab2.default, { label: "Models", value: "models" }),
|
|
6440
|
-
|
|
6596
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Tab2.default, { label: "Insights", value: "insights" })
|
|
6441
6597
|
]
|
|
6442
6598
|
}
|
|
6443
6599
|
);
|
|
@@ -6491,7 +6647,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6491
6647
|
}
|
|
6492
6648
|
),
|
|
6493
6649
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6494
|
-
|
|
6650
|
+
import_Button8.default,
|
|
6495
6651
|
{
|
|
6496
6652
|
variant: "contained",
|
|
6497
6653
|
color: "primary",
|
|
@@ -6503,8 +6659,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6503
6659
|
}
|
|
6504
6660
|
) });
|
|
6505
6661
|
}
|
|
6506
|
-
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
6507
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6662
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_Grid3.default, { container: true, spacing: 2, alignItems: "flex-start", children: [
|
|
6663
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Grid3.default, { item: true, xs: 12, lg: 8, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6508
6664
|
UsageStats,
|
|
6509
6665
|
{
|
|
6510
6666
|
usage: usage ?? null,
|
|
@@ -6518,7 +6674,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6518
6674
|
userInfo
|
|
6519
6675
|
}
|
|
6520
6676
|
) }),
|
|
6521
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6677
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Grid3.default, { item: true, xs: 12, lg: 4, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LiteLLMBudgetWidget, { compact: true, collapsible: true, userInfo: userInfo ?? null, teams, keys: keys ?? [] }) })
|
|
6522
6678
|
] });
|
|
6523
6679
|
})(),
|
|
6524
6680
|
activeTab === "keys" && (() => {
|
|
@@ -6526,7 +6682,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6526
6682
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_Alert6.default, { severity: "error", children: [
|
|
6527
6683
|
"Failed to load keys: ",
|
|
6528
6684
|
keysError.message || "Unknown error",
|
|
6529
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box17.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6685
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box17.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Button8.default, { size: "small", onClick: refreshKeys, children: "Retry" }) })
|
|
6530
6686
|
] });
|
|
6531
6687
|
}
|
|
6532
6688
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
@@ -6550,7 +6706,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6550
6706
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_Alert6.default, { severity: "error", children: [
|
|
6551
6707
|
"Failed to load teams: ",
|
|
6552
6708
|
teamsError.message || "Unknown error",
|
|
6553
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box17.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6709
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box17.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Button8.default, { size: "small", onClick: refreshTeams, children: "Retry" }) })
|
|
6554
6710
|
] });
|
|
6555
6711
|
}
|
|
6556
6712
|
const teamsById = /* @__PURE__ */ new Map();
|
|
@@ -6587,7 +6743,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6587
6743
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_Alert6.default, { severity: "error", children: [
|
|
6588
6744
|
"Failed to load models: ",
|
|
6589
6745
|
modelsError.message || "Unknown error",
|
|
6590
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box17.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6746
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box17.default, { sx: { mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Button8.default, { size: "small", onClick: refreshModels, children: "Retry" }) })
|
|
6591
6747
|
] });
|
|
6592
6748
|
}
|
|
6593
6749
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
@@ -6599,7 +6755,17 @@ var init_LiteLLMPage = __esm({
|
|
|
6599
6755
|
}
|
|
6600
6756
|
);
|
|
6601
6757
|
})(),
|
|
6602
|
-
activeTab === "
|
|
6758
|
+
activeTab === "insights" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6759
|
+
InsightsTab,
|
|
6760
|
+
{
|
|
6761
|
+
api,
|
|
6762
|
+
usage: usage ?? null,
|
|
6763
|
+
models: allModels ?? [],
|
|
6764
|
+
keys: keys ?? [],
|
|
6765
|
+
dateRange,
|
|
6766
|
+
loading: usageLoading
|
|
6767
|
+
}
|
|
6768
|
+
)
|
|
6603
6769
|
] }),
|
|
6604
6770
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6605
6771
|
KeyFormDialog,
|
|
@@ -6773,14 +6939,14 @@ var ConnectPage_exports = {};
|
|
|
6773
6939
|
__export(ConnectPage_exports, {
|
|
6774
6940
|
ConnectPage: () => ConnectPage
|
|
6775
6941
|
});
|
|
6776
|
-
var import_react14, import_Alert7, import_Box18,
|
|
6942
|
+
var import_react14, import_Alert7, import_Box18, import_Button9, import_Checkbox3, import_FormControlLabel3, import_FormGroup, import_Paper9, import_Typography16, import_react_router_dom2, import_react_use3, import_core_plugin_api6, import_core_components4, import_jsx_runtime19, ConnectPage;
|
|
6777
6943
|
var init_ConnectPage = __esm({
|
|
6778
6944
|
"src/components/ConnectPage.tsx"() {
|
|
6779
6945
|
"use strict";
|
|
6780
6946
|
import_react14 = require("react");
|
|
6781
6947
|
import_Alert7 = __toESM(require("@mui/material/Alert"));
|
|
6782
6948
|
import_Box18 = __toESM(require("@mui/material/Box"));
|
|
6783
|
-
|
|
6949
|
+
import_Button9 = __toESM(require("@mui/material/Button"));
|
|
6784
6950
|
import_Checkbox3 = __toESM(require("@mui/material/Checkbox"));
|
|
6785
6951
|
import_FormControlLabel3 = __toESM(require("@mui/material/FormControlLabel"));
|
|
6786
6952
|
import_FormGroup = __toESM(require("@mui/material/FormGroup"));
|
|
@@ -6851,7 +7017,7 @@ var init_ConnectPage = __esm({
|
|
|
6851
7017
|
)) }),
|
|
6852
7018
|
error && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_Alert7.default, { severity: "error", sx: { mb: 2 }, children: error }),
|
|
6853
7019
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
6854
|
-
|
|
7020
|
+
import_Button9.default,
|
|
6855
7021
|
{
|
|
6856
7022
|
variant: "contained",
|
|
6857
7023
|
onClick: connect,
|
|
@@ -6891,7 +7057,7 @@ __export(index_exports, {
|
|
|
6891
7057
|
module.exports = __toCommonJS(index_exports);
|
|
6892
7058
|
|
|
6893
7059
|
// src/plugin.tsx
|
|
6894
|
-
var
|
|
7060
|
+
var import_icons_material9 = require("@mui/icons-material");
|
|
6895
7061
|
var import_frontend_plugin_api4 = require("@backstage/frontend-plugin-api");
|
|
6896
7062
|
init_api();
|
|
6897
7063
|
init_routes();
|
|
@@ -6907,7 +7073,7 @@ var liteLlmPage = import_frontend_plugin_api4.PageBlueprint.make({
|
|
|
6907
7073
|
params: {
|
|
6908
7074
|
path: "/litellm",
|
|
6909
7075
|
title: "LiteLLM",
|
|
6910
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
7076
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_material9.TrendingUp, {}),
|
|
6911
7077
|
routeRef: rootRouteRef,
|
|
6912
7078
|
loader: async () => {
|
|
6913
7079
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
@@ -6947,7 +7113,7 @@ var import_Box19 = __toESM(require("@mui/material/Box"));
|
|
|
6947
7113
|
var import_Typography17 = __toESM(require("@mui/material/Typography"));
|
|
6948
7114
|
var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
|
|
6949
7115
|
var import_Select2 = __toESM(require("@mui/material/Select"));
|
|
6950
|
-
var
|
|
7116
|
+
var import_MenuItem5 = __toESM(require("@mui/material/MenuItem"));
|
|
6951
7117
|
var import_Skeleton7 = __toESM(require("@mui/material/Skeleton"));
|
|
6952
7118
|
var import_recharts3 = require("recharts");
|
|
6953
7119
|
var import_core_plugin_api7 = require("@backstage/core-plugin-api");
|
|
@@ -7084,9 +7250,9 @@ var LiteLLMHomeWidget = ({
|
|
|
7084
7250
|
displayEmpty: true,
|
|
7085
7251
|
inputProps: { "aria-label": "Usage period" },
|
|
7086
7252
|
children: [
|
|
7087
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
7088
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
7089
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
7253
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_MenuItem5.default, { value: "today", children: "Today" }),
|
|
7254
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_MenuItem5.default, { value: "7d", children: "7d" }),
|
|
7255
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_MenuItem5.default, { value: "30d", children: "30d" })
|
|
7090
7256
|
]
|
|
7091
7257
|
}
|
|
7092
7258
|
) })
|
|
@@ -7157,13 +7323,13 @@ var import_react16 = require("react");
|
|
|
7157
7323
|
var import_frontend_plugin_api6 = require("@backstage/frontend-plugin-api");
|
|
7158
7324
|
var import_Paper11 = __toESM(require("@mui/material/Paper"));
|
|
7159
7325
|
var import_Box20 = __toESM(require("@mui/material/Box"));
|
|
7160
|
-
var
|
|
7326
|
+
var import_Button10 = __toESM(require("@mui/material/Button"));
|
|
7161
7327
|
var import_Typography18 = __toESM(require("@mui/material/Typography"));
|
|
7162
7328
|
var import_Skeleton8 = __toESM(require("@mui/material/Skeleton"));
|
|
7163
|
-
var
|
|
7164
|
-
var
|
|
7329
|
+
var import_Collapse3 = __toESM(require("@mui/material/Collapse"));
|
|
7330
|
+
var import_styles12 = require("@mui/material/styles");
|
|
7165
7331
|
var import_useMediaQuery = __toESM(require("@mui/material/useMediaQuery"));
|
|
7166
|
-
var
|
|
7332
|
+
var import_icons_material10 = require("@mui/icons-material");
|
|
7167
7333
|
var import_recharts4 = require("recharts");
|
|
7168
7334
|
var import_core_plugin_api8 = require("@backstage/core-plugin-api");
|
|
7169
7335
|
var import_core_components6 = require("@backstage/core-components");
|
|
@@ -7574,7 +7740,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
7574
7740
|
keys: propKeys
|
|
7575
7741
|
}) => {
|
|
7576
7742
|
const api = (0, import_core_plugin_api8.useApi)(liteLlmApiRef);
|
|
7577
|
-
const theme = (0,
|
|
7743
|
+
const theme = (0, import_styles12.useTheme)();
|
|
7578
7744
|
const isSmallScreen = (0, import_useMediaQuery.default)(theme.breakpoints.down("sm"));
|
|
7579
7745
|
const gaugeSize = isSmallScreen ? Math.min(48, size) : size;
|
|
7580
7746
|
const expandedRegionId = (0, import_react16.useId)();
|
|
@@ -7675,10 +7841,10 @@ var LiteLLMBudgetGauges = ({
|
|
|
7675
7841
|
return onCreateKey ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenerateKeyButton, { size: "small", label, onClick: onCreateKey }, key) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenerateKeyButton, { size: "small", label, to: `${moduleHref}?generate=1` }, key);
|
|
7676
7842
|
case "module":
|
|
7677
7843
|
if (!moduleHref) return null;
|
|
7678
|
-
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
7844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_Button10.default, { size: "small", variant: "text", component: import_core_components6.Link, to: moduleHref, children: label }, key);
|
|
7679
7845
|
case "all-limits":
|
|
7680
7846
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
7681
|
-
|
|
7847
|
+
import_Button10.default,
|
|
7682
7848
|
{
|
|
7683
7849
|
size: "small",
|
|
7684
7850
|
variant: "text",
|
|
@@ -7686,7 +7852,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
7686
7852
|
"aria-expanded": isExpanded,
|
|
7687
7853
|
"aria-controls": expandedRegionId,
|
|
7688
7854
|
endIcon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
7689
|
-
|
|
7855
|
+
import_icons_material10.ExpandMore,
|
|
7690
7856
|
{
|
|
7691
7857
|
sx: (muiTheme) => ({
|
|
7692
7858
|
transform: isExpanded ? "rotate(180deg)" : "none",
|
|
@@ -7781,7 +7947,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
7781
7947
|
maxBudget: user?.max_budget ?? void 0
|
|
7782
7948
|
}
|
|
7783
7949
|
) }),
|
|
7784
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
7950
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_Collapse3.default, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
7785
7951
|
import_Box20.default,
|
|
7786
7952
|
{
|
|
7787
7953
|
id: expandedRegionId,
|