@acarmisc/backstage-plugin-litellm 0.38.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 +4 -1
- package/dist/components/ui/surfaces.d.ts +2 -0
- package/dist/index.cjs.js +159 -57
- package/dist/index.cjs.js.map +2 -2
- package/dist/index.esm.js +159 -57
- package/dist/index.esm.js.map +2 -2
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.esm.js
CHANGED
|
@@ -306,6 +306,14 @@ var init_api = __esm({
|
|
|
306
306
|
if (params.pageSize !== void 0) strParams.page_size = String(params.pageSize);
|
|
307
307
|
return this.get("/insights/spend-logs", strParams);
|
|
308
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
|
+
}
|
|
309
317
|
async getConfig() {
|
|
310
318
|
return this.get("/config");
|
|
311
319
|
}
|
|
@@ -704,7 +712,7 @@ var init_surfaces = __esm({
|
|
|
704
712
|
"src/components/ui/surfaces.tsx"() {
|
|
705
713
|
"use strict";
|
|
706
714
|
init_tokens();
|
|
707
|
-
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: [
|
|
708
716
|
(title || actions) && /* @__PURE__ */ jsxs3(
|
|
709
717
|
Box3,
|
|
710
718
|
{
|
|
@@ -726,6 +734,7 @@ var init_surfaces = __esm({
|
|
|
726
734
|
]
|
|
727
735
|
}
|
|
728
736
|
),
|
|
737
|
+
description && /* @__PURE__ */ jsx3(Typography2, { variant: "body2", color: "text.secondary", sx: { px: 2.5, pb: 1.5 }, children: description }),
|
|
729
738
|
/* @__PURE__ */ jsx3(Box3, { sx: flush ? void 0 : { px: 2.5, pb: 2.5, pt: title ? 0 : 2.5 }, children })
|
|
730
739
|
] });
|
|
731
740
|
ChartCard = ({ title, meta, height = 240, children }) => (
|
|
@@ -5682,7 +5691,7 @@ var init_insights = __esm({
|
|
|
5682
5691
|
"use strict";
|
|
5683
5692
|
costPer1k = (model, spend, tokens, models) => {
|
|
5684
5693
|
const info = models?.find((m) => m.model_name === model);
|
|
5685
|
-
const listed = info?.input_cost_per_token
|
|
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;
|
|
5686
5695
|
if (listed !== null) return listed;
|
|
5687
5696
|
return tokens > 0 ? spend / tokens * 1e3 : null;
|
|
5688
5697
|
};
|
|
@@ -5733,11 +5742,17 @@ var init_insights = __esm({
|
|
|
5733
5742
|
};
|
|
5734
5743
|
};
|
|
5735
5744
|
scorePractices = (summary, keyStats) => {
|
|
5736
|
-
const
|
|
5737
|
-
|
|
5738
|
-
|
|
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]]);
|
|
5739
5754
|
const bad = keyStats.totalKeys > 0 ? (keyStats.idleKeys + keyStats.uncappedKeys) / keyStats.totalKeys : 0;
|
|
5740
|
-
const hygiene = keyStats.totalKeys === 0 ? 25 : bad
|
|
5755
|
+
const hygiene = keyStats.totalKeys === 0 ? 25 : band(bad, [[0, 25], [0.2, 15], [0.5, 5]]);
|
|
5741
5756
|
const checks = [
|
|
5742
5757
|
{ id: "retry-waste", label: "Low retry waste", points: waste, maxPoints: 30, detail: `${(summary.retryWasteShare * 100).toFixed(1)}% of requests failed` },
|
|
5743
5758
|
{ id: "premium-mix", label: "Balanced model mix", points: premium, maxPoints: 25, detail: `${(summary.premiumShare * 100).toFixed(1)}% of spend on premium models` },
|
|
@@ -5864,6 +5879,67 @@ var init_InsightsTab = __esm({
|
|
|
5864
5879
|
pageSize: 1e3
|
|
5865
5880
|
});
|
|
5866
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
|
+
};
|
|
5867
5943
|
const logStats = useMemo8(() => {
|
|
5868
5944
|
const rows = spendLogs?.data ?? [];
|
|
5869
5945
|
const hits = rows.filter((r) => r.cacheHit);
|
|
@@ -5944,64 +6020,90 @@ var init_InsightsTab = __esm({
|
|
|
5944
6020
|
{
|
|
5945
6021
|
title: "Efficiency",
|
|
5946
6022
|
subtitle: `${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
6023
|
+
description: "What each token and request costs, and how much failed requests waste.",
|
|
5947
6024
|
children: renderEfficiencyBody()
|
|
5948
6025
|
}
|
|
5949
6026
|
),
|
|
5950
6027
|
/* @__PURE__ */ jsxs16(Grid2, { container: true, spacing: 2, alignItems: "flex-start", children: [
|
|
5951
|
-
/* @__PURE__ */ jsx17(Grid2, { item: true, xs: 12, md: 5, children: /* @__PURE__ */ jsx17(
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
/* @__PURE__ */ jsxs16(Box16, { sx: { display: "flex",
|
|
5958
|
-
/* @__PURE__ */
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
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
|
+
) })
|
|
5976
6069
|
] }),
|
|
5977
|
-
/* @__PURE__ */ jsx17(
|
|
5978
|
-
|
|
5979
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: "Model" }),
|
|
5980
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Spend" }),
|
|
5981
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Requests" }),
|
|
5982
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Failure" }),
|
|
5983
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: "Avg / req" }),
|
|
5984
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: "Signal" })
|
|
5985
|
-
] }) }),
|
|
5986
|
-
/* @__PURE__ */ jsx17(TableBody6, { children: opportunities.length === 0 ? /* @__PURE__ */ jsx17(TableRow6, { children: /* @__PURE__ */ jsx17(TableCell6, { colSpan: 6, children: /* @__PURE__ */ jsx17(EmptyState, { message: "No model activity in this period" }) }) }) : opportunities.map((o) => /* @__PURE__ */ jsxs16(TableRow6, { children: [
|
|
5987
|
-
/* @__PURE__ */ jsx17(TableCell6, { children: /* @__PURE__ */ jsx17(Typography14, { variant: "body2", sx: { fontWeight: 600 }, children: o.model }) }),
|
|
5988
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtUsd(o.spend) }),
|
|
5989
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtInt(o.requests) }),
|
|
5990
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtPct1(o.failureRate) }),
|
|
5991
|
-
/* @__PURE__ */ jsx17(TableCell6, { align: "right", children: fmtUsd(o.avgCostPerRequest) }),
|
|
5992
|
-
/* @__PURE__ */ jsxs16(TableCell6, { children: [
|
|
5993
|
-
/* @__PURE__ */ jsx17(StatusPill, { label: FLAG_LABEL[o.flag], tone: FLAG_TONE[o.flag], title: o.hint }),
|
|
5994
|
-
/* @__PURE__ */ jsx17(Typography14, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.25 }, children: o.hint })
|
|
5995
|
-
] })
|
|
5996
|
-
] }, o.model)) })
|
|
5997
|
-
] }) }) }),
|
|
5998
|
-
/* @__PURE__ */ jsx17(SectionCard, { title: "Quality scores", children: /* @__PURE__ */ jsx17(
|
|
5999
|
-
EmptyState,
|
|
6070
|
+
/* @__PURE__ */ jsx17(
|
|
6071
|
+
SectionCard,
|
|
6000
6072
|
{
|
|
6001
|
-
|
|
6002
|
-
|
|
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
|
+
] }) })
|
|
6003
6097
|
}
|
|
6004
|
-
)
|
|
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
|
+
)
|
|
6005
6107
|
] });
|
|
6006
6108
|
};
|
|
6007
6109
|
}
|