@acarmisc/backstage-plugin-litellm 0.21.0 → 0.21.2
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/budget.d.ts +4 -3
- package/dist/index.cjs.js +65 -30
- package/dist/index.cjs.js.map +2 -2
- package/dist/index.esm.js +65 -30
- package/dist/index.esm.js.map +2 -2
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -2802,16 +2802,14 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
|
|
|
2802
2802
|
};
|
|
2803
2803
|
}
|
|
2804
2804
|
function budgetHeadline(summary) {
|
|
2805
|
-
const
|
|
2806
|
-
...summary.keys
|
|
2807
|
-
...summary.user ? [summary.user
|
|
2808
|
-
...summary.teams
|
|
2805
|
+
const all = [
|
|
2806
|
+
...summary.keys,
|
|
2807
|
+
...summary.user ? [summary.user] : [],
|
|
2808
|
+
...summary.teams
|
|
2809
2809
|
];
|
|
2810
2810
|
const count = summary.keys.length + summary.hiddenBudgetedKeys + (summary.user ? 1 : 0) + summary.teams.length;
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
closestPct: pcts.length ? Math.max(...pcts) : null
|
|
2814
|
-
};
|
|
2811
|
+
const closest = all.length ? all.reduce((a, b) => b.pct > a.pct ? b : a) : null;
|
|
2812
|
+
return { count, closest };
|
|
2815
2813
|
}
|
|
2816
2814
|
function fmtBudgetDuration(duration) {
|
|
2817
2815
|
if (!duration) return null;
|
|
@@ -2847,6 +2845,7 @@ import IconButton4 from "@mui/material/IconButton";
|
|
|
2847
2845
|
import { ExpandMore as ExpandMore2 } from "@mui/icons-material";
|
|
2848
2846
|
import { alpha as alpha4 } from "@mui/material/styles";
|
|
2849
2847
|
import { useApi as useApi2 } from "@backstage/core-plugin-api";
|
|
2848
|
+
import { Link } from "@backstage/core-components";
|
|
2850
2849
|
function levelToneColor(tone, theme) {
|
|
2851
2850
|
switch (tone) {
|
|
2852
2851
|
case "success":
|
|
@@ -2861,7 +2860,7 @@ function levelToneColor(tone, theme) {
|
|
|
2861
2860
|
return theme.palette.primary.main;
|
|
2862
2861
|
}
|
|
2863
2862
|
}
|
|
2864
|
-
var LevelFrame, EmptyLimitCard, LimitCard, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
|
|
2863
|
+
var LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, MoreKeysNote, HierarchyNote, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
|
|
2865
2864
|
var init_LiteLLMBudgetWidget = __esm({
|
|
2866
2865
|
"src/components/LiteLLMBudgetWidget.tsx"() {
|
|
2867
2866
|
"use strict";
|
|
@@ -2973,6 +2972,26 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2973
2972
|
/* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
2974
2973
|
));
|
|
2975
2974
|
};
|
|
2975
|
+
CLOSEST_LEVEL_LABEL = {
|
|
2976
|
+
key: "key",
|
|
2977
|
+
user: "personal",
|
|
2978
|
+
team: "team"
|
|
2979
|
+
};
|
|
2980
|
+
KEYS_PATH = "/litellm?tab=keys";
|
|
2981
|
+
MoreKeysNote = ({ count }) => /* @__PURE__ */ React7.createElement(Link, { to: KEYS_PATH, variant: "caption" }, "+", count, " more budgeted key", count > 1 ? "s" : "", " further from the cap \u2192");
|
|
2982
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ React7.createElement(
|
|
2983
|
+
Box7,
|
|
2984
|
+
{
|
|
2985
|
+
sx: (theme) => ({
|
|
2986
|
+
px: 1.5,
|
|
2987
|
+
py: 1.25,
|
|
2988
|
+
borderRadius: 1.5,
|
|
2989
|
+
border: "1px dashed",
|
|
2990
|
+
borderColor: alpha4(theme.palette.text.primary, 0.18)
|
|
2991
|
+
})
|
|
2992
|
+
},
|
|
2993
|
+
/* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ React7.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
|
|
2994
|
+
);
|
|
2976
2995
|
TaggedLimit = ({ limit }) => {
|
|
2977
2996
|
const tone = budgetTone2(limit.pct);
|
|
2978
2997
|
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(
|
|
@@ -3002,7 +3021,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3002
3021
|
},
|
|
3003
3022
|
!hasBudgetedKeys && /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3004
3023
|
summary.keys.map((k) => /* @__PURE__ */ React7.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3005
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(
|
|
3024
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys })
|
|
3006
3025
|
), /* @__PURE__ */ React7.createElement(
|
|
3007
3026
|
LevelFrame,
|
|
3008
3027
|
{
|
|
@@ -3012,27 +3031,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3012
3031
|
note: "skipped for team-bound keys"
|
|
3013
3032
|
},
|
|
3014
3033
|
summary.user ? /* @__PURE__ */ React7.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
|
|
3015
|
-
), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ React7.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ React7.createElement(
|
|
3016
|
-
Box7,
|
|
3017
|
-
{
|
|
3018
|
-
sx: (theme) => ({
|
|
3019
|
-
mt: 2,
|
|
3020
|
-
px: 1.5,
|
|
3021
|
-
py: 1.25,
|
|
3022
|
-
borderRadius: 1.5,
|
|
3023
|
-
border: "1px dashed",
|
|
3024
|
-
borderColor: alpha4(theme.palette.text.primary, 0.18)
|
|
3025
|
-
})
|
|
3026
|
-
},
|
|
3027
|
-
/* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", display: "block" }, "The first cap a request runs into is the one that stops it. A limit with a reset window (e.g. daily or every 30 days) clears its spend to $0 when the window closes; without a window it never resets.")
|
|
3028
|
-
));
|
|
3034
|
+
), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ React7.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ React7.createElement(Box7, { sx: { mt: 2 } }, /* @__PURE__ */ React7.createElement(HierarchyNote, { withReset: true })));
|
|
3029
3035
|
};
|
|
3030
3036
|
CompactBody = ({ summary }) => {
|
|
3031
3037
|
const { count } = budgetHeadline(summary);
|
|
3032
3038
|
if (count === 0) {
|
|
3033
3039
|
return /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
|
|
3034
3040
|
}
|
|
3035
|
-
return /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, summary.keys.map((k) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(
|
|
3041
|
+
return /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ React7.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys }), summary.user && /* @__PURE__ */ React7.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
|
|
3036
3042
|
};
|
|
3037
3043
|
LiteLLMBudgetWidget = ({
|
|
3038
3044
|
title = "Budget Policy",
|
|
@@ -3079,7 +3085,9 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3079
3085
|
);
|
|
3080
3086
|
const headline = useMemo6(() => budgetHeadline(summary), [summary]);
|
|
3081
3087
|
const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
3082
|
-
const closestWord = headline.
|
|
3088
|
+
const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
|
|
3089
|
+
headline.closest.pct
|
|
3090
|
+
)}%`;
|
|
3083
3091
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3084
3092
|
const body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, loading && /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ React7.createElement(CircularProgress4, { size: 32 })), !loading && error && /* @__PURE__ */ React7.createElement(Alert3, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ React7.createElement(CompactBody, { summary }) : /* @__PURE__ */ React7.createElement(FullBody, { summary })));
|
|
3085
3093
|
return /* @__PURE__ */ React7.createElement(Paper5, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ React7.createElement(
|
|
@@ -3095,7 +3103,13 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3095
3103
|
mb: expanded ? 1.5 : 0
|
|
3096
3104
|
}
|
|
3097
3105
|
},
|
|
3098
|
-
/* @__PURE__ */ React7.createElement(Box7, { sx: { minWidth: 0, flex: 1
|
|
3106
|
+
/* @__PURE__ */ React7.createElement(Box7, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ React7.createElement(Box7, { sx: { mt: 0.5 } }, /* @__PURE__ */ React7.createElement(
|
|
3107
|
+
StatusPill,
|
|
3108
|
+
{
|
|
3109
|
+
label: summaryText,
|
|
3110
|
+
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3111
|
+
}
|
|
3112
|
+
))),
|
|
3099
3113
|
/* @__PURE__ */ React7.createElement(
|
|
3100
3114
|
IconButton4,
|
|
3101
3115
|
{
|
|
@@ -3833,6 +3847,7 @@ import Paper7 from "@mui/material/Paper";
|
|
|
3833
3847
|
import Tabs2 from "@mui/material/Tabs";
|
|
3834
3848
|
import Tab2 from "@mui/material/Tab";
|
|
3835
3849
|
import Grid2 from "@mui/material/Grid";
|
|
3850
|
+
import { useSearchParams } from "react-router-dom";
|
|
3836
3851
|
import { useAsync as useAsync3, useAsyncRetry } from "react-use";
|
|
3837
3852
|
import { useApi as useApi3 } from "@backstage/core-plugin-api";
|
|
3838
3853
|
import { usePermission } from "@backstage/plugin-permission-react";
|
|
@@ -3850,7 +3865,7 @@ function initDateRange() {
|
|
|
3850
3865
|
else start.setDate(start.getDate() - 7);
|
|
3851
3866
|
return { start, end };
|
|
3852
3867
|
}
|
|
3853
|
-
var PERIOD_LS_KEY2, LiteLLMPage;
|
|
3868
|
+
var PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
|
|
3854
3869
|
var init_LiteLLMPage = __esm({
|
|
3855
3870
|
"src/components/LiteLLMPage.tsx"() {
|
|
3856
3871
|
"use strict";
|
|
@@ -3866,6 +3881,8 @@ var init_LiteLLMPage = __esm({
|
|
|
3866
3881
|
init_api();
|
|
3867
3882
|
init_permissions();
|
|
3868
3883
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
3884
|
+
PAGE_TABS = ["overview", "keys", "teams", "models", "audit"];
|
|
3885
|
+
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
3869
3886
|
LiteLLMPage = () => {
|
|
3870
3887
|
const api = useApi3(liteLlmApiRef);
|
|
3871
3888
|
const [dateRange, setDateRange] = useState9(initDateRange);
|
|
@@ -3876,7 +3893,25 @@ var init_LiteLLMPage = __esm({
|
|
|
3876
3893
|
return "7d";
|
|
3877
3894
|
}
|
|
3878
3895
|
});
|
|
3879
|
-
const [
|
|
3896
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
3897
|
+
const [activeTab, setActiveTab] = useState9(() => {
|
|
3898
|
+
const t = searchParams.get("tab");
|
|
3899
|
+
return isPageTab(t) ? t : "overview";
|
|
3900
|
+
});
|
|
3901
|
+
const selectTab = useCallback3(
|
|
3902
|
+
(tab) => {
|
|
3903
|
+
setActiveTab(tab);
|
|
3904
|
+
setSearchParams(
|
|
3905
|
+
(prev) => {
|
|
3906
|
+
const next = new URLSearchParams(prev);
|
|
3907
|
+
next.set("tab", tab);
|
|
3908
|
+
return next;
|
|
3909
|
+
},
|
|
3910
|
+
{ replace: true }
|
|
3911
|
+
);
|
|
3912
|
+
},
|
|
3913
|
+
[setSearchParams]
|
|
3914
|
+
);
|
|
3880
3915
|
const [snackbar, setSnackbar] = useState9(null);
|
|
3881
3916
|
const [generateDialogOpen, setGenerateDialogOpen] = useState9(false);
|
|
3882
3917
|
const [manageTeam, setManageTeam] = useState9(null);
|
|
@@ -4083,7 +4118,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4083
4118
|
Tabs2,
|
|
4084
4119
|
{
|
|
4085
4120
|
value: activeTab,
|
|
4086
|
-
onChange: (_, v) =>
|
|
4121
|
+
onChange: (_, v) => selectTab(v),
|
|
4087
4122
|
variant: "scrollable",
|
|
4088
4123
|
scrollButtons: "auto",
|
|
4089
4124
|
sx: {
|