@acarmisc/backstage-plugin-litellm 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/LiteLLMHomeWidget.d.ts +8 -0
- package/dist/index.cjs.js +86 -0
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +96 -0
- package/dist/index.esm.js.map +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface LiteLLMHomeWidgetProps {
|
|
3
|
+
/** Default period when the widget mounts. Defaults to '7d'. */
|
|
4
|
+
defaultPeriod?: 'today' | '7d' | '30d';
|
|
5
|
+
/** Optional title override. Defaults to 'LiteLLM Usage'. */
|
|
6
|
+
title?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const LiteLLMHomeWidget: React.FC<LiteLLMHomeWidgetProps>;
|
package/dist/index.cjs.js
CHANGED
|
@@ -868,6 +868,7 @@ var index_exports = {};
|
|
|
868
868
|
__export(index_exports, {
|
|
869
869
|
DashboardHeader: () => DashboardHeader,
|
|
870
870
|
KeysTable: () => KeysTable,
|
|
871
|
+
LiteLLMHomeWidget: () => LiteLLMHomeWidget,
|
|
871
872
|
LiteLLMPage: () => LiteLLMPage,
|
|
872
873
|
LiteLlmApi: () => LiteLlmApi,
|
|
873
874
|
TeamUsage: () => TeamUsage,
|
|
@@ -911,5 +912,90 @@ init_DashboardHeader();
|
|
|
911
912
|
init_KeysTable();
|
|
912
913
|
init_UsageStats();
|
|
913
914
|
init_TeamUsage();
|
|
915
|
+
|
|
916
|
+
// src/components/LiteLLMHomeWidget.tsx
|
|
917
|
+
var import_react7 = __toESM(require("react"));
|
|
918
|
+
var import_material6 = require("@mui/material");
|
|
919
|
+
var import_recharts3 = require("recharts");
|
|
920
|
+
var import_core_plugin_api3 = require("@backstage/core-plugin-api");
|
|
921
|
+
init_api();
|
|
922
|
+
var fmtUsd2 = (n) => `$${(n ?? 0).toFixed(n < 1 ? 4 : 2)}`;
|
|
923
|
+
var fmtInt2 = (n) => (n ?? 0).toLocaleString();
|
|
924
|
+
function presetToDateRange(preset) {
|
|
925
|
+
const end = /* @__PURE__ */ new Date();
|
|
926
|
+
const start = /* @__PURE__ */ new Date();
|
|
927
|
+
if (preset === "today") {
|
|
928
|
+
start.setHours(0, 0, 0, 0);
|
|
929
|
+
} else if (preset === "7d") {
|
|
930
|
+
start.setDate(start.getDate() - 7);
|
|
931
|
+
} else {
|
|
932
|
+
start.setDate(start.getDate() - 30);
|
|
933
|
+
}
|
|
934
|
+
return { start, end };
|
|
935
|
+
}
|
|
936
|
+
var Kpi = ({ label, value }) => /* @__PURE__ */ import_react7.default.createElement(import_material6.Box, null, /* @__PURE__ */ import_react7.default.createElement(import_material6.Typography, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react7.default.createElement(import_material6.Typography, { variant: "subtitle1", fontWeight: 600 }, value));
|
|
937
|
+
var LiteLLMHomeWidget = ({
|
|
938
|
+
defaultPeriod = "7d",
|
|
939
|
+
title = "LiteLLM Usage"
|
|
940
|
+
}) => {
|
|
941
|
+
const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
|
|
942
|
+
const [period, setPeriod] = (0, import_react7.useState)(defaultPeriod);
|
|
943
|
+
const [loading, setLoading] = (0, import_react7.useState)(true);
|
|
944
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
945
|
+
const [usage, setUsage] = (0, import_react7.useState)(null);
|
|
946
|
+
const [keys, setKeys] = (0, import_react7.useState)([]);
|
|
947
|
+
(0, import_react7.useEffect)(() => {
|
|
948
|
+
let cancelled = false;
|
|
949
|
+
setLoading(true);
|
|
950
|
+
setError(null);
|
|
951
|
+
const { start, end } = presetToDateRange(period);
|
|
952
|
+
const startDate = start.toISOString().split("T")[0];
|
|
953
|
+
const endDate = end.toISOString().split("T")[0];
|
|
954
|
+
Promise.all([api.getUsage(startDate, endDate), api.listKeys()]).then(([usageData, keysData]) => {
|
|
955
|
+
if (!cancelled) {
|
|
956
|
+
setUsage(usageData);
|
|
957
|
+
setKeys(keysData);
|
|
958
|
+
setLoading(false);
|
|
959
|
+
}
|
|
960
|
+
}).catch((err) => {
|
|
961
|
+
if (!cancelled) {
|
|
962
|
+
setError(err.message ?? "Failed to load usage data");
|
|
963
|
+
setLoading(false);
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
return () => {
|
|
967
|
+
cancelled = true;
|
|
968
|
+
};
|
|
969
|
+
}, [api, period]);
|
|
970
|
+
const dailyData = (usage?.daily_usage ?? []).map((d) => ({
|
|
971
|
+
date: d.date,
|
|
972
|
+
spend: d.spend
|
|
973
|
+
}));
|
|
974
|
+
const hasSparkline = dailyData.length > 0;
|
|
975
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_material6.Paper, { sx: { p: 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_material6.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react7.default.createElement(import_material6.Typography, { variant: "h6" }, title), /* @__PURE__ */ import_react7.default.createElement(import_material6.FormControl, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react7.default.createElement(
|
|
976
|
+
import_material6.Select,
|
|
977
|
+
{
|
|
978
|
+
value: period,
|
|
979
|
+
onChange: (e) => setPeriod(e.target.value),
|
|
980
|
+
displayEmpty: true
|
|
981
|
+
},
|
|
982
|
+
/* @__PURE__ */ import_react7.default.createElement(import_material6.MenuItem, { value: "today" }, "Today"),
|
|
983
|
+
/* @__PURE__ */ import_react7.default.createElement(import_material6.MenuItem, { value: "7d" }, "7d"),
|
|
984
|
+
/* @__PURE__ */ import_react7.default.createElement(import_material6.MenuItem, { value: "30d" }, "30d")
|
|
985
|
+
))), loading && /* @__PURE__ */ import_react7.default.createElement(import_material6.Box, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react7.default.createElement(import_material6.CircularProgress, { size: 32 })), !loading && error && /* @__PURE__ */ import_react7.default.createElement(import_material6.Alert, { severity: "error", sx: { mt: 1 } }, error), !loading && !error && /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(import_material6.Grid, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_material6.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react7.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd2(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react7.default.createElement(import_material6.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react7.default.createElement(Kpi, { label: "Tokens In", value: fmtInt2(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react7.default.createElement(import_material6.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react7.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt2(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react7.default.createElement(import_material6.Grid, { item: true, xs: 6 }, /* @__PURE__ */ import_react7.default.createElement(Kpi, { label: "Keys", value: fmtInt2(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react7.default.createElement(import_material6.Box, { height: 120 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react7.default.createElement(
|
|
986
|
+
import_recharts3.Area,
|
|
987
|
+
{
|
|
988
|
+
type: "monotone",
|
|
989
|
+
dataKey: "spend",
|
|
990
|
+
stroke: "#8884d8",
|
|
991
|
+
fill: "#8884d8",
|
|
992
|
+
fillOpacity: 0.3,
|
|
993
|
+
dot: false,
|
|
994
|
+
isAnimationActive: false
|
|
995
|
+
}
|
|
996
|
+
))))));
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
// src/index.ts
|
|
914
1000
|
init_api();
|
|
915
1001
|
//# sourceMappingURL=index.cjs.js.map
|