@acarmisc/backstage-plugin-litellm 0.24.0 → 0.25.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/budget.d.ts +8 -0
- package/dist/components/BudgetLimitList.d.ts +41 -0
- package/dist/components/LiteLLMBudgetGauges.d.ts +34 -2
- package/dist/index.cjs.js +498 -331
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +440 -273
- package/dist/index.esm.js.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -2931,6 +2931,13 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
|
|
|
2931
2931
|
teams: teamLimits
|
|
2932
2932
|
};
|
|
2933
2933
|
}
|
|
2934
|
+
function allBudgetLimits(summary) {
|
|
2935
|
+
return [
|
|
2936
|
+
...summary.keys,
|
|
2937
|
+
...summary.user ? [summary.user] : [],
|
|
2938
|
+
...summary.teams
|
|
2939
|
+
];
|
|
2940
|
+
}
|
|
2934
2941
|
function buildBudgetGauges(summary) {
|
|
2935
2942
|
const byLevel = {
|
|
2936
2943
|
key: summary.keys,
|
|
@@ -2981,7 +2988,7 @@ var init_budget = __esm({
|
|
|
2981
2988
|
}
|
|
2982
2989
|
});
|
|
2983
2990
|
|
|
2984
|
-
// src/components/
|
|
2991
|
+
// src/components/BudgetLimitList.tsx
|
|
2985
2992
|
function levelToneColor(tone, theme) {
|
|
2986
2993
|
switch (tone) {
|
|
2987
2994
|
case "success":
|
|
@@ -2996,25 +3003,125 @@ function levelToneColor(tone, theme) {
|
|
|
2996
3003
|
return theme.palette.primary.main;
|
|
2997
3004
|
}
|
|
2998
3005
|
}
|
|
2999
|
-
var import_react7,
|
|
3000
|
-
var
|
|
3001
|
-
"src/components/
|
|
3006
|
+
var import_react7, import_Box7, import_Paper5, import_Typography7, import_styles4, import_core_components, LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
|
|
3007
|
+
var init_BudgetLimitList = __esm({
|
|
3008
|
+
"src/components/BudgetLimitList.tsx"() {
|
|
3002
3009
|
"use strict";
|
|
3003
3010
|
import_react7 = __toESM(require("react"));
|
|
3004
|
-
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
3005
3011
|
import_Box7 = __toESM(require("@mui/material/Box"));
|
|
3012
|
+
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
3006
3013
|
import_Typography7 = __toESM(require("@mui/material/Typography"));
|
|
3014
|
+
import_styles4 = require("@mui/material/styles");
|
|
3015
|
+
import_core_components = require("@backstage/core-components");
|
|
3016
|
+
init_budget();
|
|
3017
|
+
init_format();
|
|
3018
|
+
init_ui();
|
|
3019
|
+
LimitCard = ({ limit }) => {
|
|
3020
|
+
const tone = budgetTone2(limit.pct);
|
|
3021
|
+
const pct = Math.round(limit.pct);
|
|
3022
|
+
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
3023
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react7.default.createElement(
|
|
3024
|
+
import_Typography7.default,
|
|
3025
|
+
{
|
|
3026
|
+
variant: "body2",
|
|
3027
|
+
sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
3028
|
+
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
3029
|
+
},
|
|
3030
|
+
limit.label
|
|
3031
|
+
), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
|
|
3032
|
+
import_Typography7.default,
|
|
3033
|
+
{
|
|
3034
|
+
variant: "caption",
|
|
3035
|
+
color: "text.secondary",
|
|
3036
|
+
sx: {
|
|
3037
|
+
fontSize: 10.5,
|
|
3038
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
3039
|
+
display: "block",
|
|
3040
|
+
overflow: "hidden",
|
|
3041
|
+
textOverflow: "ellipsis",
|
|
3042
|
+
whiteSpace: "nowrap"
|
|
3043
|
+
}
|
|
3044
|
+
},
|
|
3045
|
+
limit.sublabel
|
|
3046
|
+
)), limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 1 } }, /* @__PURE__ */ import_react7.default.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ import_react7.default.createElement(
|
|
3047
|
+
import_Box7.default,
|
|
3048
|
+
{
|
|
3049
|
+
sx: {
|
|
3050
|
+
display: "flex",
|
|
3051
|
+
justifyContent: "space-between",
|
|
3052
|
+
alignItems: "center",
|
|
3053
|
+
gap: 1,
|
|
3054
|
+
mt: 0.75
|
|
3055
|
+
}
|
|
3056
|
+
},
|
|
3057
|
+
/* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, "Hidden by admin") : /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
|
|
3058
|
+
/* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3059
|
+
));
|
|
3060
|
+
};
|
|
3061
|
+
TaggedLimit = ({ limit }) => {
|
|
3062
|
+
const theme = (0, import_styles4.useTheme)();
|
|
3063
|
+
const tone = budgetTone2(limit.pct);
|
|
3064
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
|
|
3065
|
+
import_Typography7.default,
|
|
3066
|
+
{
|
|
3067
|
+
sx: {
|
|
3068
|
+
fontSize: 11,
|
|
3069
|
+
fontWeight: 700,
|
|
3070
|
+
letterSpacing: "0.08em",
|
|
3071
|
+
textTransform: "uppercase",
|
|
3072
|
+
color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
|
|
3073
|
+
mb: 0.5
|
|
3074
|
+
}
|
|
3075
|
+
},
|
|
3076
|
+
limit.kind
|
|
3077
|
+
), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
|
|
3078
|
+
};
|
|
3079
|
+
MoreKeysNote = ({
|
|
3080
|
+
count,
|
|
3081
|
+
href,
|
|
3082
|
+
label
|
|
3083
|
+
}) => {
|
|
3084
|
+
const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
|
|
3085
|
+
if (!href) {
|
|
3086
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, text);
|
|
3087
|
+
}
|
|
3088
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_core_components.Link, { to: href, variant: "caption" }, text, " \u2192");
|
|
3089
|
+
};
|
|
3090
|
+
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, limits.map((limit) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: `${limit.kind}:${limit.sublabel ?? limit.label}`, limit })), hiddenKeyCount > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: hiddenKeyCount, href: keysHref }));
|
|
3091
|
+
LimitListPanel = ({ children }) => /* @__PURE__ */ import_react7.default.createElement(
|
|
3092
|
+
import_Box7.default,
|
|
3093
|
+
{
|
|
3094
|
+
sx: (theme) => ({
|
|
3095
|
+
px: 1.5,
|
|
3096
|
+
py: 1.5,
|
|
3097
|
+
borderRadius: 1.5,
|
|
3098
|
+
bgcolor: (0, import_styles4.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.02)
|
|
3099
|
+
})
|
|
3100
|
+
},
|
|
3101
|
+
children
|
|
3102
|
+
);
|
|
3103
|
+
}
|
|
3104
|
+
});
|
|
3105
|
+
|
|
3106
|
+
// src/components/LiteLLMBudgetWidget.tsx
|
|
3107
|
+
var import_react8, import_Paper6, import_Box8, import_Typography8, import_CircularProgress4, import_Alert3, import_Collapse, import_IconButton4, import_icons_material5, import_styles5, import_core_plugin_api3, LevelFrame, EmptyLimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, HierarchyNote, FullBody, CompactBody, LiteLLMBudgetWidget;
|
|
3108
|
+
var init_LiteLLMBudgetWidget = __esm({
|
|
3109
|
+
"src/components/LiteLLMBudgetWidget.tsx"() {
|
|
3110
|
+
"use strict";
|
|
3111
|
+
import_react8 = __toESM(require("react"));
|
|
3112
|
+
import_Paper6 = __toESM(require("@mui/material/Paper"));
|
|
3113
|
+
import_Box8 = __toESM(require("@mui/material/Box"));
|
|
3114
|
+
import_Typography8 = __toESM(require("@mui/material/Typography"));
|
|
3007
3115
|
import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
|
|
3008
3116
|
import_Alert3 = __toESM(require("@mui/material/Alert"));
|
|
3009
3117
|
import_Collapse = __toESM(require("@mui/material/Collapse"));
|
|
3010
3118
|
import_IconButton4 = __toESM(require("@mui/material/IconButton"));
|
|
3011
3119
|
import_icons_material5 = require("@mui/icons-material");
|
|
3012
|
-
|
|
3120
|
+
import_styles5 = require("@mui/material/styles");
|
|
3013
3121
|
import_core_plugin_api3 = require("@backstage/core-plugin-api");
|
|
3014
|
-
import_core_components = require("@backstage/core-components");
|
|
3015
3122
|
init_api();
|
|
3016
|
-
init_format();
|
|
3017
3123
|
init_ui();
|
|
3124
|
+
init_BudgetLimitList();
|
|
3018
3125
|
init_budget();
|
|
3019
3126
|
LevelFrame = ({
|
|
3020
3127
|
rank,
|
|
@@ -3024,8 +3131,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3024
3131
|
tone,
|
|
3025
3132
|
isLast,
|
|
3026
3133
|
children
|
|
3027
|
-
}) => /* @__PURE__ */
|
|
3028
|
-
|
|
3134
|
+
}) => /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ import_react8.default.createElement(
|
|
3135
|
+
import_Box8.default,
|
|
3029
3136
|
{
|
|
3030
3137
|
sx: (theme) => ({
|
|
3031
3138
|
width: 26,
|
|
@@ -3034,7 +3141,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3034
3141
|
display: "flex",
|
|
3035
3142
|
alignItems: "center",
|
|
3036
3143
|
justifyContent: "center",
|
|
3037
|
-
bgcolor: (0,
|
|
3144
|
+
bgcolor: (0, import_styles5.alpha)(levelToneColor(tone, theme), 0.14),
|
|
3038
3145
|
color: levelToneColor(tone, theme),
|
|
3039
3146
|
fontSize: 12,
|
|
3040
3147
|
fontWeight: 700,
|
|
@@ -3042,18 +3149,18 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3042
3149
|
})
|
|
3043
3150
|
},
|
|
3044
3151
|
rank
|
|
3045
|
-
), /* @__PURE__ */
|
|
3046
|
-
|
|
3152
|
+
), /* @__PURE__ */ import_react8.default.createElement(
|
|
3153
|
+
import_Box8.default,
|
|
3047
3154
|
{
|
|
3048
3155
|
sx: (theme) => ({
|
|
3049
3156
|
flex: 1,
|
|
3050
3157
|
width: 2,
|
|
3051
3158
|
minHeight: 14,
|
|
3052
|
-
bgcolor: isLast ? "transparent" : (0,
|
|
3159
|
+
bgcolor: isLast ? "transparent" : (0, import_styles5.alpha)(theme.palette.text.primary, 0.12)
|
|
3053
3160
|
})
|
|
3054
3161
|
}
|
|
3055
|
-
)), /* @__PURE__ */
|
|
3056
|
-
|
|
3162
|
+
)), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ import_react8.default.createElement(
|
|
3163
|
+
import_Typography8.default,
|
|
3057
3164
|
{
|
|
3058
3165
|
sx: (theme) => ({
|
|
3059
3166
|
fontSize: 12.5,
|
|
@@ -3064,102 +3171,42 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3064
3171
|
})
|
|
3065
3172
|
},
|
|
3066
3173
|
name
|
|
3067
|
-
), /* @__PURE__ */
|
|
3068
|
-
EmptyLimitCard = ({ children }) => /* @__PURE__ */
|
|
3069
|
-
|
|
3174
|
+
), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ import_react8.default.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
|
|
3175
|
+
EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react8.default.createElement(
|
|
3176
|
+
import_Paper6.default,
|
|
3070
3177
|
{
|
|
3071
3178
|
variant: "outlined",
|
|
3072
3179
|
sx: (theme) => ({
|
|
3073
3180
|
px: 1.5,
|
|
3074
3181
|
py: 1.25,
|
|
3075
3182
|
borderRadius: 1.5,
|
|
3076
|
-
bgcolor: (0,
|
|
3183
|
+
bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
|
|
3077
3184
|
})
|
|
3078
3185
|
},
|
|
3079
|
-
/* @__PURE__ */
|
|
3186
|
+
/* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
|
|
3080
3187
|
);
|
|
3081
|
-
LimitCard = ({ limit }) => {
|
|
3082
|
-
const tone = budgetTone2(limit.pct);
|
|
3083
|
-
const pct = Math.round(limit.pct);
|
|
3084
|
-
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
3085
|
-
return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react7.default.createElement(
|
|
3086
|
-
import_Typography7.default,
|
|
3087
|
-
{
|
|
3088
|
-
variant: "body2",
|
|
3089
|
-
sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
3090
|
-
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
3091
|
-
},
|
|
3092
|
-
limit.label
|
|
3093
|
-
), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
|
|
3094
|
-
import_Typography7.default,
|
|
3095
|
-
{
|
|
3096
|
-
variant: "caption",
|
|
3097
|
-
color: "text.secondary",
|
|
3098
|
-
sx: {
|
|
3099
|
-
fontSize: 10.5,
|
|
3100
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
3101
|
-
display: "block",
|
|
3102
|
-
overflow: "hidden",
|
|
3103
|
-
textOverflow: "ellipsis",
|
|
3104
|
-
whiteSpace: "nowrap"
|
|
3105
|
-
}
|
|
3106
|
-
},
|
|
3107
|
-
limit.sublabel
|
|
3108
|
-
)), limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 1 } }, /* @__PURE__ */ import_react7.default.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ import_react7.default.createElement(
|
|
3109
|
-
import_Box7.default,
|
|
3110
|
-
{
|
|
3111
|
-
sx: {
|
|
3112
|
-
display: "flex",
|
|
3113
|
-
justifyContent: "space-between",
|
|
3114
|
-
alignItems: "center",
|
|
3115
|
-
gap: 1,
|
|
3116
|
-
mt: 0.75
|
|
3117
|
-
}
|
|
3118
|
-
},
|
|
3119
|
-
/* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, "Hidden by admin") : /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
|
|
3120
|
-
/* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3121
|
-
));
|
|
3122
|
-
};
|
|
3123
3188
|
CLOSEST_LEVEL_LABEL = {
|
|
3124
3189
|
key: "key",
|
|
3125
3190
|
user: "personal",
|
|
3126
3191
|
team: "team"
|
|
3127
3192
|
};
|
|
3128
3193
|
KEYS_PATH = "/litellm?tab=keys";
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
import_Box7.default,
|
|
3194
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react8.default.createElement(
|
|
3195
|
+
import_Box8.default,
|
|
3132
3196
|
{
|
|
3133
3197
|
sx: (theme) => ({
|
|
3134
3198
|
px: 1.5,
|
|
3135
3199
|
py: 1.25,
|
|
3136
3200
|
borderRadius: 1.5,
|
|
3137
3201
|
border: "1px dashed",
|
|
3138
|
-
borderColor: (0,
|
|
3202
|
+
borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
|
|
3139
3203
|
})
|
|
3140
3204
|
},
|
|
3141
|
-
/* @__PURE__ */
|
|
3205
|
+
/* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react8.default.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.")
|
|
3142
3206
|
);
|
|
3143
|
-
TaggedLimit = ({ limit }) => {
|
|
3144
|
-
const tone = budgetTone2(limit.pct);
|
|
3145
|
-
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
|
|
3146
|
-
import_Typography7.default,
|
|
3147
|
-
{
|
|
3148
|
-
sx: (theme) => ({
|
|
3149
|
-
fontSize: 11,
|
|
3150
|
-
fontWeight: 700,
|
|
3151
|
-
letterSpacing: "0.08em",
|
|
3152
|
-
textTransform: "uppercase",
|
|
3153
|
-
color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
|
|
3154
|
-
mb: 0.5
|
|
3155
|
-
})
|
|
3156
|
-
},
|
|
3157
|
-
limit.kind
|
|
3158
|
-
), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
|
|
3159
|
-
};
|
|
3160
3207
|
FullBody = ({ summary }) => {
|
|
3161
3208
|
const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
|
|
3162
|
-
return /* @__PURE__ */
|
|
3209
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(
|
|
3163
3210
|
LevelFrame,
|
|
3164
3211
|
{
|
|
3165
3212
|
rank: 1,
|
|
@@ -3167,10 +3214,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3167
3214
|
tagline: "per-key cap \xB7 highest priority",
|
|
3168
3215
|
note: hasBudgetedKeys ? void 0 : "no key budgets set"
|
|
3169
3216
|
},
|
|
3170
|
-
!hasBudgetedKeys && /* @__PURE__ */
|
|
3171
|
-
summary.keys.map((k) => /* @__PURE__ */
|
|
3172
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */
|
|
3173
|
-
), /* @__PURE__ */
|
|
3217
|
+
!hasBudgetedKeys && /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3218
|
+
summary.keys.map((k) => /* @__PURE__ */ import_react8.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3219
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
|
|
3220
|
+
), /* @__PURE__ */ import_react8.default.createElement(
|
|
3174
3221
|
LevelFrame,
|
|
3175
3222
|
{
|
|
3176
3223
|
rank: 2,
|
|
@@ -3178,15 +3225,15 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3178
3225
|
tagline: "cap on everything you spend with your own keys",
|
|
3179
3226
|
note: "skipped for team-bound keys"
|
|
3180
3227
|
},
|
|
3181
|
-
summary.user ? /* @__PURE__ */
|
|
3182
|
-
), /* @__PURE__ */
|
|
3228
|
+
summary.user ? /* @__PURE__ */ import_react8.default.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
|
|
3229
|
+
), /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react8.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react8.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement(import_Box8.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react8.default.createElement(HierarchyNote, { withReset: true })));
|
|
3183
3230
|
};
|
|
3184
3231
|
CompactBody = ({ summary }) => {
|
|
3185
3232
|
const { count } = budgetHeadline(summary);
|
|
3186
3233
|
if (count === 0) {
|
|
3187
|
-
return /* @__PURE__ */
|
|
3234
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
|
|
3188
3235
|
}
|
|
3189
|
-
return /* @__PURE__ */
|
|
3236
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react8.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
|
|
3190
3237
|
};
|
|
3191
3238
|
LiteLLMBudgetWidget = ({
|
|
3192
3239
|
title = "Budget Policy",
|
|
@@ -3197,13 +3244,13 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3197
3244
|
action
|
|
3198
3245
|
}) => {
|
|
3199
3246
|
const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
|
|
3200
|
-
const [loading, setLoading] = (0,
|
|
3201
|
-
const [error, setError] = (0,
|
|
3202
|
-
const [user, setUser] = (0,
|
|
3203
|
-
const [teams, setTeams] = (0,
|
|
3204
|
-
const [keys, setKeys] = (0,
|
|
3205
|
-
const [expanded, setExpanded] = (0,
|
|
3206
|
-
(0,
|
|
3247
|
+
const [loading, setLoading] = (0, import_react8.useState)(true);
|
|
3248
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
3249
|
+
const [user, setUser] = (0, import_react8.useState)(null);
|
|
3250
|
+
const [teams, setTeams] = (0, import_react8.useState)([]);
|
|
3251
|
+
const [keys, setKeys] = (0, import_react8.useState)([]);
|
|
3252
|
+
const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
|
|
3253
|
+
(0, import_react8.useEffect)(() => {
|
|
3207
3254
|
let cancelled = false;
|
|
3208
3255
|
setLoading(true);
|
|
3209
3256
|
setError(null);
|
|
@@ -3227,19 +3274,19 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3227
3274
|
cancelled = true;
|
|
3228
3275
|
};
|
|
3229
3276
|
}, [api]);
|
|
3230
|
-
const summary = (0,
|
|
3277
|
+
const summary = (0, import_react8.useMemo)(
|
|
3231
3278
|
() => buildBudgetSummary(user, teams, keys, maxKeys),
|
|
3232
3279
|
[user, teams, keys, maxKeys]
|
|
3233
3280
|
);
|
|
3234
|
-
const headline = (0,
|
|
3281
|
+
const headline = (0, import_react8.useMemo)(() => budgetHeadline(summary), [summary]);
|
|
3235
3282
|
const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
3236
3283
|
const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
|
|
3237
3284
|
headline.closest.pct
|
|
3238
3285
|
)}%`;
|
|
3239
3286
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3240
|
-
const body = /* @__PURE__ */
|
|
3241
|
-
return /* @__PURE__ */
|
|
3242
|
-
|
|
3287
|
+
const body = /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, loading && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ import_react8.default.createElement(import_CircularProgress4.default, { size: 32 })), !loading && error && /* @__PURE__ */ import_react8.default.createElement(import_Alert3.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ import_react8.default.createElement(CompactBody, { summary }) : /* @__PURE__ */ import_react8.default.createElement(FullBody, { summary })));
|
|
3288
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Paper6.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react8.default.createElement(
|
|
3289
|
+
import_Box8.default,
|
|
3243
3290
|
{
|
|
3244
3291
|
onClick: () => setExpanded((e) => !e),
|
|
3245
3292
|
sx: {
|
|
@@ -3251,14 +3298,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3251
3298
|
mb: expanded ? 1.5 : 0
|
|
3252
3299
|
}
|
|
3253
3300
|
},
|
|
3254
|
-
/* @__PURE__ */
|
|
3301
|
+
/* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { mt: 0.5 } }, /* @__PURE__ */ import_react8.default.createElement(
|
|
3255
3302
|
StatusPill,
|
|
3256
3303
|
{
|
|
3257
3304
|
label: summaryText,
|
|
3258
3305
|
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3259
3306
|
}
|
|
3260
3307
|
))),
|
|
3261
|
-
/* @__PURE__ */
|
|
3308
|
+
/* @__PURE__ */ import_react8.default.createElement(
|
|
3262
3309
|
import_IconButton4.default,
|
|
3263
3310
|
{
|
|
3264
3311
|
size: "small",
|
|
@@ -3274,10 +3321,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3274
3321
|
transition: theme.transitions.create("transform")
|
|
3275
3322
|
})
|
|
3276
3323
|
},
|
|
3277
|
-
/* @__PURE__ */
|
|
3324
|
+
/* @__PURE__ */ import_react8.default.createElement(import_icons_material5.ExpandMore, null)
|
|
3278
3325
|
)
|
|
3279
|
-
) : /* @__PURE__ */
|
|
3280
|
-
|
|
3326
|
+
) : /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ import_react8.default.createElement(import_Collapse.default, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ import_react8.default.createElement(
|
|
3327
|
+
import_Box8.default,
|
|
3281
3328
|
{
|
|
3282
3329
|
sx: (theme) => ({
|
|
3283
3330
|
mt: 2,
|
|
@@ -3306,14 +3353,14 @@ function budgetStatValue(hidden, budget) {
|
|
|
3306
3353
|
if (budget > 0) return fmtUsd2(budget);
|
|
3307
3354
|
return "Unlimited";
|
|
3308
3355
|
}
|
|
3309
|
-
var
|
|
3356
|
+
var import_react9, import_Paper7, import_Box9, import_Typography9, import_Skeleton4, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton5, import_Button5, import_CircularProgress5, import_styles6, import_icons_material6, import_recharts2, fmtUsd2, TeamCard, TeamUsage;
|
|
3310
3357
|
var init_TeamUsage = __esm({
|
|
3311
3358
|
"src/components/TeamUsage.tsx"() {
|
|
3312
3359
|
"use strict";
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3360
|
+
import_react9 = __toESM(require("react"));
|
|
3361
|
+
import_Paper7 = __toESM(require("@mui/material/Paper"));
|
|
3362
|
+
import_Box9 = __toESM(require("@mui/material/Box"));
|
|
3363
|
+
import_Typography9 = __toESM(require("@mui/material/Typography"));
|
|
3317
3364
|
import_Skeleton4 = __toESM(require("@mui/material/Skeleton"));
|
|
3318
3365
|
import_Divider3 = __toESM(require("@mui/material/Divider"));
|
|
3319
3366
|
import_Table4 = __toESM(require("@mui/material/Table"));
|
|
@@ -3326,13 +3373,13 @@ var init_TeamUsage = __esm({
|
|
|
3326
3373
|
import_IconButton5 = __toESM(require("@mui/material/IconButton"));
|
|
3327
3374
|
import_Button5 = __toESM(require("@mui/material/Button"));
|
|
3328
3375
|
import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
|
|
3329
|
-
|
|
3376
|
+
import_styles6 = require("@mui/material/styles");
|
|
3330
3377
|
import_icons_material6 = require("@mui/icons-material");
|
|
3331
3378
|
import_recharts2 = require("recharts");
|
|
3332
3379
|
init_ui();
|
|
3333
3380
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
3334
3381
|
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
|
|
3335
|
-
const [expanded, setExpanded] = (0,
|
|
3382
|
+
const [expanded, setExpanded] = (0, import_react9.useState)(false);
|
|
3336
3383
|
const chart = useChartTheme();
|
|
3337
3384
|
const hidden = team.budget_hidden === true;
|
|
3338
3385
|
const budget = hidden ? 0 : team.max_budget ?? 0;
|
|
@@ -3344,24 +3391,24 @@ var init_TeamUsage = __esm({
|
|
|
3344
3391
|
const renderDailySpendSection = () => {
|
|
3345
3392
|
if (hidden) return null;
|
|
3346
3393
|
if (usageLoading) {
|
|
3347
|
-
return /* @__PURE__ */
|
|
3394
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react9.default.createElement(import_CircularProgress5.default, { size: 22 }));
|
|
3348
3395
|
}
|
|
3349
3396
|
if (dailyData.length === 0) return null;
|
|
3350
|
-
return /* @__PURE__ */
|
|
3351
|
-
|
|
3397
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3398
|
+
import_Typography9.default,
|
|
3352
3399
|
{
|
|
3353
3400
|
variant: "caption",
|
|
3354
3401
|
color: "text.secondary",
|
|
3355
3402
|
sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
|
|
3356
3403
|
},
|
|
3357
3404
|
"Daily spend"
|
|
3358
|
-
), /* @__PURE__ */
|
|
3405
|
+
), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { height: 150 }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react9.default.createElement(import_recharts2.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react9.default.createElement(import_recharts2.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react9.default.createElement(import_recharts2.YAxis, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ import_react9.default.createElement(
|
|
3359
3406
|
import_recharts2.Tooltip,
|
|
3360
3407
|
{
|
|
3361
3408
|
cursor: chart.cursor,
|
|
3362
|
-
content: /* @__PURE__ */
|
|
3409
|
+
content: /* @__PURE__ */ import_react9.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
3363
3410
|
}
|
|
3364
|
-
), /* @__PURE__ */
|
|
3411
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3365
3412
|
import_recharts2.Area,
|
|
3366
3413
|
{
|
|
3367
3414
|
type: "monotone",
|
|
@@ -3374,8 +3421,8 @@ var init_TeamUsage = __esm({
|
|
|
3374
3421
|
}
|
|
3375
3422
|
)))));
|
|
3376
3423
|
};
|
|
3377
|
-
const sectionLabel = (label) => /* @__PURE__ */
|
|
3378
|
-
|
|
3424
|
+
const sectionLabel = (label) => /* @__PURE__ */ import_react9.default.createElement(
|
|
3425
|
+
import_Typography9.default,
|
|
3379
3426
|
{
|
|
3380
3427
|
variant: "caption",
|
|
3381
3428
|
color: "text.secondary",
|
|
@@ -3383,8 +3430,8 @@ var init_TeamUsage = __esm({
|
|
|
3383
3430
|
},
|
|
3384
3431
|
label
|
|
3385
3432
|
);
|
|
3386
|
-
return /* @__PURE__ */
|
|
3387
|
-
|
|
3433
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Paper7.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3434
|
+
import_Box9.default,
|
|
3388
3435
|
{
|
|
3389
3436
|
sx: (theme) => ({
|
|
3390
3437
|
width: 36,
|
|
@@ -3394,20 +3441,20 @@ var init_TeamUsage = __esm({
|
|
|
3394
3441
|
display: "flex",
|
|
3395
3442
|
alignItems: "center",
|
|
3396
3443
|
justifyContent: "center",
|
|
3397
|
-
bgcolor: (0,
|
|
3444
|
+
bgcolor: (0, import_styles6.alpha)(theme.palette.primary.main, 0.1),
|
|
3398
3445
|
color: theme.palette.primary.main
|
|
3399
3446
|
})
|
|
3400
3447
|
},
|
|
3401
|
-
/* @__PURE__ */
|
|
3402
|
-
), /* @__PURE__ */
|
|
3403
|
-
|
|
3448
|
+
/* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Group, { fontSize: "small" })
|
|
3449
|
+
), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react9.default.createElement(
|
|
3450
|
+
import_Typography9.default,
|
|
3404
3451
|
{
|
|
3405
3452
|
variant: "caption",
|
|
3406
3453
|
color: "text.secondary",
|
|
3407
3454
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3408
3455
|
},
|
|
3409
3456
|
team.team_id
|
|
3410
|
-
)), isOver && /* @__PURE__ */
|
|
3457
|
+
)), isOver && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ import_react9.default.createElement(
|
|
3411
3458
|
import_Button5.default,
|
|
3412
3459
|
{
|
|
3413
3460
|
size: "small",
|
|
@@ -3416,7 +3463,7 @@ var init_TeamUsage = __esm({
|
|
|
3416
3463
|
sx: { textTransform: "none" }
|
|
3417
3464
|
},
|
|
3418
3465
|
"Edit"
|
|
3419
|
-
), /* @__PURE__ */
|
|
3466
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3420
3467
|
import_IconButton5.default,
|
|
3421
3468
|
{
|
|
3422
3469
|
size: "small",
|
|
@@ -3429,9 +3476,9 @@ var init_TeamUsage = __esm({
|
|
|
3429
3476
|
transition: theme.transitions.create("transform")
|
|
3430
3477
|
})
|
|
3431
3478
|
},
|
|
3432
|
-
/* @__PURE__ */
|
|
3433
|
-
)), /* @__PURE__ */
|
|
3434
|
-
|
|
3479
|
+
/* @__PURE__ */ import_react9.default.createElement(import_icons_material6.ExpandMore, null)
|
|
3480
|
+
)), /* @__PURE__ */ import_react9.default.createElement(
|
|
3481
|
+
import_Box9.default,
|
|
3435
3482
|
{
|
|
3436
3483
|
sx: {
|
|
3437
3484
|
display: "grid",
|
|
@@ -3441,35 +3488,35 @@ var init_TeamUsage = __esm({
|
|
|
3441
3488
|
maxWidth: 640
|
|
3442
3489
|
}
|
|
3443
3490
|
},
|
|
3444
|
-
/* @__PURE__ */
|
|
3445
|
-
/* @__PURE__ */
|
|
3446
|
-
/* @__PURE__ */
|
|
3447
|
-
/* @__PURE__ */
|
|
3448
|
-
/* @__PURE__ */
|
|
3449
|
-
/* @__PURE__ */
|
|
3450
|
-
), hidden && /* @__PURE__ */
|
|
3491
|
+
/* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
|
|
3492
|
+
/* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
|
|
3493
|
+
/* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
|
|
3494
|
+
/* @__PURE__ */ import_react9.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
|
|
3495
|
+
/* @__PURE__ */ import_react9.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
3496
|
+
/* @__PURE__ */ import_react9.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
3497
|
+
), hidden && /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ import_react9.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.75 } }, "Team budget is hidden \u2014 showing usage level only", team.budget_duration ? ` \xB7 resets every ${team.budget_duration}` : "", ".")), !hidden && budget > 0 && /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react9.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react9.default.createElement(import_Collapse2.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react9.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react9.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react9.default.createElement(
|
|
3451
3498
|
import_TableContainer4.default,
|
|
3452
3499
|
{
|
|
3453
|
-
component:
|
|
3500
|
+
component: import_Paper7.default,
|
|
3454
3501
|
variant: "outlined",
|
|
3455
3502
|
sx: { borderRadius: 1.5 }
|
|
3456
3503
|
},
|
|
3457
|
-
/* @__PURE__ */
|
|
3458
|
-
|
|
3504
|
+
/* @__PURE__ */ import_react9.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react9.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, null, "User"), /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, { align: "right" }, "Role"))), /* @__PURE__ */ import_react9.default.createElement(import_TableBody4.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react9.default.createElement(import_TableRow4.default, { key: m.user_id }, /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, null, m.user_email ? /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react9.default.createElement(
|
|
3505
|
+
import_Typography9.default,
|
|
3459
3506
|
{
|
|
3460
3507
|
variant: "caption",
|
|
3461
3508
|
color: "text.secondary",
|
|
3462
3509
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3463
3510
|
},
|
|
3464
3511
|
m.user_id
|
|
3465
|
-
)) : /* @__PURE__ */
|
|
3466
|
-
|
|
3512
|
+
)) : /* @__PURE__ */ import_react9.default.createElement(
|
|
3513
|
+
import_Typography9.default,
|
|
3467
3514
|
{
|
|
3468
3515
|
variant: "body2",
|
|
3469
3516
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
3470
3517
|
},
|
|
3471
3518
|
m.user_id
|
|
3472
|
-
)), /* @__PURE__ */
|
|
3519
|
+
)), /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3473
3520
|
StatusPill,
|
|
3474
3521
|
{
|
|
3475
3522
|
label: m.role,
|
|
@@ -3477,7 +3524,7 @@ var init_TeamUsage = __esm({
|
|
|
3477
3524
|
dot: false
|
|
3478
3525
|
}
|
|
3479
3526
|
))))))
|
|
3480
|
-
) : /* @__PURE__ */
|
|
3527
|
+
) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
|
|
3481
3528
|
};
|
|
3482
3529
|
TeamUsage = ({
|
|
3483
3530
|
teams,
|
|
@@ -3489,22 +3536,22 @@ var init_TeamUsage = __esm({
|
|
|
3489
3536
|
canCreate,
|
|
3490
3537
|
onCreateTeam
|
|
3491
3538
|
}) => {
|
|
3492
|
-
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */
|
|
3539
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react9.default.createElement(
|
|
3493
3540
|
import_Button5.default,
|
|
3494
3541
|
{
|
|
3495
3542
|
variant: "contained",
|
|
3496
3543
|
color: "primary",
|
|
3497
3544
|
disableElevation: true,
|
|
3498
|
-
startIcon: /* @__PURE__ */
|
|
3545
|
+
startIcon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Add, null),
|
|
3499
3546
|
onClick: onCreateTeam
|
|
3500
3547
|
},
|
|
3501
3548
|
"Create Team"
|
|
3502
3549
|
) : void 0;
|
|
3503
3550
|
if (loading) {
|
|
3504
|
-
return /* @__PURE__ */
|
|
3551
|
+
return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react9.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
|
|
3505
3552
|
}
|
|
3506
3553
|
if (!teams.length) {
|
|
3507
|
-
return /* @__PURE__ */
|
|
3554
|
+
return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3508
3555
|
EmptyState,
|
|
3509
3556
|
{
|
|
3510
3557
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -3512,14 +3559,14 @@ var init_TeamUsage = __esm({
|
|
|
3512
3559
|
}
|
|
3513
3560
|
));
|
|
3514
3561
|
}
|
|
3515
|
-
return /* @__PURE__ */
|
|
3562
|
+
return /* @__PURE__ */ import_react9.default.createElement(
|
|
3516
3563
|
SectionCard,
|
|
3517
3564
|
{
|
|
3518
3565
|
title: "Teams",
|
|
3519
3566
|
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
3520
3567
|
actions: createAction
|
|
3521
3568
|
},
|
|
3522
|
-
/* @__PURE__ */
|
|
3569
|
+
/* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react9.default.createElement(
|
|
3523
3570
|
TeamCard,
|
|
3524
3571
|
{
|
|
3525
3572
|
key: team.team_id,
|
|
@@ -3553,12 +3600,12 @@ function fmtTokens(n) {
|
|
|
3553
3600
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
3554
3601
|
return String(n);
|
|
3555
3602
|
}
|
|
3556
|
-
var
|
|
3603
|
+
var import_react10, import_Box10, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography10, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles7, ALL_PROXY_MODELS2, ModelsTable;
|
|
3557
3604
|
var init_ModelsTable = __esm({
|
|
3558
3605
|
"src/components/ModelsTable.tsx"() {
|
|
3559
3606
|
"use strict";
|
|
3560
|
-
|
|
3561
|
-
|
|
3607
|
+
import_react10 = __toESM(require("react"));
|
|
3608
|
+
import_Box10 = __toESM(require("@mui/material/Box"));
|
|
3562
3609
|
import_Table5 = __toESM(require("@mui/material/Table"));
|
|
3563
3610
|
import_TableBody5 = __toESM(require("@mui/material/TableBody"));
|
|
3564
3611
|
import_TableCell5 = __toESM(require("@mui/material/TableCell"));
|
|
@@ -3566,7 +3613,7 @@ var init_ModelsTable = __esm({
|
|
|
3566
3613
|
import_TableHead5 = __toESM(require("@mui/material/TableHead"));
|
|
3567
3614
|
import_TableRow5 = __toESM(require("@mui/material/TableRow"));
|
|
3568
3615
|
import_TableSortLabel2 = __toESM(require("@mui/material/TableSortLabel"));
|
|
3569
|
-
|
|
3616
|
+
import_Typography10 = __toESM(require("@mui/material/Typography"));
|
|
3570
3617
|
import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
|
|
3571
3618
|
import_Select = __toESM(require("@mui/material/Select"));
|
|
3572
3619
|
import_FormControl = __toESM(require("@mui/material/FormControl"));
|
|
@@ -3579,24 +3626,24 @@ var init_ModelsTable = __esm({
|
|
|
3579
3626
|
import_Snackbar = __toESM(require("@mui/material/Snackbar"));
|
|
3580
3627
|
import_Alert4 = __toESM(require("@mui/material/Alert"));
|
|
3581
3628
|
init_ui();
|
|
3582
|
-
|
|
3629
|
+
import_styles7 = require("@mui/material/styles");
|
|
3583
3630
|
ALL_PROXY_MODELS2 = "all-proxy-models";
|
|
3584
3631
|
ModelsTable = ({ allModels, teams, loading }) => {
|
|
3585
|
-
const theme = (0,
|
|
3586
|
-
const [selectedTeamId, setSelectedTeamId] = (0,
|
|
3587
|
-
const [filterText, setFilterText] = (0,
|
|
3588
|
-
const [copiedSnackbar, setCopiedSnackbar] = (0,
|
|
3589
|
-
const [sortKey, setSortKey] = (0,
|
|
3590
|
-
const [sortDirection, setSortDirection] = (0,
|
|
3591
|
-
const selectedTeam = (0,
|
|
3632
|
+
const theme = (0, import_styles7.useTheme)();
|
|
3633
|
+
const [selectedTeamId, setSelectedTeamId] = (0, import_react10.useState)("");
|
|
3634
|
+
const [filterText, setFilterText] = (0, import_react10.useState)("");
|
|
3635
|
+
const [copiedSnackbar, setCopiedSnackbar] = (0, import_react10.useState)(false);
|
|
3636
|
+
const [sortKey, setSortKey] = (0, import_react10.useState)("model_name");
|
|
3637
|
+
const [sortDirection, setSortDirection] = (0, import_react10.useState)("asc");
|
|
3638
|
+
const selectedTeam = (0, import_react10.useMemo)(
|
|
3592
3639
|
() => teams.find((t) => t.team_id === selectedTeamId) ?? null,
|
|
3593
3640
|
[teams, selectedTeamId]
|
|
3594
3641
|
);
|
|
3595
|
-
const teamModels = (0,
|
|
3642
|
+
const teamModels = (0, import_react10.useMemo)(() => {
|
|
3596
3643
|
if (!selectedTeam) return [];
|
|
3597
3644
|
return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
|
|
3598
3645
|
}, [allModels, selectedTeam]);
|
|
3599
|
-
const filteredModels = (0,
|
|
3646
|
+
const filteredModels = (0, import_react10.useMemo)(() => {
|
|
3600
3647
|
const q = filterText.trim().toLowerCase();
|
|
3601
3648
|
let models = teamModels;
|
|
3602
3649
|
if (q) {
|
|
@@ -3643,10 +3690,10 @@ var init_ModelsTable = __esm({
|
|
|
3643
3690
|
return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
|
|
3644
3691
|
});
|
|
3645
3692
|
}, [teamModels, filterText, sortKey, sortDirection]);
|
|
3646
|
-
const handleCopyModelId = (0,
|
|
3693
|
+
const handleCopyModelId = (0, import_react10.useCallback)((modelId) => {
|
|
3647
3694
|
navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
|
|
3648
3695
|
}, []);
|
|
3649
|
-
const handleSort = (0,
|
|
3696
|
+
const handleSort = (0, import_react10.useCallback)((key) => {
|
|
3650
3697
|
if (sortKey === key) {
|
|
3651
3698
|
setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
|
|
3652
3699
|
} else {
|
|
@@ -3668,13 +3715,13 @@ var init_ModelsTable = __esm({
|
|
|
3668
3715
|
return theme.palette.info.main;
|
|
3669
3716
|
}
|
|
3670
3717
|
};
|
|
3671
|
-
return /* @__PURE__ */
|
|
3718
|
+
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3672
3719
|
SectionCard,
|
|
3673
3720
|
{
|
|
3674
3721
|
title: "Available Models",
|
|
3675
3722
|
subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
|
|
3676
3723
|
},
|
|
3677
|
-
/* @__PURE__ */
|
|
3724
|
+
/* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ import_react10.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react10.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react10.default.createElement(
|
|
3678
3725
|
import_Select.default,
|
|
3679
3726
|
{
|
|
3680
3727
|
labelId: "team-select-label",
|
|
@@ -3682,8 +3729,8 @@ var init_ModelsTable = __esm({
|
|
|
3682
3729
|
label: "Team",
|
|
3683
3730
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
3684
3731
|
},
|
|
3685
|
-
teams.map((t) => /* @__PURE__ */
|
|
3686
|
-
)), /* @__PURE__ */
|
|
3732
|
+
teams.map((t) => /* @__PURE__ */ import_react10.default.createElement(import_MenuItem4.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
|
|
3733
|
+
)), /* @__PURE__ */ import_react10.default.createElement(
|
|
3687
3734
|
import_TextField5.default,
|
|
3688
3735
|
{
|
|
3689
3736
|
label: "Filter Models",
|
|
@@ -3693,33 +3740,33 @@ var init_ModelsTable = __esm({
|
|
|
3693
3740
|
disabled: !selectedTeamId,
|
|
3694
3741
|
sx: { minWidth: 240, mb: 2 },
|
|
3695
3742
|
InputProps: {
|
|
3696
|
-
startAdornment: /* @__PURE__ */
|
|
3743
|
+
startAdornment: /* @__PURE__ */ import_react10.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react10.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
|
|
3697
3744
|
}
|
|
3698
3745
|
}
|
|
3699
3746
|
)),
|
|
3700
|
-
loading && /* @__PURE__ */
|
|
3701
|
-
!loading && !selectedTeamId && /* @__PURE__ */
|
|
3747
|
+
loading && /* @__PURE__ */ import_react10.default.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
3748
|
+
!loading && !selectedTeamId && /* @__PURE__ */ import_react10.default.createElement(
|
|
3702
3749
|
EmptyState,
|
|
3703
3750
|
{
|
|
3704
3751
|
message: "Select a team to see available models",
|
|
3705
3752
|
hint: "Each team may have a different set of models assigned"
|
|
3706
3753
|
}
|
|
3707
3754
|
),
|
|
3708
|
-
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */
|
|
3755
|
+
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
|
|
3709
3756
|
EmptyState,
|
|
3710
3757
|
{
|
|
3711
3758
|
message: "No models available for this team",
|
|
3712
3759
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
3713
3760
|
}
|
|
3714
3761
|
),
|
|
3715
|
-
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */
|
|
3762
|
+
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
|
|
3716
3763
|
EmptyState,
|
|
3717
3764
|
{
|
|
3718
3765
|
message: "No models match your search",
|
|
3719
3766
|
hint: "Try a different search term"
|
|
3720
3767
|
}
|
|
3721
3768
|
),
|
|
3722
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */
|
|
3769
|
+
!loading && filteredModels.length > 0 && /* @__PURE__ */ import_react10.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react10.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react10.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react10.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react10.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react10.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3723
3770
|
"span",
|
|
3724
3771
|
{
|
|
3725
3772
|
role: "button",
|
|
@@ -3729,9 +3776,9 @@ var init_ModelsTable = __esm({
|
|
|
3729
3776
|
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
3730
3777
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3731
3778
|
},
|
|
3732
|
-
/* @__PURE__ */
|
|
3733
|
-
)), /* @__PURE__ */
|
|
3734
|
-
|
|
3779
|
+
/* @__PURE__ */ import_react10.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
3780
|
+
)), /* @__PURE__ */ import_react10.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3781
|
+
import_Box10.default,
|
|
3735
3782
|
{
|
|
3736
3783
|
component: "span",
|
|
3737
3784
|
role: "button",
|
|
@@ -3741,9 +3788,9 @@ var init_ModelsTable = __esm({
|
|
|
3741
3788
|
sx: quietIconButtonSx("accent"),
|
|
3742
3789
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3743
3790
|
},
|
|
3744
|
-
/* @__PURE__ */
|
|
3745
|
-
)))), /* @__PURE__ */
|
|
3746
|
-
|
|
3791
|
+
/* @__PURE__ */ import_react10.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
|
|
3792
|
+
)))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(
|
|
3793
|
+
import_Box10.default,
|
|
3747
3794
|
{
|
|
3748
3795
|
component: "span",
|
|
3749
3796
|
sx: {
|
|
@@ -3754,13 +3801,13 @@ var init_ModelsTable = __esm({
|
|
|
3754
3801
|
fontSize: 11,
|
|
3755
3802
|
fontWeight: 600,
|
|
3756
3803
|
textTransform: "capitalize",
|
|
3757
|
-
bgcolor: (0,
|
|
3804
|
+
bgcolor: (0, import_styles7.alpha)(modeColor(m.mode), 0.1),
|
|
3758
3805
|
color: modeColor(m.mode)
|
|
3759
3806
|
}
|
|
3760
3807
|
},
|
|
3761
3808
|
m.mode
|
|
3762
|
-
)), /* @__PURE__ */
|
|
3763
|
-
/* @__PURE__ */
|
|
3809
|
+
)), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react10.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react10.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react10.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
|
|
3810
|
+
/* @__PURE__ */ import_react10.default.createElement(
|
|
3764
3811
|
import_Snackbar.default,
|
|
3765
3812
|
{
|
|
3766
3813
|
open: copiedSnackbar,
|
|
@@ -3768,7 +3815,7 @@ var init_ModelsTable = __esm({
|
|
|
3768
3815
|
onClose: () => setCopiedSnackbar(false),
|
|
3769
3816
|
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
3770
3817
|
},
|
|
3771
|
-
/* @__PURE__ */
|
|
3818
|
+
/* @__PURE__ */ import_react10.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
3772
3819
|
)
|
|
3773
3820
|
);
|
|
3774
3821
|
};
|
|
@@ -3796,10 +3843,10 @@ function formatDateTime(iso) {
|
|
|
3796
3843
|
}
|
|
3797
3844
|
function renderAuditLogBody(loading, entries) {
|
|
3798
3845
|
if (loading) {
|
|
3799
|
-
return /* @__PURE__ */
|
|
3846
|
+
return /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react11.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
|
|
3800
3847
|
}
|
|
3801
3848
|
if (entries.length === 0) {
|
|
3802
|
-
return /* @__PURE__ */
|
|
3849
|
+
return /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react11.default.createElement(
|
|
3803
3850
|
EmptyState,
|
|
3804
3851
|
{
|
|
3805
3852
|
message: "No audit events found",
|
|
@@ -3807,15 +3854,15 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3807
3854
|
}
|
|
3808
3855
|
)));
|
|
3809
3856
|
}
|
|
3810
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
3857
|
+
return entries.map((entry) => /* @__PURE__ */ import_react11.default.createElement(DetailRow, { key: entry.id, entry }));
|
|
3811
3858
|
}
|
|
3812
|
-
var
|
|
3859
|
+
var import_react11, import_Box11, import_Typography11, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_TablePagination, import_TextField6, import_MenuItem5, import_Skeleton5, import_Collapse3, import_IconButton6, import_Alert5, import_styles8, import_icons_material8, import_react_use2, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
3813
3860
|
var init_AuditLog = __esm({
|
|
3814
3861
|
"src/components/AuditLog.tsx"() {
|
|
3815
3862
|
"use strict";
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3863
|
+
import_react11 = __toESM(require("react"));
|
|
3864
|
+
import_Box11 = __toESM(require("@mui/material/Box"));
|
|
3865
|
+
import_Typography11 = __toESM(require("@mui/material/Typography"));
|
|
3819
3866
|
import_Table6 = __toESM(require("@mui/material/Table"));
|
|
3820
3867
|
import_TableBody6 = __toESM(require("@mui/material/TableBody"));
|
|
3821
3868
|
import_TableCell6 = __toESM(require("@mui/material/TableCell"));
|
|
@@ -3829,7 +3876,7 @@ var init_AuditLog = __esm({
|
|
|
3829
3876
|
import_Collapse3 = __toESM(require("@mui/material/Collapse"));
|
|
3830
3877
|
import_IconButton6 = __toESM(require("@mui/material/IconButton"));
|
|
3831
3878
|
import_Alert5 = __toESM(require("@mui/material/Alert"));
|
|
3832
|
-
|
|
3879
|
+
import_styles8 = require("@mui/material/styles");
|
|
3833
3880
|
import_icons_material8 = require("@mui/icons-material");
|
|
3834
3881
|
import_react_use2 = require("react-use");
|
|
3835
3882
|
init_ui();
|
|
@@ -3848,9 +3895,9 @@ var init_AuditLog = __esm({
|
|
|
3848
3895
|
LiteLLM_UserTable: "User"
|
|
3849
3896
|
};
|
|
3850
3897
|
DetailRow = ({ entry }) => {
|
|
3851
|
-
const [open, setOpen] = (0,
|
|
3898
|
+
const [open, setOpen] = (0, import_react11.useState)(false);
|
|
3852
3899
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3853
|
-
return /* @__PURE__ */
|
|
3900
|
+
return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react11.default.createElement(
|
|
3854
3901
|
import_IconButton6.default,
|
|
3855
3902
|
{
|
|
3856
3903
|
size: "small",
|
|
@@ -3863,9 +3910,9 @@ var init_AuditLog = __esm({
|
|
|
3863
3910
|
transition: theme.transitions.create("transform")
|
|
3864
3911
|
})
|
|
3865
3912
|
},
|
|
3866
|
-
/* @__PURE__ */
|
|
3867
|
-
)), /* @__PURE__ */
|
|
3868
|
-
|
|
3913
|
+
/* @__PURE__ */ import_react11.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
|
|
3914
|
+
)), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, entry.action && /* @__PURE__ */ import_react11.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react11.default.createElement(
|
|
3915
|
+
import_Typography11.default,
|
|
3869
3916
|
{
|
|
3870
3917
|
variant: "body2",
|
|
3871
3918
|
component: "code",
|
|
@@ -3874,8 +3921,8 @@ var init_AuditLog = __esm({
|
|
|
3874
3921
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3875
3922
|
},
|
|
3876
3923
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3877
|
-
)), /* @__PURE__ */
|
|
3878
|
-
|
|
3924
|
+
)), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react11.default.createElement(import_Collapse3.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react11.default.createElement(
|
|
3925
|
+
import_Box11.default,
|
|
3879
3926
|
{
|
|
3880
3927
|
display: "flex",
|
|
3881
3928
|
gap: 2,
|
|
@@ -3884,11 +3931,11 @@ var init_AuditLog = __esm({
|
|
|
3884
3931
|
p: 2,
|
|
3885
3932
|
mb: 1.5,
|
|
3886
3933
|
borderRadius: 1.5,
|
|
3887
|
-
bgcolor: (0,
|
|
3934
|
+
bgcolor: (0, import_styles8.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
3888
3935
|
})
|
|
3889
3936
|
},
|
|
3890
|
-
entry.before_value && /* @__PURE__ */
|
|
3891
|
-
|
|
3937
|
+
entry.before_value && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react11.default.createElement(
|
|
3938
|
+
import_Typography11.default,
|
|
3892
3939
|
{
|
|
3893
3940
|
variant: "caption",
|
|
3894
3941
|
color: "text.secondary",
|
|
@@ -3897,9 +3944,9 @@ var init_AuditLog = __esm({
|
|
|
3897
3944
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3898
3945
|
},
|
|
3899
3946
|
"Before"
|
|
3900
|
-
), /* @__PURE__ */
|
|
3901
|
-
entry.updated_values && /* @__PURE__ */
|
|
3902
|
-
|
|
3947
|
+
), /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
|
|
3948
|
+
entry.updated_values && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react11.default.createElement(
|
|
3949
|
+
import_Typography11.default,
|
|
3903
3950
|
{
|
|
3904
3951
|
variant: "caption",
|
|
3905
3952
|
color: "text.secondary",
|
|
@@ -3908,14 +3955,14 @@ var init_AuditLog = __esm({
|
|
|
3908
3955
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3909
3956
|
},
|
|
3910
3957
|
"After"
|
|
3911
|
-
), /* @__PURE__ */
|
|
3958
|
+
), /* @__PURE__ */ import_react11.default.createElement(import_Typography11.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
|
|
3912
3959
|
)))));
|
|
3913
3960
|
};
|
|
3914
3961
|
AuditLog = ({ api }) => {
|
|
3915
|
-
const [page, setPage] = (0,
|
|
3916
|
-
const [pageSize, setPageSize] = (0,
|
|
3917
|
-
const [filters, setFilters] = (0,
|
|
3918
|
-
const fetchParams = (0,
|
|
3962
|
+
const [page, setPage] = (0, import_react11.useState)(0);
|
|
3963
|
+
const [pageSize, setPageSize] = (0, import_react11.useState)(25);
|
|
3964
|
+
const [filters, setFilters] = (0, import_react11.useState)({});
|
|
3965
|
+
const fetchParams = (0, import_react11.useMemo)(
|
|
3919
3966
|
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
3920
3967
|
[page, pageSize, filters]
|
|
3921
3968
|
);
|
|
@@ -3925,13 +3972,13 @@ var init_AuditLog = __esm({
|
|
|
3925
3972
|
);
|
|
3926
3973
|
const entries = value?.audit_logs ?? [];
|
|
3927
3974
|
const total = value?.total ?? 0;
|
|
3928
|
-
return /* @__PURE__ */
|
|
3975
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
3929
3976
|
SectionCard,
|
|
3930
3977
|
{
|
|
3931
3978
|
title: "Audit Log",
|
|
3932
3979
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
3933
3980
|
flush: true,
|
|
3934
|
-
actions: /* @__PURE__ */
|
|
3981
|
+
actions: /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(
|
|
3935
3982
|
import_TextField6.default,
|
|
3936
3983
|
{
|
|
3937
3984
|
size: "small",
|
|
@@ -3944,12 +3991,12 @@ var init_AuditLog = __esm({
|
|
|
3944
3991
|
},
|
|
3945
3992
|
sx: { minWidth: 150 }
|
|
3946
3993
|
},
|
|
3947
|
-
/* @__PURE__ */
|
|
3948
|
-
/* @__PURE__ */
|
|
3949
|
-
/* @__PURE__ */
|
|
3950
|
-
/* @__PURE__ */
|
|
3951
|
-
/* @__PURE__ */
|
|
3952
|
-
), /* @__PURE__ */
|
|
3994
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
|
|
3995
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
|
|
3996
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
|
|
3997
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
|
|
3998
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
|
|
3999
|
+
), /* @__PURE__ */ import_react11.default.createElement(
|
|
3953
4000
|
import_TextField6.default,
|
|
3954
4001
|
{
|
|
3955
4002
|
size: "small",
|
|
@@ -3962,13 +4009,13 @@ var init_AuditLog = __esm({
|
|
|
3962
4009
|
},
|
|
3963
4010
|
sx: { minWidth: 150 }
|
|
3964
4011
|
},
|
|
3965
|
-
/* @__PURE__ */
|
|
3966
|
-
/* @__PURE__ */
|
|
3967
|
-
/* @__PURE__ */
|
|
3968
|
-
/* @__PURE__ */
|
|
3969
|
-
/* @__PURE__ */
|
|
3970
|
-
/* @__PURE__ */
|
|
3971
|
-
), /* @__PURE__ */
|
|
4012
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
|
|
4013
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
4014
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
4015
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
|
|
4016
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
|
|
4017
|
+
/* @__PURE__ */ import_react11.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
|
|
4018
|
+
), /* @__PURE__ */ import_react11.default.createElement(
|
|
3972
4019
|
import_TextField6.default,
|
|
3973
4020
|
{
|
|
3974
4021
|
size: "small",
|
|
@@ -3982,9 +4029,9 @@ var init_AuditLog = __esm({
|
|
|
3982
4029
|
}
|
|
3983
4030
|
))
|
|
3984
4031
|
},
|
|
3985
|
-
error && /* @__PURE__ */
|
|
3986
|
-
/* @__PURE__ */
|
|
3987
|
-
/* @__PURE__ */
|
|
4032
|
+
error && /* @__PURE__ */ import_react11.default.createElement(import_Box11.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react11.default.createElement(import_Alert5.default, { severity: "error" }, error.message)),
|
|
4033
|
+
/* @__PURE__ */ import_react11.default.createElement(import_TableContainer6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Table6.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react11.default.createElement(import_TableHead6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Time"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Action"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Table"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Object ID"), /* @__PURE__ */ import_react11.default.createElement(import_TableCell6.default, null, "Changed By"))), /* @__PURE__ */ import_react11.default.createElement(import_TableBody6.default, null, renderAuditLogBody(loading, entries)))),
|
|
4034
|
+
/* @__PURE__ */ import_react11.default.createElement(
|
|
3988
4035
|
import_TablePagination.default,
|
|
3989
4036
|
{
|
|
3990
4037
|
component: "div",
|
|
@@ -4053,17 +4100,17 @@ function initDateRange() {
|
|
|
4053
4100
|
else start.setDate(start.getDate() - 7);
|
|
4054
4101
|
return { start, end };
|
|
4055
4102
|
}
|
|
4056
|
-
var
|
|
4103
|
+
var import_react12, import_Box12, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography12, import_Paper8, import_Tabs2, import_Tab2, import_Grid2, import_react_router_dom, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
|
|
4057
4104
|
var init_LiteLLMPage = __esm({
|
|
4058
4105
|
"src/components/LiteLLMPage.tsx"() {
|
|
4059
4106
|
"use strict";
|
|
4060
|
-
|
|
4061
|
-
|
|
4107
|
+
import_react12 = __toESM(require("react"));
|
|
4108
|
+
import_Box12 = __toESM(require("@mui/material/Box"));
|
|
4062
4109
|
import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
|
|
4063
4110
|
import_Alert6 = __toESM(require("@mui/material/Alert"));
|
|
4064
4111
|
import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
|
|
4065
|
-
|
|
4066
|
-
|
|
4112
|
+
import_Typography12 = __toESM(require("@mui/material/Typography"));
|
|
4113
|
+
import_Paper8 = __toESM(require("@mui/material/Paper"));
|
|
4067
4114
|
import_Tabs2 = __toESM(require("@mui/material/Tabs"));
|
|
4068
4115
|
import_Tab2 = __toESM(require("@mui/material/Tab"));
|
|
4069
4116
|
import_Grid2 = __toESM(require("@mui/material/Grid"));
|
|
@@ -4087,8 +4134,8 @@ var init_LiteLLMPage = __esm({
|
|
|
4087
4134
|
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
4088
4135
|
LiteLLMPage = () => {
|
|
4089
4136
|
const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
|
|
4090
|
-
const [dateRange, setDateRange] = (0,
|
|
4091
|
-
const [currentPreset, setCurrentPreset] = (0,
|
|
4137
|
+
const [dateRange, setDateRange] = (0, import_react12.useState)(initDateRange);
|
|
4138
|
+
const [currentPreset, setCurrentPreset] = (0, import_react12.useState)(() => {
|
|
4092
4139
|
try {
|
|
4093
4140
|
return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
|
|
4094
4141
|
} catch {
|
|
@@ -4096,11 +4143,26 @@ var init_LiteLLMPage = __esm({
|
|
|
4096
4143
|
}
|
|
4097
4144
|
});
|
|
4098
4145
|
const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
|
|
4099
|
-
const [activeTab, setActiveTab] = (0,
|
|
4146
|
+
const [activeTab, setActiveTab] = (0, import_react12.useState)(() => {
|
|
4100
4147
|
const t = searchParams.get("tab");
|
|
4101
4148
|
return isPageTab(t) ? t : "overview";
|
|
4102
4149
|
});
|
|
4103
|
-
const
|
|
4150
|
+
const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react12.useState)(
|
|
4151
|
+
() => searchParams.get("generate") === "1"
|
|
4152
|
+
);
|
|
4153
|
+
(0, import_react12.useEffect)(() => {
|
|
4154
|
+
if (searchParams.get("generate") !== "1") return;
|
|
4155
|
+
setGenerateDialogOpen(true);
|
|
4156
|
+
setSearchParams(
|
|
4157
|
+
(prev) => {
|
|
4158
|
+
const next = new URLSearchParams(prev);
|
|
4159
|
+
next.delete("generate");
|
|
4160
|
+
return next;
|
|
4161
|
+
},
|
|
4162
|
+
{ replace: true }
|
|
4163
|
+
);
|
|
4164
|
+
}, [searchParams, setSearchParams]);
|
|
4165
|
+
const selectTab = (0, import_react12.useCallback)(
|
|
4104
4166
|
(tab) => {
|
|
4105
4167
|
setActiveTab(tab);
|
|
4106
4168
|
setSearchParams(
|
|
@@ -4114,11 +4176,10 @@ var init_LiteLLMPage = __esm({
|
|
|
4114
4176
|
},
|
|
4115
4177
|
[setSearchParams]
|
|
4116
4178
|
);
|
|
4117
|
-
const [snackbar, setSnackbar] = (0,
|
|
4118
|
-
const [
|
|
4119
|
-
const [
|
|
4120
|
-
const [
|
|
4121
|
-
const [teamUsageLoading, setTeamUsageLoading] = (0, import_react11.useState)({});
|
|
4179
|
+
const [snackbar, setSnackbar] = (0, import_react12.useState)(null);
|
|
4180
|
+
const [manageTeam, setManageTeam] = (0, import_react12.useState)(null);
|
|
4181
|
+
const [teamUsageCache, setTeamUsageCache] = (0, import_react12.useState)({});
|
|
4182
|
+
const [teamUsageLoading, setTeamUsageLoading] = (0, import_react12.useState)({});
|
|
4122
4183
|
const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
|
|
4123
4184
|
() => api.getUserInfo(),
|
|
4124
4185
|
[api]
|
|
@@ -4162,7 +4223,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4162
4223
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
4163
4224
|
[api, objectPermsEnabled]
|
|
4164
4225
|
);
|
|
4165
|
-
const teams = (0,
|
|
4226
|
+
const teams = (0, import_react12.useMemo)(() => {
|
|
4166
4227
|
if (!allTeams?.length) return [];
|
|
4167
4228
|
if (!userInfo) return allTeams;
|
|
4168
4229
|
const userId = userInfo.user_id;
|
|
@@ -4179,7 +4240,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4179
4240
|
const endDate = dateRange.end.toISOString().split("T")[0];
|
|
4180
4241
|
return api.getUsage(startDate, endDate);
|
|
4181
4242
|
}, [api, dateRange]);
|
|
4182
|
-
const handleDateRangeChange = (0,
|
|
4243
|
+
const handleDateRangeChange = (0, import_react12.useCallback)((range, preset) => {
|
|
4183
4244
|
setDateRange(range);
|
|
4184
4245
|
if (preset) {
|
|
4185
4246
|
setCurrentPreset(preset);
|
|
@@ -4187,7 +4248,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4187
4248
|
setTeamUsageCache({});
|
|
4188
4249
|
setTeamUsageLoading({});
|
|
4189
4250
|
}, [setDateRange, setCurrentPreset]);
|
|
4190
|
-
const loadTeamUsage = (0,
|
|
4251
|
+
const loadTeamUsage = (0, import_react12.useCallback)(async (teamId) => {
|
|
4191
4252
|
if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
|
|
4192
4253
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
|
|
4193
4254
|
try {
|
|
@@ -4201,7 +4262,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4201
4262
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
|
|
4202
4263
|
}
|
|
4203
4264
|
}, [api, dateRange, teamUsageCache, teamUsageLoading]);
|
|
4204
|
-
const allowedModels = (0,
|
|
4265
|
+
const allowedModels = (0, import_react12.useMemo)(() => {
|
|
4205
4266
|
if (!allModels?.length) return [];
|
|
4206
4267
|
const userModels = userInfo?.models;
|
|
4207
4268
|
const teamModels = teams?.flatMap((t) => t.models ?? []);
|
|
@@ -4214,7 +4275,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4214
4275
|
]);
|
|
4215
4276
|
return allModels.filter((m) => allowed.has(m.model_name));
|
|
4216
4277
|
}, [allModels, userInfo, teams]);
|
|
4217
|
-
const handleGenerateKey = (0,
|
|
4278
|
+
const handleGenerateKey = (0, import_react12.useCallback)(
|
|
4218
4279
|
async (request) => {
|
|
4219
4280
|
try {
|
|
4220
4281
|
const response = await api.generateKey(request);
|
|
@@ -4228,7 +4289,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4228
4289
|
},
|
|
4229
4290
|
[api, refreshKeys]
|
|
4230
4291
|
);
|
|
4231
|
-
const handleUpdateKey = (0,
|
|
4292
|
+
const handleUpdateKey = (0, import_react12.useCallback)(
|
|
4232
4293
|
async (keyId, request) => {
|
|
4233
4294
|
try {
|
|
4234
4295
|
await api.updateKey(keyId, request);
|
|
@@ -4241,7 +4302,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4241
4302
|
},
|
|
4242
4303
|
[api, refreshKeys]
|
|
4243
4304
|
);
|
|
4244
|
-
const handleBlockKey = (0,
|
|
4305
|
+
const handleBlockKey = (0, import_react12.useCallback)(
|
|
4245
4306
|
async (keyId) => {
|
|
4246
4307
|
try {
|
|
4247
4308
|
await api.blockKey(keyId);
|
|
@@ -4253,7 +4314,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4253
4314
|
},
|
|
4254
4315
|
[api, refreshKeys]
|
|
4255
4316
|
);
|
|
4256
|
-
const handleUnblockKey = (0,
|
|
4317
|
+
const handleUnblockKey = (0, import_react12.useCallback)(
|
|
4257
4318
|
async (keyId) => {
|
|
4258
4319
|
try {
|
|
4259
4320
|
await api.unblockKey(keyId);
|
|
@@ -4265,7 +4326,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4265
4326
|
},
|
|
4266
4327
|
[api, refreshKeys]
|
|
4267
4328
|
);
|
|
4268
|
-
const handleResetKeySpend = (0,
|
|
4329
|
+
const handleResetKeySpend = (0, import_react12.useCallback)(
|
|
4269
4330
|
async (keyId) => {
|
|
4270
4331
|
try {
|
|
4271
4332
|
await api.resetKeySpend(keyId);
|
|
@@ -4277,7 +4338,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4277
4338
|
},
|
|
4278
4339
|
[api, refreshKeys]
|
|
4279
4340
|
);
|
|
4280
|
-
const handleDeleteKey = (0,
|
|
4341
|
+
const handleDeleteKey = (0, import_react12.useCallback)(
|
|
4281
4342
|
async (keyId) => {
|
|
4282
4343
|
try {
|
|
4283
4344
|
await api.deleteKey(keyId);
|
|
@@ -4294,7 +4355,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4294
4355
|
},
|
|
4295
4356
|
[api, refreshKeys]
|
|
4296
4357
|
);
|
|
4297
|
-
const handlePruneExpiredKeys = (0,
|
|
4358
|
+
const handlePruneExpiredKeys = (0, import_react12.useCallback)(
|
|
4298
4359
|
async () => {
|
|
4299
4360
|
try {
|
|
4300
4361
|
const result = await api.pruneExpiredKeys();
|
|
@@ -4309,14 +4370,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4309
4370
|
);
|
|
4310
4371
|
const isInitialLoading = userLoading && !userInfo;
|
|
4311
4372
|
if (isInitialLoading) {
|
|
4312
|
-
return /* @__PURE__ */
|
|
4373
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react12.default.createElement(import_CircularProgress6.default, null));
|
|
4313
4374
|
}
|
|
4314
4375
|
if (userError || !userInfo) {
|
|
4315
4376
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
4316
4377
|
const hint = userError?.body?.hint;
|
|
4317
|
-
return /* @__PURE__ */
|
|
4378
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { p: 3 }, /* @__PURE__ */ import_react12.default.createElement(import_Paper8.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react12.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
|
|
4318
4379
|
}
|
|
4319
|
-
const pageTabs = /* @__PURE__ */
|
|
4380
|
+
const pageTabs = /* @__PURE__ */ import_react12.default.createElement(
|
|
4320
4381
|
import_Tabs2.default,
|
|
4321
4382
|
{
|
|
4322
4383
|
value: activeTab,
|
|
@@ -4329,13 +4390,13 @@ var init_LiteLLMPage = __esm({
|
|
|
4329
4390
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
4330
4391
|
}
|
|
4331
4392
|
},
|
|
4332
|
-
/* @__PURE__ */
|
|
4333
|
-
/* @__PURE__ */
|
|
4334
|
-
/* @__PURE__ */
|
|
4335
|
-
/* @__PURE__ */
|
|
4336
|
-
userInfo.can_view_audit && /* @__PURE__ */
|
|
4393
|
+
/* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
|
|
4394
|
+
/* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
|
|
4395
|
+
/* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
|
|
4396
|
+
/* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
|
|
4397
|
+
userInfo.can_view_audit && /* @__PURE__ */ import_react12.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
|
|
4337
4398
|
);
|
|
4338
|
-
return /* @__PURE__ */
|
|
4399
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_Box12.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react12.default.createElement(
|
|
4339
4400
|
DashboardHeader,
|
|
4340
4401
|
{
|
|
4341
4402
|
userInfo,
|
|
@@ -4345,7 +4406,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4345
4406
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4346
4407
|
tabs: pageTabs
|
|
4347
4408
|
}
|
|
4348
|
-
), activeTab === "overview" && /* @__PURE__ */
|
|
4409
|
+
), activeTab === "overview" && /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
4349
4410
|
UsageStats,
|
|
4350
4411
|
{
|
|
4351
4412
|
usage: usage ?? null,
|
|
@@ -4356,7 +4417,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4356
4417
|
loading: usageLoading,
|
|
4357
4418
|
userInfo
|
|
4358
4419
|
}
|
|
4359
|
-
)), /* @__PURE__ */
|
|
4420
|
+
)), /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ import_react12.default.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ import_react12.default.createElement(
|
|
4360
4421
|
KeysTable,
|
|
4361
4422
|
{
|
|
4362
4423
|
keys: keys ?? [],
|
|
@@ -4375,7 +4436,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4375
4436
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
4376
4437
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
4377
4438
|
const visibleTeams = Array.from(teamsById.values());
|
|
4378
|
-
return /* @__PURE__ */
|
|
4439
|
+
return /* @__PURE__ */ import_react12.default.createElement(
|
|
4379
4440
|
TeamUsage,
|
|
4380
4441
|
{
|
|
4381
4442
|
teams: visibleTeams,
|
|
@@ -4391,14 +4452,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4391
4452
|
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
4392
4453
|
}
|
|
4393
4454
|
);
|
|
4394
|
-
})(), activeTab === "models" && /* @__PURE__ */
|
|
4455
|
+
})(), activeTab === "models" && /* @__PURE__ */ import_react12.default.createElement(
|
|
4395
4456
|
ModelsTable,
|
|
4396
4457
|
{
|
|
4397
4458
|
allModels: allModels ?? [],
|
|
4398
4459
|
teams: teams ?? [],
|
|
4399
4460
|
loading: modelsLoading
|
|
4400
4461
|
}
|
|
4401
|
-
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */
|
|
4462
|
+
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react12.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react12.default.createElement(
|
|
4402
4463
|
GenerateKeyDialog,
|
|
4403
4464
|
{
|
|
4404
4465
|
open: generateDialogOpen,
|
|
@@ -4411,7 +4472,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4411
4472
|
onGenerateKey: handleGenerateKey,
|
|
4412
4473
|
onGetConfig: () => api.getConfig()
|
|
4413
4474
|
}
|
|
4414
|
-
), /* @__PURE__ */
|
|
4475
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
4415
4476
|
ManageTeamDialog,
|
|
4416
4477
|
{
|
|
4417
4478
|
open: !!manageTeam,
|
|
@@ -4473,7 +4534,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4473
4534
|
refreshTeams();
|
|
4474
4535
|
}
|
|
4475
4536
|
}
|
|
4476
|
-
), /* @__PURE__ */
|
|
4537
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
4477
4538
|
import_Snackbar2.default,
|
|
4478
4539
|
{
|
|
4479
4540
|
open: !!snackbar,
|
|
@@ -4481,7 +4542,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4481
4542
|
onClose: () => setSnackbar(null),
|
|
4482
4543
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
4483
4544
|
},
|
|
4484
|
-
snackbar ? /* @__PURE__ */
|
|
4545
|
+
snackbar ? /* @__PURE__ */ import_react12.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
4485
4546
|
));
|
|
4486
4547
|
};
|
|
4487
4548
|
}
|
|
@@ -4512,7 +4573,7 @@ __export(index_exports, {
|
|
|
4512
4573
|
module.exports = __toCommonJS(index_exports);
|
|
4513
4574
|
|
|
4514
4575
|
// src/plugin.tsx
|
|
4515
|
-
var
|
|
4576
|
+
var import_react13 = __toESM(require("react"));
|
|
4516
4577
|
var import_icons_material9 = require("@mui/icons-material");
|
|
4517
4578
|
var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
|
|
4518
4579
|
init_api();
|
|
@@ -4527,10 +4588,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
|
|
|
4527
4588
|
params: {
|
|
4528
4589
|
path: "/litellm",
|
|
4529
4590
|
title: "LiteLLM",
|
|
4530
|
-
icon: /* @__PURE__ */
|
|
4591
|
+
icon: /* @__PURE__ */ import_react13.default.createElement(import_icons_material9.TrendingUp, null),
|
|
4531
4592
|
loader: async () => {
|
|
4532
4593
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
4533
|
-
return /* @__PURE__ */
|
|
4594
|
+
return /* @__PURE__ */ import_react13.default.createElement(LiteLLMPage2, null);
|
|
4534
4595
|
}
|
|
4535
4596
|
}
|
|
4536
4597
|
});
|
|
@@ -4547,10 +4608,10 @@ init_UsageStats();
|
|
|
4547
4608
|
init_TeamUsage();
|
|
4548
4609
|
|
|
4549
4610
|
// src/components/LiteLLMHomeWidget.tsx
|
|
4550
|
-
var
|
|
4551
|
-
var
|
|
4552
|
-
var
|
|
4553
|
-
var
|
|
4611
|
+
var import_react14 = __toESM(require("react"));
|
|
4612
|
+
var import_Paper9 = __toESM(require("@mui/material/Paper"));
|
|
4613
|
+
var import_Box13 = __toESM(require("@mui/material/Box"));
|
|
4614
|
+
var import_Typography13 = __toESM(require("@mui/material/Typography"));
|
|
4554
4615
|
var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
|
|
4555
4616
|
var import_Select2 = __toESM(require("@mui/material/Select"));
|
|
4556
4617
|
var import_MenuItem6 = __toESM(require("@mui/material/MenuItem"));
|
|
@@ -4573,19 +4634,19 @@ function presetToDateRange(preset) {
|
|
|
4573
4634
|
}
|
|
4574
4635
|
return { start, end };
|
|
4575
4636
|
}
|
|
4576
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
4637
|
+
var Kpi = ({ label, value }) => /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, null, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "subtitle1", fontWeight: 600 }, value));
|
|
4577
4638
|
var LiteLLMHomeWidget = ({
|
|
4578
4639
|
defaultPeriod = "7d",
|
|
4579
4640
|
title = "LiteLLM Usage"
|
|
4580
4641
|
}) => {
|
|
4581
4642
|
const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
|
|
4582
|
-
const [period, setPeriod] = (0,
|
|
4583
|
-
const [loading, setLoading] = (0,
|
|
4584
|
-
const [usageError, setUsageError] = (0,
|
|
4585
|
-
const [keysError, setKeysError] = (0,
|
|
4586
|
-
const [usage, setUsage] = (0,
|
|
4587
|
-
const [keys, setKeys] = (0,
|
|
4588
|
-
(0,
|
|
4643
|
+
const [period, setPeriod] = (0, import_react14.useState)(defaultPeriod);
|
|
4644
|
+
const [loading, setLoading] = (0, import_react14.useState)(true);
|
|
4645
|
+
const [usageError, setUsageError] = (0, import_react14.useState)(null);
|
|
4646
|
+
const [keysError, setKeysError] = (0, import_react14.useState)(null);
|
|
4647
|
+
const [usage, setUsage] = (0, import_react14.useState)(null);
|
|
4648
|
+
const [keys, setKeys] = (0, import_react14.useState)([]);
|
|
4649
|
+
(0, import_react14.useEffect)(() => {
|
|
4589
4650
|
let cancelled = false;
|
|
4590
4651
|
setLoading(true);
|
|
4591
4652
|
setUsageError(null);
|
|
@@ -4622,17 +4683,17 @@ var LiteLLMHomeWidget = ({
|
|
|
4622
4683
|
spend: d.spend
|
|
4623
4684
|
}));
|
|
4624
4685
|
const hasSparkline = dailyData.length > 0;
|
|
4625
|
-
return /* @__PURE__ */
|
|
4686
|
+
return /* @__PURE__ */ import_react14.default.createElement(import_Paper9.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react14.default.createElement(import_Typography13.default, { variant: "h6" }, title), /* @__PURE__ */ import_react14.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react14.default.createElement(
|
|
4626
4687
|
import_Select2.default,
|
|
4627
4688
|
{
|
|
4628
4689
|
value: period,
|
|
4629
4690
|
onChange: (e) => setPeriod(e.target.value),
|
|
4630
4691
|
displayEmpty: true
|
|
4631
4692
|
},
|
|
4632
|
-
/* @__PURE__ */
|
|
4633
|
-
/* @__PURE__ */
|
|
4634
|
-
/* @__PURE__ */
|
|
4635
|
-
))), loading && /* @__PURE__ */
|
|
4693
|
+
/* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
|
|
4694
|
+
/* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
|
|
4695
|
+
/* @__PURE__ */ import_react14.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
|
|
4696
|
+
))), loading && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react14.default.createElement(import_CircularProgress7.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react14.default.createElement(import_Alert7.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react14.default.createElement(import_Alert7.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react14.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react14.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react14.default.createElement(import_Box13.default, { height: 120 }, /* @__PURE__ */ import_react14.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react14.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react14.default.createElement(
|
|
4636
4697
|
import_recharts3.Area,
|
|
4637
4698
|
{
|
|
4638
4699
|
type: "monotone",
|
|
@@ -4650,24 +4711,34 @@ var LiteLLMHomeWidget = ({
|
|
|
4650
4711
|
init_LiteLLMBudgetWidget();
|
|
4651
4712
|
|
|
4652
4713
|
// src/components/LiteLLMBudgetGauges.tsx
|
|
4653
|
-
var
|
|
4654
|
-
var
|
|
4655
|
-
var
|
|
4656
|
-
var
|
|
4714
|
+
var import_react15 = __toESM(require("react"));
|
|
4715
|
+
var import_Paper10 = __toESM(require("@mui/material/Paper"));
|
|
4716
|
+
var import_Box14 = __toESM(require("@mui/material/Box"));
|
|
4717
|
+
var import_Button6 = __toESM(require("@mui/material/Button"));
|
|
4718
|
+
var import_Typography14 = __toESM(require("@mui/material/Typography"));
|
|
4657
4719
|
var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress"));
|
|
4658
4720
|
var import_Alert8 = __toESM(require("@mui/material/Alert"));
|
|
4721
|
+
var import_Collapse4 = __toESM(require("@mui/material/Collapse"));
|
|
4722
|
+
var import_icons_material10 = require("@mui/icons-material");
|
|
4659
4723
|
var import_core_plugin_api6 = require("@backstage/core-plugin-api");
|
|
4660
4724
|
var import_core_components2 = require("@backstage/core-components");
|
|
4661
4725
|
init_api();
|
|
4662
4726
|
init_format();
|
|
4663
4727
|
init_ui();
|
|
4728
|
+
init_BudgetLimitList();
|
|
4664
4729
|
init_budget();
|
|
4665
|
-
var
|
|
4730
|
+
var MODULE_PATH = "/litellm";
|
|
4666
4731
|
var LEVEL = {
|
|
4667
4732
|
key: { name: "Key", none: "No key budget" },
|
|
4668
4733
|
user: { name: "User", none: "No personal budget" },
|
|
4669
4734
|
team: { name: "Team", none: "No team budget" }
|
|
4670
4735
|
};
|
|
4736
|
+
var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
|
|
4737
|
+
var CTA_LABELS = {
|
|
4738
|
+
"new-key": "New key",
|
|
4739
|
+
module: "Open module",
|
|
4740
|
+
"all-limits": "All limits"
|
|
4741
|
+
};
|
|
4671
4742
|
function fmtUsdShort(n) {
|
|
4672
4743
|
const v = n ?? 0;
|
|
4673
4744
|
if (v >= 100 && Number.isInteger(v)) return `$${v}`;
|
|
@@ -4690,8 +4761,8 @@ var LevelGauge = ({
|
|
|
4690
4761
|
const limit = gauge.limit;
|
|
4691
4762
|
const tone = limit ? budgetTone2(limit.pct) : "neutral";
|
|
4692
4763
|
const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
|
|
4693
|
-
return /* @__PURE__ */
|
|
4694
|
-
|
|
4764
|
+
return /* @__PURE__ */ import_react15.default.createElement(
|
|
4765
|
+
import_Box14.default,
|
|
4695
4766
|
{
|
|
4696
4767
|
sx: {
|
|
4697
4768
|
display: "flex",
|
|
@@ -4701,7 +4772,7 @@ var LevelGauge = ({
|
|
|
4701
4772
|
flex: 1
|
|
4702
4773
|
}
|
|
4703
4774
|
},
|
|
4704
|
-
/* @__PURE__ */
|
|
4775
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
4705
4776
|
Gauge,
|
|
4706
4777
|
{
|
|
4707
4778
|
value: limit?.pct ?? 0,
|
|
@@ -4710,8 +4781,8 @@ var LevelGauge = ({
|
|
|
4710
4781
|
label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
|
|
4711
4782
|
}
|
|
4712
4783
|
),
|
|
4713
|
-
/* @__PURE__ */
|
|
4714
|
-
|
|
4784
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
4785
|
+
import_Typography14.default,
|
|
4715
4786
|
{
|
|
4716
4787
|
sx: (theme) => ({
|
|
4717
4788
|
mt: 0.75,
|
|
@@ -4724,8 +4795,8 @@ var LevelGauge = ({
|
|
|
4724
4795
|
},
|
|
4725
4796
|
name
|
|
4726
4797
|
),
|
|
4727
|
-
limit ? /* @__PURE__ */
|
|
4728
|
-
|
|
4798
|
+
limit ? /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
|
|
4799
|
+
import_Typography14.default,
|
|
4729
4800
|
{
|
|
4730
4801
|
variant: "caption",
|
|
4731
4802
|
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
|
|
@@ -4740,8 +4811,8 @@ var LevelGauge = ({
|
|
|
4740
4811
|
}
|
|
4741
4812
|
},
|
|
4742
4813
|
limit.label
|
|
4743
|
-
), /* @__PURE__ */
|
|
4744
|
-
|
|
4814
|
+
), /* @__PURE__ */ import_react15.default.createElement(
|
|
4815
|
+
import_Typography14.default,
|
|
4745
4816
|
{
|
|
4746
4817
|
variant: "caption",
|
|
4747
4818
|
color: "text.secondary",
|
|
@@ -4752,8 +4823,8 @@ var LevelGauge = ({
|
|
|
4752
4823
|
}
|
|
4753
4824
|
},
|
|
4754
4825
|
spendCaption(limit)
|
|
4755
|
-
), extra > 0 ? /* @__PURE__ */
|
|
4756
|
-
|
|
4826
|
+
), extra > 0 ? /* @__PURE__ */ import_react15.default.createElement(
|
|
4827
|
+
import_Typography14.default,
|
|
4757
4828
|
{
|
|
4758
4829
|
component: import_core_components2.Link,
|
|
4759
4830
|
to: keysHref,
|
|
@@ -4768,8 +4839,8 @@ var LevelGauge = ({
|
|
|
4768
4839
|
"+",
|
|
4769
4840
|
extra,
|
|
4770
4841
|
" more"
|
|
4771
|
-
) : /* @__PURE__ */
|
|
4772
|
-
|
|
4842
|
+
) : /* @__PURE__ */ import_react15.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ import_react15.default.createElement(
|
|
4843
|
+
import_Typography14.default,
|
|
4773
4844
|
{
|
|
4774
4845
|
variant: "caption",
|
|
4775
4846
|
color: "text.secondary",
|
|
@@ -4782,16 +4853,32 @@ var LevelGauge = ({
|
|
|
4782
4853
|
var LiteLLMBudgetGauges = ({
|
|
4783
4854
|
title = "Budget",
|
|
4784
4855
|
size = 72,
|
|
4785
|
-
keysHref
|
|
4856
|
+
keysHref,
|
|
4857
|
+
moduleHref = MODULE_PATH,
|
|
4858
|
+
onCreateKey,
|
|
4859
|
+
ctas,
|
|
4860
|
+
maxExpandedKeys = 8,
|
|
4861
|
+
expanded,
|
|
4862
|
+
defaultExpanded = false,
|
|
4863
|
+
onExpandedChange,
|
|
4786
4864
|
action
|
|
4787
4865
|
}) => {
|
|
4788
4866
|
const api = (0, import_core_plugin_api6.useApi)(liteLlmApiRef);
|
|
4789
|
-
const [loading, setLoading] = (0,
|
|
4790
|
-
const [error, setError] = (0,
|
|
4791
|
-
const [user, setUser] = (0,
|
|
4792
|
-
const [teams, setTeams] = (0,
|
|
4793
|
-
const [keys, setKeys] = (0,
|
|
4794
|
-
|
|
4867
|
+
const [loading, setLoading] = (0, import_react15.useState)(true);
|
|
4868
|
+
const [error, setError] = (0, import_react15.useState)(null);
|
|
4869
|
+
const [user, setUser] = (0, import_react15.useState)(null);
|
|
4870
|
+
const [teams, setTeams] = (0, import_react15.useState)([]);
|
|
4871
|
+
const [keys, setKeys] = (0, import_react15.useState)([]);
|
|
4872
|
+
const isControlled = expanded !== void 0;
|
|
4873
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react15.useState)(defaultExpanded);
|
|
4874
|
+
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
4875
|
+
const toggleExpanded = (0, import_react15.useCallback)(() => {
|
|
4876
|
+
const next = !isExpanded;
|
|
4877
|
+
if (!isControlled) setUncontrolledExpanded(next);
|
|
4878
|
+
onExpandedChange?.(next);
|
|
4879
|
+
}, [isControlled, isExpanded, onExpandedChange]);
|
|
4880
|
+
const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
|
|
4881
|
+
(0, import_react15.useEffect)(() => {
|
|
4795
4882
|
let cancelled = false;
|
|
4796
4883
|
setLoading(true);
|
|
4797
4884
|
setError(null);
|
|
@@ -4815,29 +4902,109 @@ var LiteLLMBudgetGauges = ({
|
|
|
4815
4902
|
cancelled = true;
|
|
4816
4903
|
};
|
|
4817
4904
|
}, [api]);
|
|
4818
|
-
const summary = (0,
|
|
4905
|
+
const summary = (0, import_react15.useMemo)(
|
|
4819
4906
|
() => buildBudgetSummary(user, teams, keys, keys.length),
|
|
4820
4907
|
[user, teams, keys]
|
|
4821
4908
|
);
|
|
4822
|
-
const gauges = (0,
|
|
4823
|
-
const headline = (0,
|
|
4909
|
+
const gauges = (0, import_react15.useMemo)(() => buildBudgetGauges(summary), [summary]);
|
|
4910
|
+
const headline = (0, import_react15.useMemo)(() => budgetHeadline(summary), [summary]);
|
|
4911
|
+
const expandedSummary = (0, import_react15.useMemo)(
|
|
4912
|
+
() => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
|
|
4913
|
+
[user, teams, keys, maxExpandedKeys]
|
|
4914
|
+
);
|
|
4915
|
+
const expandedLimits = (0, import_react15.useMemo)(
|
|
4916
|
+
() => allBudgetLimits(expandedSummary),
|
|
4917
|
+
[expandedSummary]
|
|
4918
|
+
);
|
|
4824
4919
|
const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
4825
|
-
|
|
4920
|
+
const resolvedCtas = (0, import_react15.useMemo)(() => {
|
|
4921
|
+
const list = ctas ?? DEFAULT_CTAS;
|
|
4922
|
+
return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
|
|
4923
|
+
}, [ctas, headline.count]);
|
|
4924
|
+
const renderCta = (cta, index) => {
|
|
4925
|
+
const label = cta.label ?? CTA_LABELS[cta.kind];
|
|
4926
|
+
const key = `${cta.kind}-${index}`;
|
|
4927
|
+
switch (cta.kind) {
|
|
4928
|
+
case "new-key":
|
|
4929
|
+
return onCreateKey ? /* @__PURE__ */ import_react15.default.createElement(import_Button6.default, { key, size: "small", variant: "contained", onClick: onCreateKey }, label) : /* @__PURE__ */ import_react15.default.createElement(
|
|
4930
|
+
import_Button6.default,
|
|
4931
|
+
{
|
|
4932
|
+
key,
|
|
4933
|
+
size: "small",
|
|
4934
|
+
variant: "contained",
|
|
4935
|
+
component: import_core_components2.Link,
|
|
4936
|
+
to: `${moduleHref}?generate=1`
|
|
4937
|
+
},
|
|
4938
|
+
label
|
|
4939
|
+
);
|
|
4940
|
+
case "module":
|
|
4941
|
+
return /* @__PURE__ */ import_react15.default.createElement(import_Button6.default, { key, size: "small", variant: "outlined", component: import_core_components2.Link, to: moduleHref }, label);
|
|
4942
|
+
case "all-limits":
|
|
4943
|
+
return /* @__PURE__ */ import_react15.default.createElement(
|
|
4944
|
+
import_Button6.default,
|
|
4945
|
+
{
|
|
4946
|
+
key,
|
|
4947
|
+
size: "small",
|
|
4948
|
+
variant: "text",
|
|
4949
|
+
onClick: toggleExpanded,
|
|
4950
|
+
endIcon: /* @__PURE__ */ import_react15.default.createElement(
|
|
4951
|
+
import_icons_material10.ExpandMore,
|
|
4952
|
+
{
|
|
4953
|
+
sx: {
|
|
4954
|
+
transform: isExpanded ? "rotate(180deg)" : "none",
|
|
4955
|
+
transition: (theme) => theme.transitions.create("transform")
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
)
|
|
4959
|
+
},
|
|
4960
|
+
label
|
|
4961
|
+
);
|
|
4962
|
+
/* istanbul ignore next — exhaustiveness */
|
|
4963
|
+
default:
|
|
4964
|
+
return null;
|
|
4965
|
+
}
|
|
4966
|
+
};
|
|
4967
|
+
const hasFooter = resolvedCtas.length > 0 || !!action;
|
|
4968
|
+
return /* @__PURE__ */ import_react15.default.createElement(import_Paper10.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react15.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ import_react15.default.createElement(import_Typography14.default, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ import_react15.default.createElement(
|
|
4826
4969
|
StatusPill,
|
|
4827
4970
|
{
|
|
4828
4971
|
label: summaryText,
|
|
4829
4972
|
tone: budgetTone2(headline.closest?.pct ?? 0),
|
|
4830
4973
|
dot: headline.count > 0
|
|
4831
4974
|
}
|
|
4832
|
-
)), loading && /* @__PURE__ */
|
|
4833
|
-
|
|
4975
|
+
)), loading && /* @__PURE__ */ import_react15.default.createElement(import_Box14.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ import_react15.default.createElement(import_CircularProgress8.default, { size: 28 })), !loading && error && /* @__PURE__ */ import_react15.default.createElement(import_Alert8.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ import_react15.default.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref: keysTabHref }))), /* @__PURE__ */ import_react15.default.createElement(import_Collapse4.default, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ import_react15.default.createElement(
|
|
4976
|
+
import_Box14.default,
|
|
4977
|
+
{
|
|
4978
|
+
sx: {
|
|
4979
|
+
mt: 1.5,
|
|
4980
|
+
// Bound the expanded list so a user with many limits still
|
|
4981
|
+
// keeps the card compact and the toggle within reach.
|
|
4982
|
+
maxHeight: 300,
|
|
4983
|
+
overflowY: "auto"
|
|
4984
|
+
}
|
|
4985
|
+
},
|
|
4986
|
+
/* @__PURE__ */ import_react15.default.createElement(LimitListPanel, null, /* @__PURE__ */ import_react15.default.createElement(
|
|
4987
|
+
BudgetLimitList,
|
|
4988
|
+
{
|
|
4989
|
+
limits: expandedLimits,
|
|
4990
|
+
hiddenKeyCount: expandedSummary.hiddenBudgetedKeys,
|
|
4991
|
+
keysHref: keysTabHref
|
|
4992
|
+
}
|
|
4993
|
+
))
|
|
4994
|
+
))), hasFooter && /* @__PURE__ */ import_react15.default.createElement(
|
|
4995
|
+
import_Box14.default,
|
|
4834
4996
|
{
|
|
4835
4997
|
sx: (theme) => ({
|
|
4836
4998
|
mt: 2,
|
|
4837
4999
|
pt: 2,
|
|
4838
|
-
borderTop: `1px solid ${theme.palette.divider}
|
|
5000
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
5001
|
+
display: "flex",
|
|
5002
|
+
alignItems: "center",
|
|
5003
|
+
gap: 1,
|
|
5004
|
+
flexWrap: "wrap"
|
|
4839
5005
|
})
|
|
4840
5006
|
},
|
|
5007
|
+
resolvedCtas.map(renderCta),
|
|
4841
5008
|
action
|
|
4842
5009
|
));
|
|
4843
5010
|
};
|