@acarmisc/backstage-plugin-litellm 0.23.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 +30 -0
- package/dist/components/BudgetLimitList.d.ts +41 -0
- package/dist/components/LiteLLMBudgetGauges.d.ts +59 -0
- package/dist/components/ui.d.ts +20 -0
- package/dist/index.cjs.js +777 -300
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +727 -250
- package/dist/index.esm.js.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -335,7 +335,7 @@ function SegmentedControl({
|
|
|
335
335
|
})
|
|
336
336
|
);
|
|
337
337
|
}
|
|
338
|
-
var import_react, import_Box, import_ButtonBase, import_Paper, import_Typography, import_styles, MONTHS, fmtDateShort, fmtCompact, fmtUsdCompact, CHART_COLORS, SERIES, OTHER_COLOR, ChartTooltip, SeriesLegend, StatusPill, TagChip, SectionCard, ChartCard, MetricStrip, Stat, Meter, dataTableSx, quietIconButtonSx, EmptyState;
|
|
338
|
+
var import_react, import_Box, import_ButtonBase, import_Paper, import_Typography, import_styles, MONTHS, fmtDateShort, fmtCompact, fmtUsdCompact, CHART_COLORS, SERIES, OTHER_COLOR, ChartTooltip, SeriesLegend, StatusPill, TagChip, SectionCard, ChartCard, MetricStrip, Stat, Meter, Gauge, dataTableSx, quietIconButtonSx, EmptyState;
|
|
339
339
|
var init_ui = __esm({
|
|
340
340
|
"src/components/ui.tsx"() {
|
|
341
341
|
"use strict";
|
|
@@ -705,6 +705,102 @@ var init_ui = __esm({
|
|
|
705
705
|
}
|
|
706
706
|
)
|
|
707
707
|
);
|
|
708
|
+
Gauge = ({ value, tone = "accent", size = 72, thickness = 6, label, caption }) => {
|
|
709
|
+
const clamped = Math.max(0, Math.min(100, value ?? 0));
|
|
710
|
+
const r = (size - thickness) / 2;
|
|
711
|
+
const c = 2 * Math.PI * r;
|
|
712
|
+
const dash = clamped / 100 * c;
|
|
713
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
714
|
+
import_Box.default,
|
|
715
|
+
{
|
|
716
|
+
sx: (theme) => ({
|
|
717
|
+
position: "relative",
|
|
718
|
+
width: size,
|
|
719
|
+
height: size,
|
|
720
|
+
flexShrink: 0,
|
|
721
|
+
color: toneColor(theme, tone)
|
|
722
|
+
})
|
|
723
|
+
},
|
|
724
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
725
|
+
import_Box.default,
|
|
726
|
+
{
|
|
727
|
+
component: "svg",
|
|
728
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
729
|
+
width: size,
|
|
730
|
+
height: size,
|
|
731
|
+
sx: { display: "block", transform: "rotate(-90deg)" }
|
|
732
|
+
},
|
|
733
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
734
|
+
import_Box.default,
|
|
735
|
+
{
|
|
736
|
+
component: "circle",
|
|
737
|
+
cx: size / 2,
|
|
738
|
+
cy: size / 2,
|
|
739
|
+
r,
|
|
740
|
+
fill: "none",
|
|
741
|
+
strokeWidth: thickness,
|
|
742
|
+
sx: (theme) => ({
|
|
743
|
+
stroke: (0, import_styles.alpha)(
|
|
744
|
+
theme.palette.text.primary,
|
|
745
|
+
theme.palette.mode === "dark" ? 0.14 : 0.09
|
|
746
|
+
)
|
|
747
|
+
})
|
|
748
|
+
}
|
|
749
|
+
),
|
|
750
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
751
|
+
import_Box.default,
|
|
752
|
+
{
|
|
753
|
+
component: "circle",
|
|
754
|
+
cx: size / 2,
|
|
755
|
+
cy: size / 2,
|
|
756
|
+
r,
|
|
757
|
+
fill: "none",
|
|
758
|
+
strokeWidth: thickness,
|
|
759
|
+
strokeLinecap: "round",
|
|
760
|
+
strokeDasharray: `${dash} ${c - dash}`,
|
|
761
|
+
sx: (theme) => ({
|
|
762
|
+
stroke: "currentColor",
|
|
763
|
+
transition: theme.transitions.create("stroke-dasharray")
|
|
764
|
+
})
|
|
765
|
+
}
|
|
766
|
+
)
|
|
767
|
+
),
|
|
768
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
769
|
+
import_Box.default,
|
|
770
|
+
{
|
|
771
|
+
sx: {
|
|
772
|
+
position: "absolute",
|
|
773
|
+
inset: 0,
|
|
774
|
+
display: "flex",
|
|
775
|
+
flexDirection: "column",
|
|
776
|
+
alignItems: "center",
|
|
777
|
+
justifyContent: "center",
|
|
778
|
+
lineHeight: 1
|
|
779
|
+
}
|
|
780
|
+
},
|
|
781
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
782
|
+
import_Typography.default,
|
|
783
|
+
{
|
|
784
|
+
sx: {
|
|
785
|
+
fontSize: size <= 64 ? 15 : 18,
|
|
786
|
+
fontWeight: 700,
|
|
787
|
+
fontVariantNumeric: "tabular-nums",
|
|
788
|
+
color: "text.primary"
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
label ?? `${Math.round(value)}%`
|
|
792
|
+
),
|
|
793
|
+
caption && /* @__PURE__ */ import_react.default.createElement(
|
|
794
|
+
import_Typography.default,
|
|
795
|
+
{
|
|
796
|
+
variant: "caption",
|
|
797
|
+
sx: { fontSize: 9.5, color: "text.secondary", mt: 0.25 }
|
|
798
|
+
},
|
|
799
|
+
caption
|
|
800
|
+
)
|
|
801
|
+
)
|
|
802
|
+
);
|
|
803
|
+
};
|
|
708
804
|
dataTableSx = (theme) => ({
|
|
709
805
|
// Element selectors, not `.MuiTableCell-head`: host apps may set a MUI
|
|
710
806
|
// classname prefix (Backstage ships `v5-`), which breaks global class targeting.
|
|
@@ -2835,6 +2931,29 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
|
|
|
2835
2931
|
teams: teamLimits
|
|
2836
2932
|
};
|
|
2837
2933
|
}
|
|
2934
|
+
function allBudgetLimits(summary) {
|
|
2935
|
+
return [
|
|
2936
|
+
...summary.keys,
|
|
2937
|
+
...summary.user ? [summary.user] : [],
|
|
2938
|
+
...summary.teams
|
|
2939
|
+
];
|
|
2940
|
+
}
|
|
2941
|
+
function buildBudgetGauges(summary) {
|
|
2942
|
+
const byLevel = {
|
|
2943
|
+
key: summary.keys,
|
|
2944
|
+
user: summary.user ? [summary.user] : [],
|
|
2945
|
+
team: summary.teams
|
|
2946
|
+
};
|
|
2947
|
+
return GAUGE_KINDS.map((kind) => {
|
|
2948
|
+
const limits = byLevel[kind];
|
|
2949
|
+
const count = kind === "key" ? limits.length + summary.hiddenBudgetedKeys : limits.length;
|
|
2950
|
+
return {
|
|
2951
|
+
kind,
|
|
2952
|
+
count,
|
|
2953
|
+
limit: limits.length ? limits.reduce((a, b) => b.pct > a.pct ? b : a) : void 0
|
|
2954
|
+
};
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2838
2957
|
function budgetHeadline(summary) {
|
|
2839
2958
|
const all = [
|
|
2840
2959
|
...summary.keys,
|
|
@@ -2861,13 +2980,15 @@ function budgetTone2(pct) {
|
|
|
2861
2980
|
if (pct >= 80) return "warning";
|
|
2862
2981
|
return "accent";
|
|
2863
2982
|
}
|
|
2983
|
+
var GAUGE_KINDS;
|
|
2864
2984
|
var init_budget = __esm({
|
|
2865
2985
|
"src/budget.ts"() {
|
|
2866
2986
|
"use strict";
|
|
2987
|
+
GAUGE_KINDS = ["key", "user", "team"];
|
|
2867
2988
|
}
|
|
2868
2989
|
});
|
|
2869
2990
|
|
|
2870
|
-
// src/components/
|
|
2991
|
+
// src/components/BudgetLimitList.tsx
|
|
2871
2992
|
function levelToneColor(tone, theme) {
|
|
2872
2993
|
switch (tone) {
|
|
2873
2994
|
case "success":
|
|
@@ -2882,25 +3003,125 @@ function levelToneColor(tone, theme) {
|
|
|
2882
3003
|
return theme.palette.primary.main;
|
|
2883
3004
|
}
|
|
2884
3005
|
}
|
|
2885
|
-
var import_react7,
|
|
2886
|
-
var
|
|
2887
|
-
"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"() {
|
|
2888
3009
|
"use strict";
|
|
2889
3010
|
import_react7 = __toESM(require("react"));
|
|
2890
|
-
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
2891
3011
|
import_Box7 = __toESM(require("@mui/material/Box"));
|
|
3012
|
+
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
2892
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"));
|
|
2893
3115
|
import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
|
|
2894
3116
|
import_Alert3 = __toESM(require("@mui/material/Alert"));
|
|
2895
3117
|
import_Collapse = __toESM(require("@mui/material/Collapse"));
|
|
2896
3118
|
import_IconButton4 = __toESM(require("@mui/material/IconButton"));
|
|
2897
3119
|
import_icons_material5 = require("@mui/icons-material");
|
|
2898
|
-
|
|
3120
|
+
import_styles5 = require("@mui/material/styles");
|
|
2899
3121
|
import_core_plugin_api3 = require("@backstage/core-plugin-api");
|
|
2900
|
-
import_core_components = require("@backstage/core-components");
|
|
2901
3122
|
init_api();
|
|
2902
|
-
init_format();
|
|
2903
3123
|
init_ui();
|
|
3124
|
+
init_BudgetLimitList();
|
|
2904
3125
|
init_budget();
|
|
2905
3126
|
LevelFrame = ({
|
|
2906
3127
|
rank,
|
|
@@ -2910,8 +3131,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2910
3131
|
tone,
|
|
2911
3132
|
isLast,
|
|
2912
3133
|
children
|
|
2913
|
-
}) => /* @__PURE__ */
|
|
2914
|
-
|
|
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,
|
|
2915
3136
|
{
|
|
2916
3137
|
sx: (theme) => ({
|
|
2917
3138
|
width: 26,
|
|
@@ -2920,7 +3141,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2920
3141
|
display: "flex",
|
|
2921
3142
|
alignItems: "center",
|
|
2922
3143
|
justifyContent: "center",
|
|
2923
|
-
bgcolor: (0,
|
|
3144
|
+
bgcolor: (0, import_styles5.alpha)(levelToneColor(tone, theme), 0.14),
|
|
2924
3145
|
color: levelToneColor(tone, theme),
|
|
2925
3146
|
fontSize: 12,
|
|
2926
3147
|
fontWeight: 700,
|
|
@@ -2928,18 +3149,18 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2928
3149
|
})
|
|
2929
3150
|
},
|
|
2930
3151
|
rank
|
|
2931
|
-
), /* @__PURE__ */
|
|
2932
|
-
|
|
3152
|
+
), /* @__PURE__ */ import_react8.default.createElement(
|
|
3153
|
+
import_Box8.default,
|
|
2933
3154
|
{
|
|
2934
3155
|
sx: (theme) => ({
|
|
2935
3156
|
flex: 1,
|
|
2936
3157
|
width: 2,
|
|
2937
3158
|
minHeight: 14,
|
|
2938
|
-
bgcolor: isLast ? "transparent" : (0,
|
|
3159
|
+
bgcolor: isLast ? "transparent" : (0, import_styles5.alpha)(theme.palette.text.primary, 0.12)
|
|
2939
3160
|
})
|
|
2940
3161
|
}
|
|
2941
|
-
)), /* @__PURE__ */
|
|
2942
|
-
|
|
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,
|
|
2943
3164
|
{
|
|
2944
3165
|
sx: (theme) => ({
|
|
2945
3166
|
fontSize: 12.5,
|
|
@@ -2950,102 +3171,42 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2950
3171
|
})
|
|
2951
3172
|
},
|
|
2952
3173
|
name
|
|
2953
|
-
), /* @__PURE__ */
|
|
2954
|
-
EmptyLimitCard = ({ children }) => /* @__PURE__ */
|
|
2955
|
-
|
|
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,
|
|
2956
3177
|
{
|
|
2957
3178
|
variant: "outlined",
|
|
2958
3179
|
sx: (theme) => ({
|
|
2959
3180
|
px: 1.5,
|
|
2960
3181
|
py: 1.25,
|
|
2961
3182
|
borderRadius: 1.5,
|
|
2962
|
-
bgcolor: (0,
|
|
3183
|
+
bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
|
|
2963
3184
|
})
|
|
2964
3185
|
},
|
|
2965
|
-
/* @__PURE__ */
|
|
3186
|
+
/* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
|
|
2966
3187
|
);
|
|
2967
|
-
LimitCard = ({ limit }) => {
|
|
2968
|
-
const tone = budgetTone2(limit.pct);
|
|
2969
|
-
const pct = Math.round(limit.pct);
|
|
2970
|
-
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
2971
|
-
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(
|
|
2972
|
-
import_Typography7.default,
|
|
2973
|
-
{
|
|
2974
|
-
variant: "body2",
|
|
2975
|
-
sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
2976
|
-
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
2977
|
-
},
|
|
2978
|
-
limit.label
|
|
2979
|
-
), limit.sublabel && /* @__PURE__ */ import_react7.default.createElement(
|
|
2980
|
-
import_Typography7.default,
|
|
2981
|
-
{
|
|
2982
|
-
variant: "caption",
|
|
2983
|
-
color: "text.secondary",
|
|
2984
|
-
sx: {
|
|
2985
|
-
fontSize: 10.5,
|
|
2986
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
2987
|
-
display: "block",
|
|
2988
|
-
overflow: "hidden",
|
|
2989
|
-
textOverflow: "ellipsis",
|
|
2990
|
-
whiteSpace: "nowrap"
|
|
2991
|
-
}
|
|
2992
|
-
},
|
|
2993
|
-
limit.sublabel
|
|
2994
|
-
)), 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(
|
|
2995
|
-
import_Box7.default,
|
|
2996
|
-
{
|
|
2997
|
-
sx: {
|
|
2998
|
-
display: "flex",
|
|
2999
|
-
justifyContent: "space-between",
|
|
3000
|
-
alignItems: "center",
|
|
3001
|
-
gap: 1,
|
|
3002
|
-
mt: 0.75
|
|
3003
|
-
}
|
|
3004
|
-
},
|
|
3005
|
-
/* @__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`)),
|
|
3006
|
-
/* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3007
|
-
));
|
|
3008
|
-
};
|
|
3009
3188
|
CLOSEST_LEVEL_LABEL = {
|
|
3010
3189
|
key: "key",
|
|
3011
3190
|
user: "personal",
|
|
3012
3191
|
team: "team"
|
|
3013
3192
|
};
|
|
3014
3193
|
KEYS_PATH = "/litellm?tab=keys";
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
import_Box7.default,
|
|
3194
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react8.default.createElement(
|
|
3195
|
+
import_Box8.default,
|
|
3018
3196
|
{
|
|
3019
3197
|
sx: (theme) => ({
|
|
3020
3198
|
px: 1.5,
|
|
3021
3199
|
py: 1.25,
|
|
3022
3200
|
borderRadius: 1.5,
|
|
3023
3201
|
border: "1px dashed",
|
|
3024
|
-
borderColor: (0,
|
|
3202
|
+
borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
|
|
3025
3203
|
})
|
|
3026
3204
|
},
|
|
3027
|
-
/* @__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.")
|
|
3028
3206
|
);
|
|
3029
|
-
TaggedLimit = ({ limit }) => {
|
|
3030
|
-
const tone = budgetTone2(limit.pct);
|
|
3031
|
-
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
|
|
3032
|
-
import_Typography7.default,
|
|
3033
|
-
{
|
|
3034
|
-
sx: (theme) => ({
|
|
3035
|
-
fontSize: 11,
|
|
3036
|
-
fontWeight: 700,
|
|
3037
|
-
letterSpacing: "0.08em",
|
|
3038
|
-
textTransform: "uppercase",
|
|
3039
|
-
color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
|
|
3040
|
-
mb: 0.5
|
|
3041
|
-
})
|
|
3042
|
-
},
|
|
3043
|
-
limit.kind
|
|
3044
|
-
), /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit }));
|
|
3045
|
-
};
|
|
3046
3207
|
FullBody = ({ summary }) => {
|
|
3047
3208
|
const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
|
|
3048
|
-
return /* @__PURE__ */
|
|
3209
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(
|
|
3049
3210
|
LevelFrame,
|
|
3050
3211
|
{
|
|
3051
3212
|
rank: 1,
|
|
@@ -3053,10 +3214,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3053
3214
|
tagline: "per-key cap \xB7 highest priority",
|
|
3054
3215
|
note: hasBudgetedKeys ? void 0 : "no key budgets set"
|
|
3055
3216
|
},
|
|
3056
|
-
!hasBudgetedKeys && /* @__PURE__ */
|
|
3057
|
-
summary.keys.map((k) => /* @__PURE__ */
|
|
3058
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */
|
|
3059
|
-
), /* @__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(
|
|
3060
3221
|
LevelFrame,
|
|
3061
3222
|
{
|
|
3062
3223
|
rank: 2,
|
|
@@ -3064,15 +3225,15 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3064
3225
|
tagline: "cap on everything you spend with your own keys",
|
|
3065
3226
|
note: "skipped for team-bound keys"
|
|
3066
3227
|
},
|
|
3067
|
-
summary.user ? /* @__PURE__ */
|
|
3068
|
-
), /* @__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 })));
|
|
3069
3230
|
};
|
|
3070
3231
|
CompactBody = ({ summary }) => {
|
|
3071
3232
|
const { count } = budgetHeadline(summary);
|
|
3072
3233
|
if (count === 0) {
|
|
3073
|
-
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.");
|
|
3074
3235
|
}
|
|
3075
|
-
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 })));
|
|
3076
3237
|
};
|
|
3077
3238
|
LiteLLMBudgetWidget = ({
|
|
3078
3239
|
title = "Budget Policy",
|
|
@@ -3083,13 +3244,13 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3083
3244
|
action
|
|
3084
3245
|
}) => {
|
|
3085
3246
|
const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
|
|
3086
|
-
const [loading, setLoading] = (0,
|
|
3087
|
-
const [error, setError] = (0,
|
|
3088
|
-
const [user, setUser] = (0,
|
|
3089
|
-
const [teams, setTeams] = (0,
|
|
3090
|
-
const [keys, setKeys] = (0,
|
|
3091
|
-
const [expanded, setExpanded] = (0,
|
|
3092
|
-
(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)(() => {
|
|
3093
3254
|
let cancelled = false;
|
|
3094
3255
|
setLoading(true);
|
|
3095
3256
|
setError(null);
|
|
@@ -3113,19 +3274,19 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3113
3274
|
cancelled = true;
|
|
3114
3275
|
};
|
|
3115
3276
|
}, [api]);
|
|
3116
|
-
const summary = (0,
|
|
3277
|
+
const summary = (0, import_react8.useMemo)(
|
|
3117
3278
|
() => buildBudgetSummary(user, teams, keys, maxKeys),
|
|
3118
3279
|
[user, teams, keys, maxKeys]
|
|
3119
3280
|
);
|
|
3120
|
-
const headline = (0,
|
|
3281
|
+
const headline = (0, import_react8.useMemo)(() => budgetHeadline(summary), [summary]);
|
|
3121
3282
|
const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
3122
3283
|
const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
|
|
3123
3284
|
headline.closest.pct
|
|
3124
3285
|
)}%`;
|
|
3125
3286
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3126
|
-
const body = /* @__PURE__ */
|
|
3127
|
-
return /* @__PURE__ */
|
|
3128
|
-
|
|
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,
|
|
3129
3290
|
{
|
|
3130
3291
|
onClick: () => setExpanded((e) => !e),
|
|
3131
3292
|
sx: {
|
|
@@ -3137,14 +3298,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3137
3298
|
mb: expanded ? 1.5 : 0
|
|
3138
3299
|
}
|
|
3139
3300
|
},
|
|
3140
|
-
/* @__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(
|
|
3141
3302
|
StatusPill,
|
|
3142
3303
|
{
|
|
3143
3304
|
label: summaryText,
|
|
3144
3305
|
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3145
3306
|
}
|
|
3146
3307
|
))),
|
|
3147
|
-
/* @__PURE__ */
|
|
3308
|
+
/* @__PURE__ */ import_react8.default.createElement(
|
|
3148
3309
|
import_IconButton4.default,
|
|
3149
3310
|
{
|
|
3150
3311
|
size: "small",
|
|
@@ -3160,10 +3321,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3160
3321
|
transition: theme.transitions.create("transform")
|
|
3161
3322
|
})
|
|
3162
3323
|
},
|
|
3163
|
-
/* @__PURE__ */
|
|
3324
|
+
/* @__PURE__ */ import_react8.default.createElement(import_icons_material5.ExpandMore, null)
|
|
3164
3325
|
)
|
|
3165
|
-
) : /* @__PURE__ */
|
|
3166
|
-
|
|
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,
|
|
3167
3328
|
{
|
|
3168
3329
|
sx: (theme) => ({
|
|
3169
3330
|
mt: 2,
|
|
@@ -3192,14 +3353,14 @@ function budgetStatValue(hidden, budget) {
|
|
|
3192
3353
|
if (budget > 0) return fmtUsd2(budget);
|
|
3193
3354
|
return "Unlimited";
|
|
3194
3355
|
}
|
|
3195
|
-
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;
|
|
3196
3357
|
var init_TeamUsage = __esm({
|
|
3197
3358
|
"src/components/TeamUsage.tsx"() {
|
|
3198
3359
|
"use strict";
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
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"));
|
|
3203
3364
|
import_Skeleton4 = __toESM(require("@mui/material/Skeleton"));
|
|
3204
3365
|
import_Divider3 = __toESM(require("@mui/material/Divider"));
|
|
3205
3366
|
import_Table4 = __toESM(require("@mui/material/Table"));
|
|
@@ -3212,13 +3373,13 @@ var init_TeamUsage = __esm({
|
|
|
3212
3373
|
import_IconButton5 = __toESM(require("@mui/material/IconButton"));
|
|
3213
3374
|
import_Button5 = __toESM(require("@mui/material/Button"));
|
|
3214
3375
|
import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
|
|
3215
|
-
|
|
3376
|
+
import_styles6 = require("@mui/material/styles");
|
|
3216
3377
|
import_icons_material6 = require("@mui/icons-material");
|
|
3217
3378
|
import_recharts2 = require("recharts");
|
|
3218
3379
|
init_ui();
|
|
3219
3380
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
3220
3381
|
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
|
|
3221
|
-
const [expanded, setExpanded] = (0,
|
|
3382
|
+
const [expanded, setExpanded] = (0, import_react9.useState)(false);
|
|
3222
3383
|
const chart = useChartTheme();
|
|
3223
3384
|
const hidden = team.budget_hidden === true;
|
|
3224
3385
|
const budget = hidden ? 0 : team.max_budget ?? 0;
|
|
@@ -3230,24 +3391,24 @@ var init_TeamUsage = __esm({
|
|
|
3230
3391
|
const renderDailySpendSection = () => {
|
|
3231
3392
|
if (hidden) return null;
|
|
3232
3393
|
if (usageLoading) {
|
|
3233
|
-
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 }));
|
|
3234
3395
|
}
|
|
3235
3396
|
if (dailyData.length === 0) return null;
|
|
3236
|
-
return /* @__PURE__ */
|
|
3237
|
-
|
|
3397
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3398
|
+
import_Typography9.default,
|
|
3238
3399
|
{
|
|
3239
3400
|
variant: "caption",
|
|
3240
3401
|
color: "text.secondary",
|
|
3241
3402
|
sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
|
|
3242
3403
|
},
|
|
3243
3404
|
"Daily spend"
|
|
3244
|
-
), /* @__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(
|
|
3245
3406
|
import_recharts2.Tooltip,
|
|
3246
3407
|
{
|
|
3247
3408
|
cursor: chart.cursor,
|
|
3248
|
-
content: /* @__PURE__ */
|
|
3409
|
+
content: /* @__PURE__ */ import_react9.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
3249
3410
|
}
|
|
3250
|
-
), /* @__PURE__ */
|
|
3411
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3251
3412
|
import_recharts2.Area,
|
|
3252
3413
|
{
|
|
3253
3414
|
type: "monotone",
|
|
@@ -3260,8 +3421,8 @@ var init_TeamUsage = __esm({
|
|
|
3260
3421
|
}
|
|
3261
3422
|
)))));
|
|
3262
3423
|
};
|
|
3263
|
-
const sectionLabel = (label) => /* @__PURE__ */
|
|
3264
|
-
|
|
3424
|
+
const sectionLabel = (label) => /* @__PURE__ */ import_react9.default.createElement(
|
|
3425
|
+
import_Typography9.default,
|
|
3265
3426
|
{
|
|
3266
3427
|
variant: "caption",
|
|
3267
3428
|
color: "text.secondary",
|
|
@@ -3269,8 +3430,8 @@ var init_TeamUsage = __esm({
|
|
|
3269
3430
|
},
|
|
3270
3431
|
label
|
|
3271
3432
|
);
|
|
3272
|
-
return /* @__PURE__ */
|
|
3273
|
-
|
|
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,
|
|
3274
3435
|
{
|
|
3275
3436
|
sx: (theme) => ({
|
|
3276
3437
|
width: 36,
|
|
@@ -3280,20 +3441,20 @@ var init_TeamUsage = __esm({
|
|
|
3280
3441
|
display: "flex",
|
|
3281
3442
|
alignItems: "center",
|
|
3282
3443
|
justifyContent: "center",
|
|
3283
|
-
bgcolor: (0,
|
|
3444
|
+
bgcolor: (0, import_styles6.alpha)(theme.palette.primary.main, 0.1),
|
|
3284
3445
|
color: theme.palette.primary.main
|
|
3285
3446
|
})
|
|
3286
3447
|
},
|
|
3287
|
-
/* @__PURE__ */
|
|
3288
|
-
), /* @__PURE__ */
|
|
3289
|
-
|
|
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,
|
|
3290
3451
|
{
|
|
3291
3452
|
variant: "caption",
|
|
3292
3453
|
color: "text.secondary",
|
|
3293
3454
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3294
3455
|
},
|
|
3295
3456
|
team.team_id
|
|
3296
|
-
)), 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(
|
|
3297
3458
|
import_Button5.default,
|
|
3298
3459
|
{
|
|
3299
3460
|
size: "small",
|
|
@@ -3302,7 +3463,7 @@ var init_TeamUsage = __esm({
|
|
|
3302
3463
|
sx: { textTransform: "none" }
|
|
3303
3464
|
},
|
|
3304
3465
|
"Edit"
|
|
3305
|
-
), /* @__PURE__ */
|
|
3466
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3306
3467
|
import_IconButton5.default,
|
|
3307
3468
|
{
|
|
3308
3469
|
size: "small",
|
|
@@ -3315,9 +3476,9 @@ var init_TeamUsage = __esm({
|
|
|
3315
3476
|
transition: theme.transitions.create("transform")
|
|
3316
3477
|
})
|
|
3317
3478
|
},
|
|
3318
|
-
/* @__PURE__ */
|
|
3319
|
-
)), /* @__PURE__ */
|
|
3320
|
-
|
|
3479
|
+
/* @__PURE__ */ import_react9.default.createElement(import_icons_material6.ExpandMore, null)
|
|
3480
|
+
)), /* @__PURE__ */ import_react9.default.createElement(
|
|
3481
|
+
import_Box9.default,
|
|
3321
3482
|
{
|
|
3322
3483
|
sx: {
|
|
3323
3484
|
display: "grid",
|
|
@@ -3327,35 +3488,35 @@ var init_TeamUsage = __esm({
|
|
|
3327
3488
|
maxWidth: 640
|
|
3328
3489
|
}
|
|
3329
3490
|
},
|
|
3330
|
-
/* @__PURE__ */
|
|
3331
|
-
/* @__PURE__ */
|
|
3332
|
-
/* @__PURE__ */
|
|
3333
|
-
/* @__PURE__ */
|
|
3334
|
-
/* @__PURE__ */
|
|
3335
|
-
/* @__PURE__ */
|
|
3336
|
-
), 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(
|
|
3337
3498
|
import_TableContainer4.default,
|
|
3338
3499
|
{
|
|
3339
|
-
component:
|
|
3500
|
+
component: import_Paper7.default,
|
|
3340
3501
|
variant: "outlined",
|
|
3341
3502
|
sx: { borderRadius: 1.5 }
|
|
3342
3503
|
},
|
|
3343
|
-
/* @__PURE__ */
|
|
3344
|
-
|
|
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,
|
|
3345
3506
|
{
|
|
3346
3507
|
variant: "caption",
|
|
3347
3508
|
color: "text.secondary",
|
|
3348
3509
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3349
3510
|
},
|
|
3350
3511
|
m.user_id
|
|
3351
|
-
)) : /* @__PURE__ */
|
|
3352
|
-
|
|
3512
|
+
)) : /* @__PURE__ */ import_react9.default.createElement(
|
|
3513
|
+
import_Typography9.default,
|
|
3353
3514
|
{
|
|
3354
3515
|
variant: "body2",
|
|
3355
3516
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
3356
3517
|
},
|
|
3357
3518
|
m.user_id
|
|
3358
|
-
)), /* @__PURE__ */
|
|
3519
|
+
)), /* @__PURE__ */ import_react9.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3359
3520
|
StatusPill,
|
|
3360
3521
|
{
|
|
3361
3522
|
label: m.role,
|
|
@@ -3363,7 +3524,7 @@ var init_TeamUsage = __esm({
|
|
|
3363
3524
|
dot: false
|
|
3364
3525
|
}
|
|
3365
3526
|
))))))
|
|
3366
|
-
) : /* @__PURE__ */
|
|
3527
|
+
) : /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
|
|
3367
3528
|
};
|
|
3368
3529
|
TeamUsage = ({
|
|
3369
3530
|
teams,
|
|
@@ -3375,22 +3536,22 @@ var init_TeamUsage = __esm({
|
|
|
3375
3536
|
canCreate,
|
|
3376
3537
|
onCreateTeam
|
|
3377
3538
|
}) => {
|
|
3378
|
-
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */
|
|
3539
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react9.default.createElement(
|
|
3379
3540
|
import_Button5.default,
|
|
3380
3541
|
{
|
|
3381
3542
|
variant: "contained",
|
|
3382
3543
|
color: "primary",
|
|
3383
3544
|
disableElevation: true,
|
|
3384
|
-
startIcon: /* @__PURE__ */
|
|
3545
|
+
startIcon: /* @__PURE__ */ import_react9.default.createElement(import_icons_material6.Add, null),
|
|
3385
3546
|
onClick: onCreateTeam
|
|
3386
3547
|
},
|
|
3387
3548
|
"Create Team"
|
|
3388
3549
|
) : void 0;
|
|
3389
3550
|
if (loading) {
|
|
3390
|
-
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 }));
|
|
3391
3552
|
}
|
|
3392
3553
|
if (!teams.length) {
|
|
3393
|
-
return /* @__PURE__ */
|
|
3554
|
+
return /* @__PURE__ */ import_react9.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3394
3555
|
EmptyState,
|
|
3395
3556
|
{
|
|
3396
3557
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -3398,14 +3559,14 @@ var init_TeamUsage = __esm({
|
|
|
3398
3559
|
}
|
|
3399
3560
|
));
|
|
3400
3561
|
}
|
|
3401
|
-
return /* @__PURE__ */
|
|
3562
|
+
return /* @__PURE__ */ import_react9.default.createElement(
|
|
3402
3563
|
SectionCard,
|
|
3403
3564
|
{
|
|
3404
3565
|
title: "Teams",
|
|
3405
3566
|
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
3406
3567
|
actions: createAction
|
|
3407
3568
|
},
|
|
3408
|
-
/* @__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(
|
|
3409
3570
|
TeamCard,
|
|
3410
3571
|
{
|
|
3411
3572
|
key: team.team_id,
|
|
@@ -3439,12 +3600,12 @@ function fmtTokens(n) {
|
|
|
3439
3600
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
3440
3601
|
return String(n);
|
|
3441
3602
|
}
|
|
3442
|
-
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;
|
|
3443
3604
|
var init_ModelsTable = __esm({
|
|
3444
3605
|
"src/components/ModelsTable.tsx"() {
|
|
3445
3606
|
"use strict";
|
|
3446
|
-
|
|
3447
|
-
|
|
3607
|
+
import_react10 = __toESM(require("react"));
|
|
3608
|
+
import_Box10 = __toESM(require("@mui/material/Box"));
|
|
3448
3609
|
import_Table5 = __toESM(require("@mui/material/Table"));
|
|
3449
3610
|
import_TableBody5 = __toESM(require("@mui/material/TableBody"));
|
|
3450
3611
|
import_TableCell5 = __toESM(require("@mui/material/TableCell"));
|
|
@@ -3452,7 +3613,7 @@ var init_ModelsTable = __esm({
|
|
|
3452
3613
|
import_TableHead5 = __toESM(require("@mui/material/TableHead"));
|
|
3453
3614
|
import_TableRow5 = __toESM(require("@mui/material/TableRow"));
|
|
3454
3615
|
import_TableSortLabel2 = __toESM(require("@mui/material/TableSortLabel"));
|
|
3455
|
-
|
|
3616
|
+
import_Typography10 = __toESM(require("@mui/material/Typography"));
|
|
3456
3617
|
import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
|
|
3457
3618
|
import_Select = __toESM(require("@mui/material/Select"));
|
|
3458
3619
|
import_FormControl = __toESM(require("@mui/material/FormControl"));
|
|
@@ -3465,24 +3626,24 @@ var init_ModelsTable = __esm({
|
|
|
3465
3626
|
import_Snackbar = __toESM(require("@mui/material/Snackbar"));
|
|
3466
3627
|
import_Alert4 = __toESM(require("@mui/material/Alert"));
|
|
3467
3628
|
init_ui();
|
|
3468
|
-
|
|
3629
|
+
import_styles7 = require("@mui/material/styles");
|
|
3469
3630
|
ALL_PROXY_MODELS2 = "all-proxy-models";
|
|
3470
3631
|
ModelsTable = ({ allModels, teams, loading }) => {
|
|
3471
|
-
const theme = (0,
|
|
3472
|
-
const [selectedTeamId, setSelectedTeamId] = (0,
|
|
3473
|
-
const [filterText, setFilterText] = (0,
|
|
3474
|
-
const [copiedSnackbar, setCopiedSnackbar] = (0,
|
|
3475
|
-
const [sortKey, setSortKey] = (0,
|
|
3476
|
-
const [sortDirection, setSortDirection] = (0,
|
|
3477
|
-
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)(
|
|
3478
3639
|
() => teams.find((t) => t.team_id === selectedTeamId) ?? null,
|
|
3479
3640
|
[teams, selectedTeamId]
|
|
3480
3641
|
);
|
|
3481
|
-
const teamModels = (0,
|
|
3642
|
+
const teamModels = (0, import_react10.useMemo)(() => {
|
|
3482
3643
|
if (!selectedTeam) return [];
|
|
3483
3644
|
return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
|
|
3484
3645
|
}, [allModels, selectedTeam]);
|
|
3485
|
-
const filteredModels = (0,
|
|
3646
|
+
const filteredModels = (0, import_react10.useMemo)(() => {
|
|
3486
3647
|
const q = filterText.trim().toLowerCase();
|
|
3487
3648
|
let models = teamModels;
|
|
3488
3649
|
if (q) {
|
|
@@ -3529,10 +3690,10 @@ var init_ModelsTable = __esm({
|
|
|
3529
3690
|
return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
|
|
3530
3691
|
});
|
|
3531
3692
|
}, [teamModels, filterText, sortKey, sortDirection]);
|
|
3532
|
-
const handleCopyModelId = (0,
|
|
3693
|
+
const handleCopyModelId = (0, import_react10.useCallback)((modelId) => {
|
|
3533
3694
|
navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
|
|
3534
3695
|
}, []);
|
|
3535
|
-
const handleSort = (0,
|
|
3696
|
+
const handleSort = (0, import_react10.useCallback)((key) => {
|
|
3536
3697
|
if (sortKey === key) {
|
|
3537
3698
|
setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
|
|
3538
3699
|
} else {
|
|
@@ -3554,13 +3715,13 @@ var init_ModelsTable = __esm({
|
|
|
3554
3715
|
return theme.palette.info.main;
|
|
3555
3716
|
}
|
|
3556
3717
|
};
|
|
3557
|
-
return /* @__PURE__ */
|
|
3718
|
+
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3558
3719
|
SectionCard,
|
|
3559
3720
|
{
|
|
3560
3721
|
title: "Available Models",
|
|
3561
3722
|
subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
|
|
3562
3723
|
},
|
|
3563
|
-
/* @__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(
|
|
3564
3725
|
import_Select.default,
|
|
3565
3726
|
{
|
|
3566
3727
|
labelId: "team-select-label",
|
|
@@ -3568,8 +3729,8 @@ var init_ModelsTable = __esm({
|
|
|
3568
3729
|
label: "Team",
|
|
3569
3730
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
3570
3731
|
},
|
|
3571
|
-
teams.map((t) => /* @__PURE__ */
|
|
3572
|
-
)), /* @__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(
|
|
3573
3734
|
import_TextField5.default,
|
|
3574
3735
|
{
|
|
3575
3736
|
label: "Filter Models",
|
|
@@ -3579,33 +3740,33 @@ var init_ModelsTable = __esm({
|
|
|
3579
3740
|
disabled: !selectedTeamId,
|
|
3580
3741
|
sx: { minWidth: 240, mb: 2 },
|
|
3581
3742
|
InputProps: {
|
|
3582
|
-
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" }))
|
|
3583
3744
|
}
|
|
3584
3745
|
}
|
|
3585
3746
|
)),
|
|
3586
|
-
loading && /* @__PURE__ */
|
|
3587
|
-
!loading && !selectedTeamId && /* @__PURE__ */
|
|
3747
|
+
loading && /* @__PURE__ */ import_react10.default.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
3748
|
+
!loading && !selectedTeamId && /* @__PURE__ */ import_react10.default.createElement(
|
|
3588
3749
|
EmptyState,
|
|
3589
3750
|
{
|
|
3590
3751
|
message: "Select a team to see available models",
|
|
3591
3752
|
hint: "Each team may have a different set of models assigned"
|
|
3592
3753
|
}
|
|
3593
3754
|
),
|
|
3594
|
-
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */
|
|
3755
|
+
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
|
|
3595
3756
|
EmptyState,
|
|
3596
3757
|
{
|
|
3597
3758
|
message: "No models available for this team",
|
|
3598
3759
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
3599
3760
|
}
|
|
3600
3761
|
),
|
|
3601
|
-
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */
|
|
3762
|
+
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react10.default.createElement(
|
|
3602
3763
|
EmptyState,
|
|
3603
3764
|
{
|
|
3604
3765
|
message: "No models match your search",
|
|
3605
3766
|
hint: "Try a different search term"
|
|
3606
3767
|
}
|
|
3607
3768
|
),
|
|
3608
|
-
!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(
|
|
3609
3770
|
"span",
|
|
3610
3771
|
{
|
|
3611
3772
|
role: "button",
|
|
@@ -3615,9 +3776,9 @@ var init_ModelsTable = __esm({
|
|
|
3615
3776
|
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
3616
3777
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3617
3778
|
},
|
|
3618
|
-
/* @__PURE__ */
|
|
3619
|
-
)), /* @__PURE__ */
|
|
3620
|
-
|
|
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,
|
|
3621
3782
|
{
|
|
3622
3783
|
component: "span",
|
|
3623
3784
|
role: "button",
|
|
@@ -3627,9 +3788,9 @@ var init_ModelsTable = __esm({
|
|
|
3627
3788
|
sx: quietIconButtonSx("accent"),
|
|
3628
3789
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3629
3790
|
},
|
|
3630
|
-
/* @__PURE__ */
|
|
3631
|
-
)))), /* @__PURE__ */
|
|
3632
|
-
|
|
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,
|
|
3633
3794
|
{
|
|
3634
3795
|
component: "span",
|
|
3635
3796
|
sx: {
|
|
@@ -3640,13 +3801,13 @@ var init_ModelsTable = __esm({
|
|
|
3640
3801
|
fontSize: 11,
|
|
3641
3802
|
fontWeight: 600,
|
|
3642
3803
|
textTransform: "capitalize",
|
|
3643
|
-
bgcolor: (0,
|
|
3804
|
+
bgcolor: (0, import_styles7.alpha)(modeColor(m.mode), 0.1),
|
|
3644
3805
|
color: modeColor(m.mode)
|
|
3645
3806
|
}
|
|
3646
3807
|
},
|
|
3647
3808
|
m.mode
|
|
3648
|
-
)), /* @__PURE__ */
|
|
3649
|
-
/* @__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(
|
|
3650
3811
|
import_Snackbar.default,
|
|
3651
3812
|
{
|
|
3652
3813
|
open: copiedSnackbar,
|
|
@@ -3654,7 +3815,7 @@ var init_ModelsTable = __esm({
|
|
|
3654
3815
|
onClose: () => setCopiedSnackbar(false),
|
|
3655
3816
|
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
3656
3817
|
},
|
|
3657
|
-
/* @__PURE__ */
|
|
3818
|
+
/* @__PURE__ */ import_react10.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
3658
3819
|
)
|
|
3659
3820
|
);
|
|
3660
3821
|
};
|
|
@@ -3682,10 +3843,10 @@ function formatDateTime(iso) {
|
|
|
3682
3843
|
}
|
|
3683
3844
|
function renderAuditLogBody(loading, entries) {
|
|
3684
3845
|
if (loading) {
|
|
3685
|
-
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 })));
|
|
3686
3847
|
}
|
|
3687
3848
|
if (entries.length === 0) {
|
|
3688
|
-
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(
|
|
3689
3850
|
EmptyState,
|
|
3690
3851
|
{
|
|
3691
3852
|
message: "No audit events found",
|
|
@@ -3693,15 +3854,15 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3693
3854
|
}
|
|
3694
3855
|
)));
|
|
3695
3856
|
}
|
|
3696
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
3857
|
+
return entries.map((entry) => /* @__PURE__ */ import_react11.default.createElement(DetailRow, { key: entry.id, entry }));
|
|
3697
3858
|
}
|
|
3698
|
-
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;
|
|
3699
3860
|
var init_AuditLog = __esm({
|
|
3700
3861
|
"src/components/AuditLog.tsx"() {
|
|
3701
3862
|
"use strict";
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3863
|
+
import_react11 = __toESM(require("react"));
|
|
3864
|
+
import_Box11 = __toESM(require("@mui/material/Box"));
|
|
3865
|
+
import_Typography11 = __toESM(require("@mui/material/Typography"));
|
|
3705
3866
|
import_Table6 = __toESM(require("@mui/material/Table"));
|
|
3706
3867
|
import_TableBody6 = __toESM(require("@mui/material/TableBody"));
|
|
3707
3868
|
import_TableCell6 = __toESM(require("@mui/material/TableCell"));
|
|
@@ -3715,7 +3876,7 @@ var init_AuditLog = __esm({
|
|
|
3715
3876
|
import_Collapse3 = __toESM(require("@mui/material/Collapse"));
|
|
3716
3877
|
import_IconButton6 = __toESM(require("@mui/material/IconButton"));
|
|
3717
3878
|
import_Alert5 = __toESM(require("@mui/material/Alert"));
|
|
3718
|
-
|
|
3879
|
+
import_styles8 = require("@mui/material/styles");
|
|
3719
3880
|
import_icons_material8 = require("@mui/icons-material");
|
|
3720
3881
|
import_react_use2 = require("react-use");
|
|
3721
3882
|
init_ui();
|
|
@@ -3734,9 +3895,9 @@ var init_AuditLog = __esm({
|
|
|
3734
3895
|
LiteLLM_UserTable: "User"
|
|
3735
3896
|
};
|
|
3736
3897
|
DetailRow = ({ entry }) => {
|
|
3737
|
-
const [open, setOpen] = (0,
|
|
3898
|
+
const [open, setOpen] = (0, import_react11.useState)(false);
|
|
3738
3899
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3739
|
-
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(
|
|
3740
3901
|
import_IconButton6.default,
|
|
3741
3902
|
{
|
|
3742
3903
|
size: "small",
|
|
@@ -3749,9 +3910,9 @@ var init_AuditLog = __esm({
|
|
|
3749
3910
|
transition: theme.transitions.create("transform")
|
|
3750
3911
|
})
|
|
3751
3912
|
},
|
|
3752
|
-
/* @__PURE__ */
|
|
3753
|
-
)), /* @__PURE__ */
|
|
3754
|
-
|
|
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,
|
|
3755
3916
|
{
|
|
3756
3917
|
variant: "body2",
|
|
3757
3918
|
component: "code",
|
|
@@ -3760,8 +3921,8 @@ var init_AuditLog = __esm({
|
|
|
3760
3921
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3761
3922
|
},
|
|
3762
3923
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3763
|
-
)), /* @__PURE__ */
|
|
3764
|
-
|
|
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,
|
|
3765
3926
|
{
|
|
3766
3927
|
display: "flex",
|
|
3767
3928
|
gap: 2,
|
|
@@ -3770,11 +3931,11 @@ var init_AuditLog = __esm({
|
|
|
3770
3931
|
p: 2,
|
|
3771
3932
|
mb: 1.5,
|
|
3772
3933
|
borderRadius: 1.5,
|
|
3773
|
-
bgcolor: (0,
|
|
3934
|
+
bgcolor: (0, import_styles8.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
3774
3935
|
})
|
|
3775
3936
|
},
|
|
3776
|
-
entry.before_value && /* @__PURE__ */
|
|
3777
|
-
|
|
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,
|
|
3778
3939
|
{
|
|
3779
3940
|
variant: "caption",
|
|
3780
3941
|
color: "text.secondary",
|
|
@@ -3783,9 +3944,9 @@ var init_AuditLog = __esm({
|
|
|
3783
3944
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3784
3945
|
},
|
|
3785
3946
|
"Before"
|
|
3786
|
-
), /* @__PURE__ */
|
|
3787
|
-
entry.updated_values && /* @__PURE__ */
|
|
3788
|
-
|
|
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,
|
|
3789
3950
|
{
|
|
3790
3951
|
variant: "caption",
|
|
3791
3952
|
color: "text.secondary",
|
|
@@ -3794,14 +3955,14 @@ var init_AuditLog = __esm({
|
|
|
3794
3955
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3795
3956
|
},
|
|
3796
3957
|
"After"
|
|
3797
|
-
), /* @__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)))
|
|
3798
3959
|
)))));
|
|
3799
3960
|
};
|
|
3800
3961
|
AuditLog = ({ api }) => {
|
|
3801
|
-
const [page, setPage] = (0,
|
|
3802
|
-
const [pageSize, setPageSize] = (0,
|
|
3803
|
-
const [filters, setFilters] = (0,
|
|
3804
|
-
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)(
|
|
3805
3966
|
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
3806
3967
|
[page, pageSize, filters]
|
|
3807
3968
|
);
|
|
@@ -3811,13 +3972,13 @@ var init_AuditLog = __esm({
|
|
|
3811
3972
|
);
|
|
3812
3973
|
const entries = value?.audit_logs ?? [];
|
|
3813
3974
|
const total = value?.total ?? 0;
|
|
3814
|
-
return /* @__PURE__ */
|
|
3975
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
3815
3976
|
SectionCard,
|
|
3816
3977
|
{
|
|
3817
3978
|
title: "Audit Log",
|
|
3818
3979
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
3819
3980
|
flush: true,
|
|
3820
|
-
actions: /* @__PURE__ */
|
|
3981
|
+
actions: /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(
|
|
3821
3982
|
import_TextField6.default,
|
|
3822
3983
|
{
|
|
3823
3984
|
size: "small",
|
|
@@ -3830,12 +3991,12 @@ var init_AuditLog = __esm({
|
|
|
3830
3991
|
},
|
|
3831
3992
|
sx: { minWidth: 150 }
|
|
3832
3993
|
},
|
|
3833
|
-
/* @__PURE__ */
|
|
3834
|
-
/* @__PURE__ */
|
|
3835
|
-
/* @__PURE__ */
|
|
3836
|
-
/* @__PURE__ */
|
|
3837
|
-
/* @__PURE__ */
|
|
3838
|
-
), /* @__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(
|
|
3839
4000
|
import_TextField6.default,
|
|
3840
4001
|
{
|
|
3841
4002
|
size: "small",
|
|
@@ -3848,13 +4009,13 @@ var init_AuditLog = __esm({
|
|
|
3848
4009
|
},
|
|
3849
4010
|
sx: { minWidth: 150 }
|
|
3850
4011
|
},
|
|
3851
|
-
/* @__PURE__ */
|
|
3852
|
-
/* @__PURE__ */
|
|
3853
|
-
/* @__PURE__ */
|
|
3854
|
-
/* @__PURE__ */
|
|
3855
|
-
/* @__PURE__ */
|
|
3856
|
-
/* @__PURE__ */
|
|
3857
|
-
), /* @__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(
|
|
3858
4019
|
import_TextField6.default,
|
|
3859
4020
|
{
|
|
3860
4021
|
size: "small",
|
|
@@ -3868,9 +4029,9 @@ var init_AuditLog = __esm({
|
|
|
3868
4029
|
}
|
|
3869
4030
|
))
|
|
3870
4031
|
},
|
|
3871
|
-
error && /* @__PURE__ */
|
|
3872
|
-
/* @__PURE__ */
|
|
3873
|
-
/* @__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(
|
|
3874
4035
|
import_TablePagination.default,
|
|
3875
4036
|
{
|
|
3876
4037
|
component: "div",
|
|
@@ -3939,17 +4100,17 @@ function initDateRange() {
|
|
|
3939
4100
|
else start.setDate(start.getDate() - 7);
|
|
3940
4101
|
return { start, end };
|
|
3941
4102
|
}
|
|
3942
|
-
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;
|
|
3943
4104
|
var init_LiteLLMPage = __esm({
|
|
3944
4105
|
"src/components/LiteLLMPage.tsx"() {
|
|
3945
4106
|
"use strict";
|
|
3946
|
-
|
|
3947
|
-
|
|
4107
|
+
import_react12 = __toESM(require("react"));
|
|
4108
|
+
import_Box12 = __toESM(require("@mui/material/Box"));
|
|
3948
4109
|
import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
|
|
3949
4110
|
import_Alert6 = __toESM(require("@mui/material/Alert"));
|
|
3950
4111
|
import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
|
|
3951
|
-
|
|
3952
|
-
|
|
4112
|
+
import_Typography12 = __toESM(require("@mui/material/Typography"));
|
|
4113
|
+
import_Paper8 = __toESM(require("@mui/material/Paper"));
|
|
3953
4114
|
import_Tabs2 = __toESM(require("@mui/material/Tabs"));
|
|
3954
4115
|
import_Tab2 = __toESM(require("@mui/material/Tab"));
|
|
3955
4116
|
import_Grid2 = __toESM(require("@mui/material/Grid"));
|
|
@@ -3973,8 +4134,8 @@ var init_LiteLLMPage = __esm({
|
|
|
3973
4134
|
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
3974
4135
|
LiteLLMPage = () => {
|
|
3975
4136
|
const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
|
|
3976
|
-
const [dateRange, setDateRange] = (0,
|
|
3977
|
-
const [currentPreset, setCurrentPreset] = (0,
|
|
4137
|
+
const [dateRange, setDateRange] = (0, import_react12.useState)(initDateRange);
|
|
4138
|
+
const [currentPreset, setCurrentPreset] = (0, import_react12.useState)(() => {
|
|
3978
4139
|
try {
|
|
3979
4140
|
return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
|
|
3980
4141
|
} catch {
|
|
@@ -3982,11 +4143,26 @@ var init_LiteLLMPage = __esm({
|
|
|
3982
4143
|
}
|
|
3983
4144
|
});
|
|
3984
4145
|
const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
|
|
3985
|
-
const [activeTab, setActiveTab] = (0,
|
|
4146
|
+
const [activeTab, setActiveTab] = (0, import_react12.useState)(() => {
|
|
3986
4147
|
const t = searchParams.get("tab");
|
|
3987
4148
|
return isPageTab(t) ? t : "overview";
|
|
3988
4149
|
});
|
|
3989
|
-
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)(
|
|
3990
4166
|
(tab) => {
|
|
3991
4167
|
setActiveTab(tab);
|
|
3992
4168
|
setSearchParams(
|
|
@@ -4000,11 +4176,10 @@ var init_LiteLLMPage = __esm({
|
|
|
4000
4176
|
},
|
|
4001
4177
|
[setSearchParams]
|
|
4002
4178
|
);
|
|
4003
|
-
const [snackbar, setSnackbar] = (0,
|
|
4004
|
-
const [
|
|
4005
|
-
const [
|
|
4006
|
-
const [
|
|
4007
|
-
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)({});
|
|
4008
4183
|
const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
|
|
4009
4184
|
() => api.getUserInfo(),
|
|
4010
4185
|
[api]
|
|
@@ -4048,7 +4223,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4048
4223
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
4049
4224
|
[api, objectPermsEnabled]
|
|
4050
4225
|
);
|
|
4051
|
-
const teams = (0,
|
|
4226
|
+
const teams = (0, import_react12.useMemo)(() => {
|
|
4052
4227
|
if (!allTeams?.length) return [];
|
|
4053
4228
|
if (!userInfo) return allTeams;
|
|
4054
4229
|
const userId = userInfo.user_id;
|
|
@@ -4065,7 +4240,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4065
4240
|
const endDate = dateRange.end.toISOString().split("T")[0];
|
|
4066
4241
|
return api.getUsage(startDate, endDate);
|
|
4067
4242
|
}, [api, dateRange]);
|
|
4068
|
-
const handleDateRangeChange = (0,
|
|
4243
|
+
const handleDateRangeChange = (0, import_react12.useCallback)((range, preset) => {
|
|
4069
4244
|
setDateRange(range);
|
|
4070
4245
|
if (preset) {
|
|
4071
4246
|
setCurrentPreset(preset);
|
|
@@ -4073,7 +4248,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4073
4248
|
setTeamUsageCache({});
|
|
4074
4249
|
setTeamUsageLoading({});
|
|
4075
4250
|
}, [setDateRange, setCurrentPreset]);
|
|
4076
|
-
const loadTeamUsage = (0,
|
|
4251
|
+
const loadTeamUsage = (0, import_react12.useCallback)(async (teamId) => {
|
|
4077
4252
|
if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
|
|
4078
4253
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
|
|
4079
4254
|
try {
|
|
@@ -4087,7 +4262,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4087
4262
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
|
|
4088
4263
|
}
|
|
4089
4264
|
}, [api, dateRange, teamUsageCache, teamUsageLoading]);
|
|
4090
|
-
const allowedModels = (0,
|
|
4265
|
+
const allowedModels = (0, import_react12.useMemo)(() => {
|
|
4091
4266
|
if (!allModels?.length) return [];
|
|
4092
4267
|
const userModels = userInfo?.models;
|
|
4093
4268
|
const teamModels = teams?.flatMap((t) => t.models ?? []);
|
|
@@ -4100,7 +4275,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4100
4275
|
]);
|
|
4101
4276
|
return allModels.filter((m) => allowed.has(m.model_name));
|
|
4102
4277
|
}, [allModels, userInfo, teams]);
|
|
4103
|
-
const handleGenerateKey = (0,
|
|
4278
|
+
const handleGenerateKey = (0, import_react12.useCallback)(
|
|
4104
4279
|
async (request) => {
|
|
4105
4280
|
try {
|
|
4106
4281
|
const response = await api.generateKey(request);
|
|
@@ -4114,7 +4289,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4114
4289
|
},
|
|
4115
4290
|
[api, refreshKeys]
|
|
4116
4291
|
);
|
|
4117
|
-
const handleUpdateKey = (0,
|
|
4292
|
+
const handleUpdateKey = (0, import_react12.useCallback)(
|
|
4118
4293
|
async (keyId, request) => {
|
|
4119
4294
|
try {
|
|
4120
4295
|
await api.updateKey(keyId, request);
|
|
@@ -4127,7 +4302,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4127
4302
|
},
|
|
4128
4303
|
[api, refreshKeys]
|
|
4129
4304
|
);
|
|
4130
|
-
const handleBlockKey = (0,
|
|
4305
|
+
const handleBlockKey = (0, import_react12.useCallback)(
|
|
4131
4306
|
async (keyId) => {
|
|
4132
4307
|
try {
|
|
4133
4308
|
await api.blockKey(keyId);
|
|
@@ -4139,7 +4314,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4139
4314
|
},
|
|
4140
4315
|
[api, refreshKeys]
|
|
4141
4316
|
);
|
|
4142
|
-
const handleUnblockKey = (0,
|
|
4317
|
+
const handleUnblockKey = (0, import_react12.useCallback)(
|
|
4143
4318
|
async (keyId) => {
|
|
4144
4319
|
try {
|
|
4145
4320
|
await api.unblockKey(keyId);
|
|
@@ -4151,7 +4326,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4151
4326
|
},
|
|
4152
4327
|
[api, refreshKeys]
|
|
4153
4328
|
);
|
|
4154
|
-
const handleResetKeySpend = (0,
|
|
4329
|
+
const handleResetKeySpend = (0, import_react12.useCallback)(
|
|
4155
4330
|
async (keyId) => {
|
|
4156
4331
|
try {
|
|
4157
4332
|
await api.resetKeySpend(keyId);
|
|
@@ -4163,7 +4338,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4163
4338
|
},
|
|
4164
4339
|
[api, refreshKeys]
|
|
4165
4340
|
);
|
|
4166
|
-
const handleDeleteKey = (0,
|
|
4341
|
+
const handleDeleteKey = (0, import_react12.useCallback)(
|
|
4167
4342
|
async (keyId) => {
|
|
4168
4343
|
try {
|
|
4169
4344
|
await api.deleteKey(keyId);
|
|
@@ -4180,7 +4355,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4180
4355
|
},
|
|
4181
4356
|
[api, refreshKeys]
|
|
4182
4357
|
);
|
|
4183
|
-
const handlePruneExpiredKeys = (0,
|
|
4358
|
+
const handlePruneExpiredKeys = (0, import_react12.useCallback)(
|
|
4184
4359
|
async () => {
|
|
4185
4360
|
try {
|
|
4186
4361
|
const result = await api.pruneExpiredKeys();
|
|
@@ -4195,14 +4370,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4195
4370
|
);
|
|
4196
4371
|
const isInitialLoading = userLoading && !userInfo;
|
|
4197
4372
|
if (isInitialLoading) {
|
|
4198
|
-
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));
|
|
4199
4374
|
}
|
|
4200
4375
|
if (userError || !userInfo) {
|
|
4201
4376
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
4202
4377
|
const hint = userError?.body?.hint;
|
|
4203
|
-
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.")));
|
|
4204
4379
|
}
|
|
4205
|
-
const pageTabs = /* @__PURE__ */
|
|
4380
|
+
const pageTabs = /* @__PURE__ */ import_react12.default.createElement(
|
|
4206
4381
|
import_Tabs2.default,
|
|
4207
4382
|
{
|
|
4208
4383
|
value: activeTab,
|
|
@@ -4215,13 +4390,13 @@ var init_LiteLLMPage = __esm({
|
|
|
4215
4390
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
4216
4391
|
}
|
|
4217
4392
|
},
|
|
4218
|
-
/* @__PURE__ */
|
|
4219
|
-
/* @__PURE__ */
|
|
4220
|
-
/* @__PURE__ */
|
|
4221
|
-
/* @__PURE__ */
|
|
4222
|
-
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" })
|
|
4223
4398
|
);
|
|
4224
|
-
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(
|
|
4225
4400
|
DashboardHeader,
|
|
4226
4401
|
{
|
|
4227
4402
|
userInfo,
|
|
@@ -4231,7 +4406,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4231
4406
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4232
4407
|
tabs: pageTabs
|
|
4233
4408
|
}
|
|
4234
|
-
), 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(
|
|
4235
4410
|
UsageStats,
|
|
4236
4411
|
{
|
|
4237
4412
|
usage: usage ?? null,
|
|
@@ -4242,7 +4417,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4242
4417
|
loading: usageLoading,
|
|
4243
4418
|
userInfo
|
|
4244
4419
|
}
|
|
4245
|
-
)), /* @__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(
|
|
4246
4421
|
KeysTable,
|
|
4247
4422
|
{
|
|
4248
4423
|
keys: keys ?? [],
|
|
@@ -4261,7 +4436,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4261
4436
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
4262
4437
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
4263
4438
|
const visibleTeams = Array.from(teamsById.values());
|
|
4264
|
-
return /* @__PURE__ */
|
|
4439
|
+
return /* @__PURE__ */ import_react12.default.createElement(
|
|
4265
4440
|
TeamUsage,
|
|
4266
4441
|
{
|
|
4267
4442
|
teams: visibleTeams,
|
|
@@ -4277,14 +4452,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4277
4452
|
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
4278
4453
|
}
|
|
4279
4454
|
);
|
|
4280
|
-
})(), activeTab === "models" && /* @__PURE__ */
|
|
4455
|
+
})(), activeTab === "models" && /* @__PURE__ */ import_react12.default.createElement(
|
|
4281
4456
|
ModelsTable,
|
|
4282
4457
|
{
|
|
4283
4458
|
allModels: allModels ?? [],
|
|
4284
4459
|
teams: teams ?? [],
|
|
4285
4460
|
loading: modelsLoading
|
|
4286
4461
|
}
|
|
4287
|
-
), 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(
|
|
4288
4463
|
GenerateKeyDialog,
|
|
4289
4464
|
{
|
|
4290
4465
|
open: generateDialogOpen,
|
|
@@ -4297,7 +4472,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4297
4472
|
onGenerateKey: handleGenerateKey,
|
|
4298
4473
|
onGetConfig: () => api.getConfig()
|
|
4299
4474
|
}
|
|
4300
|
-
), /* @__PURE__ */
|
|
4475
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
4301
4476
|
ManageTeamDialog,
|
|
4302
4477
|
{
|
|
4303
4478
|
open: !!manageTeam,
|
|
@@ -4359,7 +4534,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4359
4534
|
refreshTeams();
|
|
4360
4535
|
}
|
|
4361
4536
|
}
|
|
4362
|
-
), /* @__PURE__ */
|
|
4537
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
4363
4538
|
import_Snackbar2.default,
|
|
4364
4539
|
{
|
|
4365
4540
|
open: !!snackbar,
|
|
@@ -4367,7 +4542,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4367
4542
|
onClose: () => setSnackbar(null),
|
|
4368
4543
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
4369
4544
|
},
|
|
4370
|
-
snackbar ? /* @__PURE__ */
|
|
4545
|
+
snackbar ? /* @__PURE__ */ import_react12.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
4371
4546
|
));
|
|
4372
4547
|
};
|
|
4373
4548
|
}
|
|
@@ -4379,6 +4554,7 @@ __export(index_exports, {
|
|
|
4379
4554
|
DashboardHeader: () => DashboardHeader,
|
|
4380
4555
|
GenerateKeyDialog: () => GenerateKeyDialog,
|
|
4381
4556
|
KeysTable: () => KeysTable,
|
|
4557
|
+
LiteLLMBudgetGauges: () => LiteLLMBudgetGauges,
|
|
4382
4558
|
LiteLLMBudgetWidget: () => LiteLLMBudgetWidget,
|
|
4383
4559
|
LiteLLMHomeWidget: () => LiteLLMHomeWidget,
|
|
4384
4560
|
LiteLLMPage: () => LiteLLMPage,
|
|
@@ -4397,7 +4573,7 @@ __export(index_exports, {
|
|
|
4397
4573
|
module.exports = __toCommonJS(index_exports);
|
|
4398
4574
|
|
|
4399
4575
|
// src/plugin.tsx
|
|
4400
|
-
var
|
|
4576
|
+
var import_react13 = __toESM(require("react"));
|
|
4401
4577
|
var import_icons_material9 = require("@mui/icons-material");
|
|
4402
4578
|
var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
|
|
4403
4579
|
init_api();
|
|
@@ -4412,10 +4588,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
|
|
|
4412
4588
|
params: {
|
|
4413
4589
|
path: "/litellm",
|
|
4414
4590
|
title: "LiteLLM",
|
|
4415
|
-
icon: /* @__PURE__ */
|
|
4591
|
+
icon: /* @__PURE__ */ import_react13.default.createElement(import_icons_material9.TrendingUp, null),
|
|
4416
4592
|
loader: async () => {
|
|
4417
4593
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
4418
|
-
return /* @__PURE__ */
|
|
4594
|
+
return /* @__PURE__ */ import_react13.default.createElement(LiteLLMPage2, null);
|
|
4419
4595
|
}
|
|
4420
4596
|
}
|
|
4421
4597
|
});
|
|
@@ -4432,10 +4608,10 @@ init_UsageStats();
|
|
|
4432
4608
|
init_TeamUsage();
|
|
4433
4609
|
|
|
4434
4610
|
// src/components/LiteLLMHomeWidget.tsx
|
|
4435
|
-
var
|
|
4436
|
-
var
|
|
4437
|
-
var
|
|
4438
|
-
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"));
|
|
4439
4615
|
var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
|
|
4440
4616
|
var import_Select2 = __toESM(require("@mui/material/Select"));
|
|
4441
4617
|
var import_MenuItem6 = __toESM(require("@mui/material/MenuItem"));
|
|
@@ -4458,19 +4634,19 @@ function presetToDateRange(preset) {
|
|
|
4458
4634
|
}
|
|
4459
4635
|
return { start, end };
|
|
4460
4636
|
}
|
|
4461
|
-
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));
|
|
4462
4638
|
var LiteLLMHomeWidget = ({
|
|
4463
4639
|
defaultPeriod = "7d",
|
|
4464
4640
|
title = "LiteLLM Usage"
|
|
4465
4641
|
}) => {
|
|
4466
4642
|
const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
|
|
4467
|
-
const [period, setPeriod] = (0,
|
|
4468
|
-
const [loading, setLoading] = (0,
|
|
4469
|
-
const [usageError, setUsageError] = (0,
|
|
4470
|
-
const [keysError, setKeysError] = (0,
|
|
4471
|
-
const [usage, setUsage] = (0,
|
|
4472
|
-
const [keys, setKeys] = (0,
|
|
4473
|
-
(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)(() => {
|
|
4474
4650
|
let cancelled = false;
|
|
4475
4651
|
setLoading(true);
|
|
4476
4652
|
setUsageError(null);
|
|
@@ -4507,17 +4683,17 @@ var LiteLLMHomeWidget = ({
|
|
|
4507
4683
|
spend: d.spend
|
|
4508
4684
|
}));
|
|
4509
4685
|
const hasSparkline = dailyData.length > 0;
|
|
4510
|
-
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(
|
|
4511
4687
|
import_Select2.default,
|
|
4512
4688
|
{
|
|
4513
4689
|
value: period,
|
|
4514
4690
|
onChange: (e) => setPeriod(e.target.value),
|
|
4515
4691
|
displayEmpty: true
|
|
4516
4692
|
},
|
|
4517
|
-
/* @__PURE__ */
|
|
4518
|
-
/* @__PURE__ */
|
|
4519
|
-
/* @__PURE__ */
|
|
4520
|
-
))), 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(
|
|
4521
4697
|
import_recharts3.Area,
|
|
4522
4698
|
{
|
|
4523
4699
|
type: "monotone",
|
|
@@ -4533,6 +4709,307 @@ var LiteLLMHomeWidget = ({
|
|
|
4533
4709
|
|
|
4534
4710
|
// src/index.ts
|
|
4535
4711
|
init_LiteLLMBudgetWidget();
|
|
4712
|
+
|
|
4713
|
+
// src/components/LiteLLMBudgetGauges.tsx
|
|
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"));
|
|
4719
|
+
var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress"));
|
|
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");
|
|
4723
|
+
var import_core_plugin_api6 = require("@backstage/core-plugin-api");
|
|
4724
|
+
var import_core_components2 = require("@backstage/core-components");
|
|
4725
|
+
init_api();
|
|
4726
|
+
init_format();
|
|
4727
|
+
init_ui();
|
|
4728
|
+
init_BudgetLimitList();
|
|
4729
|
+
init_budget();
|
|
4730
|
+
var MODULE_PATH = "/litellm";
|
|
4731
|
+
var LEVEL = {
|
|
4732
|
+
key: { name: "Key", none: "No key budget" },
|
|
4733
|
+
user: { name: "User", none: "No personal budget" },
|
|
4734
|
+
team: { name: "Team", none: "No team budget" }
|
|
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
|
+
};
|
|
4742
|
+
function fmtUsdShort(n) {
|
|
4743
|
+
const v = n ?? 0;
|
|
4744
|
+
if (v >= 100 && Number.isInteger(v)) return `$${v}`;
|
|
4745
|
+
return fmtUsd(v);
|
|
4746
|
+
}
|
|
4747
|
+
function spendCaption(limit) {
|
|
4748
|
+
if (limit.hidden) return "hidden by admin";
|
|
4749
|
+
return `${fmtUsdShort(limit.spend)} / ${fmtUsdShort(limit.budget)}`;
|
|
4750
|
+
}
|
|
4751
|
+
function resetLabel(limit) {
|
|
4752
|
+
const window2 = fmtBudgetDuration(limit.budgetDuration);
|
|
4753
|
+
return window2 ? `resets ${window2}` : "never resets";
|
|
4754
|
+
}
|
|
4755
|
+
var LevelGauge = ({
|
|
4756
|
+
gauge,
|
|
4757
|
+
size,
|
|
4758
|
+
keysHref
|
|
4759
|
+
}) => {
|
|
4760
|
+
const { name, none } = LEVEL[gauge.kind];
|
|
4761
|
+
const limit = gauge.limit;
|
|
4762
|
+
const tone = limit ? budgetTone2(limit.pct) : "neutral";
|
|
4763
|
+
const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
|
|
4764
|
+
return /* @__PURE__ */ import_react15.default.createElement(
|
|
4765
|
+
import_Box14.default,
|
|
4766
|
+
{
|
|
4767
|
+
sx: {
|
|
4768
|
+
display: "flex",
|
|
4769
|
+
flexDirection: "column",
|
|
4770
|
+
alignItems: "center",
|
|
4771
|
+
minWidth: 0,
|
|
4772
|
+
flex: 1
|
|
4773
|
+
}
|
|
4774
|
+
},
|
|
4775
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
4776
|
+
Gauge,
|
|
4777
|
+
{
|
|
4778
|
+
value: limit?.pct ?? 0,
|
|
4779
|
+
tone,
|
|
4780
|
+
size,
|
|
4781
|
+
label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
|
|
4782
|
+
}
|
|
4783
|
+
),
|
|
4784
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
4785
|
+
import_Typography14.default,
|
|
4786
|
+
{
|
|
4787
|
+
sx: (theme) => ({
|
|
4788
|
+
mt: 0.75,
|
|
4789
|
+
fontSize: 10.5,
|
|
4790
|
+
fontWeight: 700,
|
|
4791
|
+
letterSpacing: "0.08em",
|
|
4792
|
+
textTransform: "uppercase",
|
|
4793
|
+
color: tone === "neutral" ? theme.palette.text.secondary : void 0
|
|
4794
|
+
})
|
|
4795
|
+
},
|
|
4796
|
+
name
|
|
4797
|
+
),
|
|
4798
|
+
limit ? /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
|
|
4799
|
+
import_Typography14.default,
|
|
4800
|
+
{
|
|
4801
|
+
variant: "caption",
|
|
4802
|
+
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
|
|
4803
|
+
sx: {
|
|
4804
|
+
mt: 0.25,
|
|
4805
|
+
fontSize: 11,
|
|
4806
|
+
fontWeight: 600,
|
|
4807
|
+
maxWidth: "100%",
|
|
4808
|
+
overflow: "hidden",
|
|
4809
|
+
textOverflow: "ellipsis",
|
|
4810
|
+
whiteSpace: "nowrap"
|
|
4811
|
+
}
|
|
4812
|
+
},
|
|
4813
|
+
limit.label
|
|
4814
|
+
), /* @__PURE__ */ import_react15.default.createElement(
|
|
4815
|
+
import_Typography14.default,
|
|
4816
|
+
{
|
|
4817
|
+
variant: "caption",
|
|
4818
|
+
color: "text.secondary",
|
|
4819
|
+
sx: {
|
|
4820
|
+
fontSize: 10.5,
|
|
4821
|
+
fontVariantNumeric: "tabular-nums",
|
|
4822
|
+
textAlign: "center"
|
|
4823
|
+
}
|
|
4824
|
+
},
|
|
4825
|
+
spendCaption(limit)
|
|
4826
|
+
), extra > 0 ? /* @__PURE__ */ import_react15.default.createElement(
|
|
4827
|
+
import_Typography14.default,
|
|
4828
|
+
{
|
|
4829
|
+
component: import_core_components2.Link,
|
|
4830
|
+
to: keysHref,
|
|
4831
|
+
variant: "caption",
|
|
4832
|
+
sx: {
|
|
4833
|
+
fontSize: 10.5,
|
|
4834
|
+
color: "primary.main",
|
|
4835
|
+
textDecoration: "none",
|
|
4836
|
+
"&:hover": { textDecoration: "underline" }
|
|
4837
|
+
}
|
|
4838
|
+
},
|
|
4839
|
+
"+",
|
|
4840
|
+
extra,
|
|
4841
|
+
" more"
|
|
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,
|
|
4844
|
+
{
|
|
4845
|
+
variant: "caption",
|
|
4846
|
+
color: "text.secondary",
|
|
4847
|
+
sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
|
|
4848
|
+
},
|
|
4849
|
+
none
|
|
4850
|
+
)
|
|
4851
|
+
);
|
|
4852
|
+
};
|
|
4853
|
+
var LiteLLMBudgetGauges = ({
|
|
4854
|
+
title = "Budget",
|
|
4855
|
+
size = 72,
|
|
4856
|
+
keysHref,
|
|
4857
|
+
moduleHref = MODULE_PATH,
|
|
4858
|
+
onCreateKey,
|
|
4859
|
+
ctas,
|
|
4860
|
+
maxExpandedKeys = 8,
|
|
4861
|
+
expanded,
|
|
4862
|
+
defaultExpanded = false,
|
|
4863
|
+
onExpandedChange,
|
|
4864
|
+
action
|
|
4865
|
+
}) => {
|
|
4866
|
+
const api = (0, import_core_plugin_api6.useApi)(liteLlmApiRef);
|
|
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)(() => {
|
|
4882
|
+
let cancelled = false;
|
|
4883
|
+
setLoading(true);
|
|
4884
|
+
setError(null);
|
|
4885
|
+
Promise.allSettled([api.getUserInfo(), api.getTeams(), api.listKeys()]).then(
|
|
4886
|
+
([userResult, teamsResult, keysResult]) => {
|
|
4887
|
+
if (cancelled) return;
|
|
4888
|
+
if (userResult.status === "fulfilled") {
|
|
4889
|
+
setUser(userResult.value);
|
|
4890
|
+
} else {
|
|
4891
|
+
setUser(null);
|
|
4892
|
+
setError(
|
|
4893
|
+
userResult.reason?.message ?? "Failed to load your LiteLLM profile"
|
|
4894
|
+
);
|
|
4895
|
+
}
|
|
4896
|
+
setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
|
|
4897
|
+
setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
|
|
4898
|
+
setLoading(false);
|
|
4899
|
+
}
|
|
4900
|
+
);
|
|
4901
|
+
return () => {
|
|
4902
|
+
cancelled = true;
|
|
4903
|
+
};
|
|
4904
|
+
}, [api]);
|
|
4905
|
+
const summary = (0, import_react15.useMemo)(
|
|
4906
|
+
() => buildBudgetSummary(user, teams, keys, keys.length),
|
|
4907
|
+
[user, teams, keys]
|
|
4908
|
+
);
|
|
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
|
+
);
|
|
4919
|
+
const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
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(
|
|
4969
|
+
StatusPill,
|
|
4970
|
+
{
|
|
4971
|
+
label: summaryText,
|
|
4972
|
+
tone: budgetTone2(headline.closest?.pct ?? 0),
|
|
4973
|
+
dot: headline.count > 0
|
|
4974
|
+
}
|
|
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,
|
|
4996
|
+
{
|
|
4997
|
+
sx: (theme) => ({
|
|
4998
|
+
mt: 2,
|
|
4999
|
+
pt: 2,
|
|
5000
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
5001
|
+
display: "flex",
|
|
5002
|
+
alignItems: "center",
|
|
5003
|
+
gap: 1,
|
|
5004
|
+
flexWrap: "wrap"
|
|
5005
|
+
})
|
|
5006
|
+
},
|
|
5007
|
+
resolvedCtas.map(renderCta),
|
|
5008
|
+
action
|
|
5009
|
+
));
|
|
5010
|
+
};
|
|
5011
|
+
|
|
5012
|
+
// src/index.ts
|
|
4536
5013
|
init_GenerateKeyDialog();
|
|
4537
5014
|
init_ManageTeamDialog();
|
|
4538
5015
|
init_api();
|