@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.esm.js
CHANGED
|
@@ -319,7 +319,7 @@ function SegmentedControl({
|
|
|
319
319
|
})
|
|
320
320
|
);
|
|
321
321
|
}
|
|
322
|
-
var MONTHS, fmtDateShort, fmtCompact, fmtUsdCompact, CHART_COLORS, SERIES, OTHER_COLOR, ChartTooltip, SeriesLegend, StatusPill, TagChip, SectionCard, ChartCard, MetricStrip, Stat, Meter, dataTableSx, quietIconButtonSx, EmptyState;
|
|
322
|
+
var MONTHS, fmtDateShort, fmtCompact, fmtUsdCompact, CHART_COLORS, SERIES, OTHER_COLOR, ChartTooltip, SeriesLegend, StatusPill, TagChip, SectionCard, ChartCard, MetricStrip, Stat, Meter, Gauge, dataTableSx, quietIconButtonSx, EmptyState;
|
|
323
323
|
var init_ui = __esm({
|
|
324
324
|
"src/components/ui.tsx"() {
|
|
325
325
|
"use strict";
|
|
@@ -683,6 +683,102 @@ var init_ui = __esm({
|
|
|
683
683
|
}
|
|
684
684
|
)
|
|
685
685
|
);
|
|
686
|
+
Gauge = ({ value, tone = "accent", size = 72, thickness = 6, label, caption }) => {
|
|
687
|
+
const clamped = Math.max(0, Math.min(100, value ?? 0));
|
|
688
|
+
const r = (size - thickness) / 2;
|
|
689
|
+
const c = 2 * Math.PI * r;
|
|
690
|
+
const dash = clamped / 100 * c;
|
|
691
|
+
return /* @__PURE__ */ React.createElement(
|
|
692
|
+
Box,
|
|
693
|
+
{
|
|
694
|
+
sx: (theme) => ({
|
|
695
|
+
position: "relative",
|
|
696
|
+
width: size,
|
|
697
|
+
height: size,
|
|
698
|
+
flexShrink: 0,
|
|
699
|
+
color: toneColor(theme, tone)
|
|
700
|
+
})
|
|
701
|
+
},
|
|
702
|
+
/* @__PURE__ */ React.createElement(
|
|
703
|
+
Box,
|
|
704
|
+
{
|
|
705
|
+
component: "svg",
|
|
706
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
707
|
+
width: size,
|
|
708
|
+
height: size,
|
|
709
|
+
sx: { display: "block", transform: "rotate(-90deg)" }
|
|
710
|
+
},
|
|
711
|
+
/* @__PURE__ */ React.createElement(
|
|
712
|
+
Box,
|
|
713
|
+
{
|
|
714
|
+
component: "circle",
|
|
715
|
+
cx: size / 2,
|
|
716
|
+
cy: size / 2,
|
|
717
|
+
r,
|
|
718
|
+
fill: "none",
|
|
719
|
+
strokeWidth: thickness,
|
|
720
|
+
sx: (theme) => ({
|
|
721
|
+
stroke: alpha(
|
|
722
|
+
theme.palette.text.primary,
|
|
723
|
+
theme.palette.mode === "dark" ? 0.14 : 0.09
|
|
724
|
+
)
|
|
725
|
+
})
|
|
726
|
+
}
|
|
727
|
+
),
|
|
728
|
+
/* @__PURE__ */ React.createElement(
|
|
729
|
+
Box,
|
|
730
|
+
{
|
|
731
|
+
component: "circle",
|
|
732
|
+
cx: size / 2,
|
|
733
|
+
cy: size / 2,
|
|
734
|
+
r,
|
|
735
|
+
fill: "none",
|
|
736
|
+
strokeWidth: thickness,
|
|
737
|
+
strokeLinecap: "round",
|
|
738
|
+
strokeDasharray: `${dash} ${c - dash}`,
|
|
739
|
+
sx: (theme) => ({
|
|
740
|
+
stroke: "currentColor",
|
|
741
|
+
transition: theme.transitions.create("stroke-dasharray")
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
)
|
|
745
|
+
),
|
|
746
|
+
/* @__PURE__ */ React.createElement(
|
|
747
|
+
Box,
|
|
748
|
+
{
|
|
749
|
+
sx: {
|
|
750
|
+
position: "absolute",
|
|
751
|
+
inset: 0,
|
|
752
|
+
display: "flex",
|
|
753
|
+
flexDirection: "column",
|
|
754
|
+
alignItems: "center",
|
|
755
|
+
justifyContent: "center",
|
|
756
|
+
lineHeight: 1
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
/* @__PURE__ */ React.createElement(
|
|
760
|
+
Typography,
|
|
761
|
+
{
|
|
762
|
+
sx: {
|
|
763
|
+
fontSize: size <= 64 ? 15 : 18,
|
|
764
|
+
fontWeight: 700,
|
|
765
|
+
fontVariantNumeric: "tabular-nums",
|
|
766
|
+
color: "text.primary"
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
label ?? `${Math.round(value)}%`
|
|
770
|
+
),
|
|
771
|
+
caption && /* @__PURE__ */ React.createElement(
|
|
772
|
+
Typography,
|
|
773
|
+
{
|
|
774
|
+
variant: "caption",
|
|
775
|
+
sx: { fontSize: 9.5, color: "text.secondary", mt: 0.25 }
|
|
776
|
+
},
|
|
777
|
+
caption
|
|
778
|
+
)
|
|
779
|
+
)
|
|
780
|
+
);
|
|
781
|
+
};
|
|
686
782
|
dataTableSx = (theme) => ({
|
|
687
783
|
// Element selectors, not `.MuiTableCell-head`: host apps may set a MUI
|
|
688
784
|
// classname prefix (Backstage ships `v5-`), which breaks global class targeting.
|
|
@@ -2828,6 +2924,29 @@ function buildBudgetSummary(user, teams, keys, maxKeys = 3) {
|
|
|
2828
2924
|
teams: teamLimits
|
|
2829
2925
|
};
|
|
2830
2926
|
}
|
|
2927
|
+
function allBudgetLimits(summary) {
|
|
2928
|
+
return [
|
|
2929
|
+
...summary.keys,
|
|
2930
|
+
...summary.user ? [summary.user] : [],
|
|
2931
|
+
...summary.teams
|
|
2932
|
+
];
|
|
2933
|
+
}
|
|
2934
|
+
function buildBudgetGauges(summary) {
|
|
2935
|
+
const byLevel = {
|
|
2936
|
+
key: summary.keys,
|
|
2937
|
+
user: summary.user ? [summary.user] : [],
|
|
2938
|
+
team: summary.teams
|
|
2939
|
+
};
|
|
2940
|
+
return GAUGE_KINDS.map((kind) => {
|
|
2941
|
+
const limits = byLevel[kind];
|
|
2942
|
+
const count = kind === "key" ? limits.length + summary.hiddenBudgetedKeys : limits.length;
|
|
2943
|
+
return {
|
|
2944
|
+
kind,
|
|
2945
|
+
count,
|
|
2946
|
+
limit: limits.length ? limits.reduce((a, b) => b.pct > a.pct ? b : a) : void 0
|
|
2947
|
+
};
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2831
2950
|
function budgetHeadline(summary) {
|
|
2832
2951
|
const all = [
|
|
2833
2952
|
...summary.keys,
|
|
@@ -2854,24 +2973,20 @@ function budgetTone2(pct) {
|
|
|
2854
2973
|
if (pct >= 80) return "warning";
|
|
2855
2974
|
return "accent";
|
|
2856
2975
|
}
|
|
2976
|
+
var GAUGE_KINDS;
|
|
2857
2977
|
var init_budget = __esm({
|
|
2858
2978
|
"src/budget.ts"() {
|
|
2859
2979
|
"use strict";
|
|
2980
|
+
GAUGE_KINDS = ["key", "user", "team"];
|
|
2860
2981
|
}
|
|
2861
2982
|
});
|
|
2862
2983
|
|
|
2863
|
-
// src/components/
|
|
2864
|
-
import React7
|
|
2865
|
-
import Paper5 from "@mui/material/Paper";
|
|
2984
|
+
// src/components/BudgetLimitList.tsx
|
|
2985
|
+
import React7 from "react";
|
|
2866
2986
|
import Box7 from "@mui/material/Box";
|
|
2987
|
+
import Paper5 from "@mui/material/Paper";
|
|
2867
2988
|
import Typography7 from "@mui/material/Typography";
|
|
2868
|
-
import
|
|
2869
|
-
import Alert3 from "@mui/material/Alert";
|
|
2870
|
-
import Collapse from "@mui/material/Collapse";
|
|
2871
|
-
import IconButton4 from "@mui/material/IconButton";
|
|
2872
|
-
import { ExpandMore as ExpandMore2 } from "@mui/icons-material";
|
|
2873
|
-
import { alpha as alpha4 } from "@mui/material/styles";
|
|
2874
|
-
import { useApi as useApi2 } from "@backstage/core-plugin-api";
|
|
2989
|
+
import { alpha as alpha4, useTheme as useTheme2 } from "@mui/material/styles";
|
|
2875
2990
|
import { Link } from "@backstage/core-components";
|
|
2876
2991
|
function levelToneColor(tone, theme) {
|
|
2877
2992
|
switch (tone) {
|
|
@@ -2887,13 +3002,119 @@ function levelToneColor(tone, theme) {
|
|
|
2887
3002
|
return theme.palette.primary.main;
|
|
2888
3003
|
}
|
|
2889
3004
|
}
|
|
2890
|
-
var
|
|
3005
|
+
var LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
|
|
3006
|
+
var init_BudgetLimitList = __esm({
|
|
3007
|
+
"src/components/BudgetLimitList.tsx"() {
|
|
3008
|
+
"use strict";
|
|
3009
|
+
init_budget();
|
|
3010
|
+
init_format();
|
|
3011
|
+
init_ui();
|
|
3012
|
+
LimitCard = ({ limit }) => {
|
|
3013
|
+
const tone = budgetTone2(limit.pct);
|
|
3014
|
+
const pct = Math.round(limit.pct);
|
|
3015
|
+
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
3016
|
+
return /* @__PURE__ */ React7.createElement(Paper5, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React7.createElement(
|
|
3017
|
+
Typography7,
|
|
3018
|
+
{
|
|
3019
|
+
variant: "body2",
|
|
3020
|
+
sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
3021
|
+
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
3022
|
+
},
|
|
3023
|
+
limit.label
|
|
3024
|
+
), limit.sublabel && /* @__PURE__ */ React7.createElement(
|
|
3025
|
+
Typography7,
|
|
3026
|
+
{
|
|
3027
|
+
variant: "caption",
|
|
3028
|
+
color: "text.secondary",
|
|
3029
|
+
sx: {
|
|
3030
|
+
fontSize: 10.5,
|
|
3031
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
3032
|
+
display: "block",
|
|
3033
|
+
overflow: "hidden",
|
|
3034
|
+
textOverflow: "ellipsis",
|
|
3035
|
+
whiteSpace: "nowrap"
|
|
3036
|
+
}
|
|
3037
|
+
},
|
|
3038
|
+
limit.sublabel
|
|
3039
|
+
)), limit.hidden ? /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ React7.createElement(Box7, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ React7.createElement(Box7, { sx: { mt: 1 } }, /* @__PURE__ */ React7.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ React7.createElement(
|
|
3040
|
+
Box7,
|
|
3041
|
+
{
|
|
3042
|
+
sx: {
|
|
3043
|
+
display: "flex",
|
|
3044
|
+
justifyContent: "space-between",
|
|
3045
|
+
alignItems: "center",
|
|
3046
|
+
gap: 1,
|
|
3047
|
+
mt: 0.75
|
|
3048
|
+
}
|
|
3049
|
+
},
|
|
3050
|
+
/* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, "Hidden by admin") : /* @__PURE__ */ React7.createElement(React7.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
|
|
3051
|
+
/* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3052
|
+
));
|
|
3053
|
+
};
|
|
3054
|
+
TaggedLimit = ({ limit }) => {
|
|
3055
|
+
const theme = useTheme2();
|
|
3056
|
+
const tone = budgetTone2(limit.pct);
|
|
3057
|
+
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(
|
|
3058
|
+
Typography7,
|
|
3059
|
+
{
|
|
3060
|
+
sx: {
|
|
3061
|
+
fontSize: 11,
|
|
3062
|
+
fontWeight: 700,
|
|
3063
|
+
letterSpacing: "0.08em",
|
|
3064
|
+
textTransform: "uppercase",
|
|
3065
|
+
color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
|
|
3066
|
+
mb: 0.5
|
|
3067
|
+
}
|
|
3068
|
+
},
|
|
3069
|
+
limit.kind
|
|
3070
|
+
), /* @__PURE__ */ React7.createElement(LimitCard, { limit }));
|
|
3071
|
+
};
|
|
3072
|
+
MoreKeysNote = ({
|
|
3073
|
+
count,
|
|
3074
|
+
href,
|
|
3075
|
+
label
|
|
3076
|
+
}) => {
|
|
3077
|
+
const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
|
|
3078
|
+
if (!href) {
|
|
3079
|
+
return /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, text);
|
|
3080
|
+
}
|
|
3081
|
+
return /* @__PURE__ */ React7.createElement(Link, { to: href, variant: "caption" }, text, " \u2192");
|
|
3082
|
+
};
|
|
3083
|
+
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, limits.map((limit) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: `${limit.kind}:${limit.sublabel ?? limit.label}`, limit })), hiddenKeyCount > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: hiddenKeyCount, href: keysHref }));
|
|
3084
|
+
LimitListPanel = ({ children }) => /* @__PURE__ */ React7.createElement(
|
|
3085
|
+
Box7,
|
|
3086
|
+
{
|
|
3087
|
+
sx: (theme) => ({
|
|
3088
|
+
px: 1.5,
|
|
3089
|
+
py: 1.5,
|
|
3090
|
+
borderRadius: 1.5,
|
|
3091
|
+
bgcolor: alpha4(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.02)
|
|
3092
|
+
})
|
|
3093
|
+
},
|
|
3094
|
+
children
|
|
3095
|
+
);
|
|
3096
|
+
}
|
|
3097
|
+
});
|
|
3098
|
+
|
|
3099
|
+
// src/components/LiteLLMBudgetWidget.tsx
|
|
3100
|
+
import React8, { useEffect as useEffect3, useMemo as useMemo6, useState as useState5 } from "react";
|
|
3101
|
+
import Paper6 from "@mui/material/Paper";
|
|
3102
|
+
import Box8 from "@mui/material/Box";
|
|
3103
|
+
import Typography8 from "@mui/material/Typography";
|
|
3104
|
+
import CircularProgress4 from "@mui/material/CircularProgress";
|
|
3105
|
+
import Alert3 from "@mui/material/Alert";
|
|
3106
|
+
import Collapse from "@mui/material/Collapse";
|
|
3107
|
+
import IconButton4 from "@mui/material/IconButton";
|
|
3108
|
+
import { ExpandMore as ExpandMore2 } from "@mui/icons-material";
|
|
3109
|
+
import { alpha as alpha5 } from "@mui/material/styles";
|
|
3110
|
+
import { useApi as useApi2 } from "@backstage/core-plugin-api";
|
|
3111
|
+
var LevelFrame, EmptyLimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, HierarchyNote, FullBody, CompactBody, LiteLLMBudgetWidget;
|
|
2891
3112
|
var init_LiteLLMBudgetWidget = __esm({
|
|
2892
3113
|
"src/components/LiteLLMBudgetWidget.tsx"() {
|
|
2893
3114
|
"use strict";
|
|
2894
3115
|
init_api();
|
|
2895
|
-
init_format();
|
|
2896
3116
|
init_ui();
|
|
3117
|
+
init_BudgetLimitList();
|
|
2897
3118
|
init_budget();
|
|
2898
3119
|
LevelFrame = ({
|
|
2899
3120
|
rank,
|
|
@@ -2903,8 +3124,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2903
3124
|
tone,
|
|
2904
3125
|
isLast,
|
|
2905
3126
|
children
|
|
2906
|
-
}) => /* @__PURE__ */
|
|
2907
|
-
|
|
3127
|
+
}) => /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ React8.createElement(
|
|
3128
|
+
Box8,
|
|
2908
3129
|
{
|
|
2909
3130
|
sx: (theme) => ({
|
|
2910
3131
|
width: 26,
|
|
@@ -2913,7 +3134,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2913
3134
|
display: "flex",
|
|
2914
3135
|
alignItems: "center",
|
|
2915
3136
|
justifyContent: "center",
|
|
2916
|
-
bgcolor:
|
|
3137
|
+
bgcolor: alpha5(levelToneColor(tone, theme), 0.14),
|
|
2917
3138
|
color: levelToneColor(tone, theme),
|
|
2918
3139
|
fontSize: 12,
|
|
2919
3140
|
fontWeight: 700,
|
|
@@ -2921,18 +3142,18 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2921
3142
|
})
|
|
2922
3143
|
},
|
|
2923
3144
|
rank
|
|
2924
|
-
), /* @__PURE__ */
|
|
2925
|
-
|
|
3145
|
+
), /* @__PURE__ */ React8.createElement(
|
|
3146
|
+
Box8,
|
|
2926
3147
|
{
|
|
2927
3148
|
sx: (theme) => ({
|
|
2928
3149
|
flex: 1,
|
|
2929
3150
|
width: 2,
|
|
2930
3151
|
minHeight: 14,
|
|
2931
|
-
bgcolor: isLast ? "transparent" :
|
|
3152
|
+
bgcolor: isLast ? "transparent" : alpha5(theme.palette.text.primary, 0.12)
|
|
2932
3153
|
})
|
|
2933
3154
|
}
|
|
2934
|
-
)), /* @__PURE__ */
|
|
2935
|
-
|
|
3155
|
+
)), /* @__PURE__ */ React8.createElement(Box8, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ React8.createElement(
|
|
3156
|
+
Typography8,
|
|
2936
3157
|
{
|
|
2937
3158
|
sx: (theme) => ({
|
|
2938
3159
|
fontSize: 12.5,
|
|
@@ -2943,102 +3164,42 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2943
3164
|
})
|
|
2944
3165
|
},
|
|
2945
3166
|
name
|
|
2946
|
-
), /* @__PURE__ */
|
|
2947
|
-
EmptyLimitCard = ({ children }) => /* @__PURE__ */
|
|
2948
|
-
|
|
3167
|
+
), /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ React8.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
|
|
3168
|
+
EmptyLimitCard = ({ children }) => /* @__PURE__ */ React8.createElement(
|
|
3169
|
+
Paper6,
|
|
2949
3170
|
{
|
|
2950
3171
|
variant: "outlined",
|
|
2951
3172
|
sx: (theme) => ({
|
|
2952
3173
|
px: 1.5,
|
|
2953
3174
|
py: 1.25,
|
|
2954
3175
|
borderRadius: 1.5,
|
|
2955
|
-
bgcolor:
|
|
3176
|
+
bgcolor: alpha5(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
|
|
2956
3177
|
})
|
|
2957
3178
|
},
|
|
2958
|
-
/* @__PURE__ */
|
|
3179
|
+
/* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
|
|
2959
3180
|
);
|
|
2960
|
-
LimitCard = ({ limit }) => {
|
|
2961
|
-
const tone = budgetTone2(limit.pct);
|
|
2962
|
-
const pct = Math.round(limit.pct);
|
|
2963
|
-
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
2964
|
-
return /* @__PURE__ */ React7.createElement(Paper5, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ React7.createElement(Box7, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React7.createElement(
|
|
2965
|
-
Typography7,
|
|
2966
|
-
{
|
|
2967
|
-
variant: "body2",
|
|
2968
|
-
sx: { fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
2969
|
-
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
2970
|
-
},
|
|
2971
|
-
limit.label
|
|
2972
|
-
), limit.sublabel && /* @__PURE__ */ React7.createElement(
|
|
2973
|
-
Typography7,
|
|
2974
|
-
{
|
|
2975
|
-
variant: "caption",
|
|
2976
|
-
color: "text.secondary",
|
|
2977
|
-
sx: {
|
|
2978
|
-
fontSize: 10.5,
|
|
2979
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
2980
|
-
display: "block",
|
|
2981
|
-
overflow: "hidden",
|
|
2982
|
-
textOverflow: "ellipsis",
|
|
2983
|
-
whiteSpace: "nowrap"
|
|
2984
|
-
}
|
|
2985
|
-
},
|
|
2986
|
-
limit.sublabel
|
|
2987
|
-
)), limit.hidden ? /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ React7.createElement(Box7, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ React7.createElement(Box7, { sx: { mt: 1 } }, /* @__PURE__ */ React7.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ React7.createElement(
|
|
2988
|
-
Box7,
|
|
2989
|
-
{
|
|
2990
|
-
sx: {
|
|
2991
|
-
display: "flex",
|
|
2992
|
-
justifyContent: "space-between",
|
|
2993
|
-
alignItems: "center",
|
|
2994
|
-
gap: 1,
|
|
2995
|
-
mt: 0.75
|
|
2996
|
-
}
|
|
2997
|
-
},
|
|
2998
|
-
/* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, "Hidden by admin") : /* @__PURE__ */ React7.createElement(React7.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
|
|
2999
|
-
/* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3000
|
-
));
|
|
3001
|
-
};
|
|
3002
3181
|
CLOSEST_LEVEL_LABEL = {
|
|
3003
3182
|
key: "key",
|
|
3004
3183
|
user: "personal",
|
|
3005
3184
|
team: "team"
|
|
3006
3185
|
};
|
|
3007
3186
|
KEYS_PATH = "/litellm?tab=keys";
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
Box7,
|
|
3187
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ React8.createElement(
|
|
3188
|
+
Box8,
|
|
3011
3189
|
{
|
|
3012
3190
|
sx: (theme) => ({
|
|
3013
3191
|
px: 1.5,
|
|
3014
3192
|
py: 1.25,
|
|
3015
3193
|
borderRadius: 1.5,
|
|
3016
3194
|
border: "1px dashed",
|
|
3017
|
-
borderColor:
|
|
3195
|
+
borderColor: alpha5(theme.palette.text.primary, 0.18)
|
|
3018
3196
|
})
|
|
3019
3197
|
},
|
|
3020
|
-
/* @__PURE__ */
|
|
3198
|
+
/* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ React8.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.")
|
|
3021
3199
|
);
|
|
3022
|
-
TaggedLimit = ({ limit }) => {
|
|
3023
|
-
const tone = budgetTone2(limit.pct);
|
|
3024
|
-
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(
|
|
3025
|
-
Typography7,
|
|
3026
|
-
{
|
|
3027
|
-
sx: (theme) => ({
|
|
3028
|
-
fontSize: 11,
|
|
3029
|
-
fontWeight: 700,
|
|
3030
|
-
letterSpacing: "0.08em",
|
|
3031
|
-
textTransform: "uppercase",
|
|
3032
|
-
color: tone === "accent" ? theme.palette.text.secondary : levelToneColor(tone, theme),
|
|
3033
|
-
mb: 0.5
|
|
3034
|
-
})
|
|
3035
|
-
},
|
|
3036
|
-
limit.kind
|
|
3037
|
-
), /* @__PURE__ */ React7.createElement(LimitCard, { limit }));
|
|
3038
|
-
};
|
|
3039
3200
|
FullBody = ({ summary }) => {
|
|
3040
3201
|
const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
|
|
3041
|
-
return /* @__PURE__ */
|
|
3202
|
+
return /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
|
|
3042
3203
|
LevelFrame,
|
|
3043
3204
|
{
|
|
3044
3205
|
rank: 1,
|
|
@@ -3046,10 +3207,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3046
3207
|
tagline: "per-key cap \xB7 highest priority",
|
|
3047
3208
|
note: hasBudgetedKeys ? void 0 : "no key budgets set"
|
|
3048
3209
|
},
|
|
3049
|
-
!hasBudgetedKeys && /* @__PURE__ */
|
|
3050
|
-
summary.keys.map((k) => /* @__PURE__ */
|
|
3051
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */
|
|
3052
|
-
), /* @__PURE__ */
|
|
3210
|
+
!hasBudgetedKeys && /* @__PURE__ */ React8.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3211
|
+
summary.keys.map((k) => /* @__PURE__ */ React8.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3212
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React8.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
|
|
3213
|
+
), /* @__PURE__ */ React8.createElement(
|
|
3053
3214
|
LevelFrame,
|
|
3054
3215
|
{
|
|
3055
3216
|
rank: 2,
|
|
@@ -3057,15 +3218,15 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3057
3218
|
tagline: "cap on everything you spend with your own keys",
|
|
3058
3219
|
note: "skipped for team-bound keys"
|
|
3059
3220
|
},
|
|
3060
|
-
summary.user ? /* @__PURE__ */
|
|
3061
|
-
), /* @__PURE__ */
|
|
3221
|
+
summary.user ? /* @__PURE__ */ React8.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React8.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
|
|
3222
|
+
), /* @__PURE__ */ React8.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__ */ React8.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React8.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React8.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React8.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__ */ React8.createElement(Box8, { sx: { mt: 2 } }, /* @__PURE__ */ React8.createElement(HierarchyNote, { withReset: true })));
|
|
3062
3223
|
};
|
|
3063
3224
|
CompactBody = ({ summary }) => {
|
|
3064
3225
|
const { count } = budgetHeadline(summary);
|
|
3065
3226
|
if (count === 0) {
|
|
3066
|
-
return /* @__PURE__ */
|
|
3227
|
+
return /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
|
|
3067
3228
|
}
|
|
3068
|
-
return /* @__PURE__ */
|
|
3229
|
+
return /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ React8.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ React8.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React8.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ React8.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React8.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
|
|
3069
3230
|
};
|
|
3070
3231
|
LiteLLMBudgetWidget = ({
|
|
3071
3232
|
title = "Budget Policy",
|
|
@@ -3116,9 +3277,9 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3116
3277
|
headline.closest.pct
|
|
3117
3278
|
)}%`;
|
|
3118
3279
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3119
|
-
const body = /* @__PURE__ */
|
|
3120
|
-
return /* @__PURE__ */
|
|
3121
|
-
|
|
3280
|
+
const body = /* @__PURE__ */ React8.createElement(React8.Fragment, null, loading && /* @__PURE__ */ React8.createElement(Box8, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ React8.createElement(CircularProgress4, { size: 32 })), !loading && error && /* @__PURE__ */ React8.createElement(Alert3, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ React8.createElement(CompactBody, { summary }) : /* @__PURE__ */ React8.createElement(FullBody, { summary })));
|
|
3281
|
+
return /* @__PURE__ */ React8.createElement(Paper6, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ React8.createElement(
|
|
3282
|
+
Box8,
|
|
3122
3283
|
{
|
|
3123
3284
|
onClick: () => setExpanded((e) => !e),
|
|
3124
3285
|
sx: {
|
|
@@ -3130,14 +3291,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3130
3291
|
mb: expanded ? 1.5 : 0
|
|
3131
3292
|
}
|
|
3132
3293
|
},
|
|
3133
|
-
/* @__PURE__ */
|
|
3294
|
+
/* @__PURE__ */ React8.createElement(Box8, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ React8.createElement(Box8, { sx: { mt: 0.5 } }, /* @__PURE__ */ React8.createElement(
|
|
3134
3295
|
StatusPill,
|
|
3135
3296
|
{
|
|
3136
3297
|
label: summaryText,
|
|
3137
3298
|
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3138
3299
|
}
|
|
3139
3300
|
))),
|
|
3140
|
-
/* @__PURE__ */
|
|
3301
|
+
/* @__PURE__ */ React8.createElement(
|
|
3141
3302
|
IconButton4,
|
|
3142
3303
|
{
|
|
3143
3304
|
size: "small",
|
|
@@ -3153,10 +3314,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3153
3314
|
transition: theme.transitions.create("transform")
|
|
3154
3315
|
})
|
|
3155
3316
|
},
|
|
3156
|
-
/* @__PURE__ */
|
|
3317
|
+
/* @__PURE__ */ React8.createElement(ExpandMore2, null)
|
|
3157
3318
|
)
|
|
3158
|
-
) : /* @__PURE__ */
|
|
3159
|
-
|
|
3319
|
+
) : /* @__PURE__ */ React8.createElement(Box8, { sx: { mb: 1.5 } }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ React8.createElement(Typography8, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ React8.createElement(Collapse, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ React8.createElement(
|
|
3320
|
+
Box8,
|
|
3160
3321
|
{
|
|
3161
3322
|
sx: (theme) => ({
|
|
3162
3323
|
mt: 2,
|
|
@@ -3171,10 +3332,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3171
3332
|
});
|
|
3172
3333
|
|
|
3173
3334
|
// src/components/TeamUsage.tsx
|
|
3174
|
-
import
|
|
3175
|
-
import
|
|
3176
|
-
import
|
|
3177
|
-
import
|
|
3335
|
+
import React9, { useState as useState6 } from "react";
|
|
3336
|
+
import Paper7 from "@mui/material/Paper";
|
|
3337
|
+
import Box9 from "@mui/material/Box";
|
|
3338
|
+
import Typography9 from "@mui/material/Typography";
|
|
3178
3339
|
import Skeleton4 from "@mui/material/Skeleton";
|
|
3179
3340
|
import Divider3 from "@mui/material/Divider";
|
|
3180
3341
|
import Table4 from "@mui/material/Table";
|
|
@@ -3187,7 +3348,7 @@ import Collapse2 from "@mui/material/Collapse";
|
|
|
3187
3348
|
import IconButton5 from "@mui/material/IconButton";
|
|
3188
3349
|
import Button5 from "@mui/material/Button";
|
|
3189
3350
|
import CircularProgress5 from "@mui/material/CircularProgress";
|
|
3190
|
-
import { alpha as
|
|
3351
|
+
import { alpha as alpha6 } from "@mui/material/styles";
|
|
3191
3352
|
import { ExpandMore as ExpandMore3, Group, Add as Add3 } from "@mui/icons-material";
|
|
3192
3353
|
import {
|
|
3193
3354
|
AreaChart as AreaChart2,
|
|
@@ -3231,24 +3392,24 @@ var init_TeamUsage = __esm({
|
|
|
3231
3392
|
const renderDailySpendSection = () => {
|
|
3232
3393
|
if (hidden) return null;
|
|
3233
3394
|
if (usageLoading) {
|
|
3234
|
-
return /* @__PURE__ */
|
|
3395
|
+
return /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React9.createElement(CircularProgress5, { size: 22 }));
|
|
3235
3396
|
}
|
|
3236
3397
|
if (dailyData.length === 0) return null;
|
|
3237
|
-
return /* @__PURE__ */
|
|
3238
|
-
|
|
3398
|
+
return /* @__PURE__ */ React9.createElement(Box9, { mb: 2.5 }, /* @__PURE__ */ React9.createElement(
|
|
3399
|
+
Typography9,
|
|
3239
3400
|
{
|
|
3240
3401
|
variant: "caption",
|
|
3241
3402
|
color: "text.secondary",
|
|
3242
3403
|
sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
|
|
3243
3404
|
},
|
|
3244
3405
|
"Daily spend"
|
|
3245
|
-
), /* @__PURE__ */
|
|
3406
|
+
), /* @__PURE__ */ React9.createElement(Box9, { height: 150 }, /* @__PURE__ */ React9.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React9.createElement(AreaChart2, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React9.createElement(CartesianGrid2, { ...chart.grid }), /* @__PURE__ */ React9.createElement(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React9.createElement(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ React9.createElement(
|
|
3246
3407
|
Tooltip2,
|
|
3247
3408
|
{
|
|
3248
3409
|
cursor: chart.cursor,
|
|
3249
|
-
content: /* @__PURE__ */
|
|
3410
|
+
content: /* @__PURE__ */ React9.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
3250
3411
|
}
|
|
3251
|
-
), /* @__PURE__ */
|
|
3412
|
+
), /* @__PURE__ */ React9.createElement(
|
|
3252
3413
|
Area2,
|
|
3253
3414
|
{
|
|
3254
3415
|
type: "monotone",
|
|
@@ -3261,8 +3422,8 @@ var init_TeamUsage = __esm({
|
|
|
3261
3422
|
}
|
|
3262
3423
|
)))));
|
|
3263
3424
|
};
|
|
3264
|
-
const sectionLabel = (label) => /* @__PURE__ */
|
|
3265
|
-
|
|
3425
|
+
const sectionLabel = (label) => /* @__PURE__ */ React9.createElement(
|
|
3426
|
+
Typography9,
|
|
3266
3427
|
{
|
|
3267
3428
|
variant: "caption",
|
|
3268
3429
|
color: "text.secondary",
|
|
@@ -3270,8 +3431,8 @@ var init_TeamUsage = __esm({
|
|
|
3270
3431
|
},
|
|
3271
3432
|
label
|
|
3272
3433
|
);
|
|
3273
|
-
return /* @__PURE__ */
|
|
3274
|
-
|
|
3434
|
+
return /* @__PURE__ */ React9.createElement(Paper7, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React9.createElement(Box9, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React9.createElement(
|
|
3435
|
+
Box9,
|
|
3275
3436
|
{
|
|
3276
3437
|
sx: (theme) => ({
|
|
3277
3438
|
width: 36,
|
|
@@ -3281,20 +3442,20 @@ var init_TeamUsage = __esm({
|
|
|
3281
3442
|
display: "flex",
|
|
3282
3443
|
alignItems: "center",
|
|
3283
3444
|
justifyContent: "center",
|
|
3284
|
-
bgcolor:
|
|
3445
|
+
bgcolor: alpha6(theme.palette.primary.main, 0.1),
|
|
3285
3446
|
color: theme.palette.primary.main
|
|
3286
3447
|
})
|
|
3287
3448
|
},
|
|
3288
|
-
/* @__PURE__ */
|
|
3289
|
-
), /* @__PURE__ */
|
|
3290
|
-
|
|
3449
|
+
/* @__PURE__ */ React9.createElement(Group, { fontSize: "small" })
|
|
3450
|
+
), /* @__PURE__ */ React9.createElement(Box9, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ React9.createElement(
|
|
3451
|
+
Typography9,
|
|
3291
3452
|
{
|
|
3292
3453
|
variant: "caption",
|
|
3293
3454
|
color: "text.secondary",
|
|
3294
3455
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3295
3456
|
},
|
|
3296
3457
|
team.team_id
|
|
3297
|
-
)), isOver && /* @__PURE__ */
|
|
3458
|
+
)), isOver && /* @__PURE__ */ React9.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ React9.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ React9.createElement(
|
|
3298
3459
|
Button5,
|
|
3299
3460
|
{
|
|
3300
3461
|
size: "small",
|
|
@@ -3303,7 +3464,7 @@ var init_TeamUsage = __esm({
|
|
|
3303
3464
|
sx: { textTransform: "none" }
|
|
3304
3465
|
},
|
|
3305
3466
|
"Edit"
|
|
3306
|
-
), /* @__PURE__ */
|
|
3467
|
+
), /* @__PURE__ */ React9.createElement(
|
|
3307
3468
|
IconButton5,
|
|
3308
3469
|
{
|
|
3309
3470
|
size: "small",
|
|
@@ -3316,9 +3477,9 @@ var init_TeamUsage = __esm({
|
|
|
3316
3477
|
transition: theme.transitions.create("transform")
|
|
3317
3478
|
})
|
|
3318
3479
|
},
|
|
3319
|
-
/* @__PURE__ */
|
|
3320
|
-
)), /* @__PURE__ */
|
|
3321
|
-
|
|
3480
|
+
/* @__PURE__ */ React9.createElement(ExpandMore3, null)
|
|
3481
|
+
)), /* @__PURE__ */ React9.createElement(
|
|
3482
|
+
Box9,
|
|
3322
3483
|
{
|
|
3323
3484
|
sx: {
|
|
3324
3485
|
display: "grid",
|
|
@@ -3328,35 +3489,35 @@ var init_TeamUsage = __esm({
|
|
|
3328
3489
|
maxWidth: 640
|
|
3329
3490
|
}
|
|
3330
3491
|
},
|
|
3331
|
-
/* @__PURE__ */
|
|
3332
|
-
/* @__PURE__ */
|
|
3333
|
-
/* @__PURE__ */
|
|
3334
|
-
/* @__PURE__ */
|
|
3335
|
-
/* @__PURE__ */
|
|
3336
|
-
/* @__PURE__ */
|
|
3337
|
-
), hidden && /* @__PURE__ */
|
|
3492
|
+
/* @__PURE__ */ React9.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
|
|
3493
|
+
/* @__PURE__ */ React9.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
|
|
3494
|
+
/* @__PURE__ */ React9.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
|
|
3495
|
+
/* @__PURE__ */ React9.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
|
|
3496
|
+
/* @__PURE__ */ React9.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
3497
|
+
/* @__PURE__ */ React9.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
3498
|
+
), hidden && /* @__PURE__ */ React9.createElement(Box9, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ React9.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ React9.createElement(Typography9, { 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__ */ React9.createElement(Box9, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React9.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React9.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ React9.createElement(Collapse2, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(Divider3, { sx: { my: 2.5 } }), /* @__PURE__ */ React9.createElement(Box9, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React9.createElement(Box9, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React9.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React9.createElement(Box9, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React9.createElement(
|
|
3338
3499
|
TableContainer4,
|
|
3339
3500
|
{
|
|
3340
|
-
component:
|
|
3501
|
+
component: Paper7,
|
|
3341
3502
|
variant: "outlined",
|
|
3342
3503
|
sx: { borderRadius: 1.5 }
|
|
3343
3504
|
},
|
|
3344
|
-
/* @__PURE__ */
|
|
3345
|
-
|
|
3505
|
+
/* @__PURE__ */ React9.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead4, null, /* @__PURE__ */ React9.createElement(TableRow4, null, /* @__PURE__ */ React9.createElement(TableCell4, null, "User"), /* @__PURE__ */ React9.createElement(TableCell4, { align: "right" }, "Role"))), /* @__PURE__ */ React9.createElement(TableBody4, null, team.members_with_roles.map((m) => /* @__PURE__ */ React9.createElement(TableRow4, { key: m.user_id }, /* @__PURE__ */ React9.createElement(TableCell4, null, m.user_email ? /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, m.user_email), /* @__PURE__ */ React9.createElement(
|
|
3506
|
+
Typography9,
|
|
3346
3507
|
{
|
|
3347
3508
|
variant: "caption",
|
|
3348
3509
|
color: "text.secondary",
|
|
3349
3510
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3350
3511
|
},
|
|
3351
3512
|
m.user_id
|
|
3352
|
-
)) : /* @__PURE__ */
|
|
3353
|
-
|
|
3513
|
+
)) : /* @__PURE__ */ React9.createElement(
|
|
3514
|
+
Typography9,
|
|
3354
3515
|
{
|
|
3355
3516
|
variant: "body2",
|
|
3356
3517
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
3357
3518
|
},
|
|
3358
3519
|
m.user_id
|
|
3359
|
-
)), /* @__PURE__ */
|
|
3520
|
+
)), /* @__PURE__ */ React9.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React9.createElement(
|
|
3360
3521
|
StatusPill,
|
|
3361
3522
|
{
|
|
3362
3523
|
label: m.role,
|
|
@@ -3364,7 +3525,7 @@ var init_TeamUsage = __esm({
|
|
|
3364
3525
|
dot: false
|
|
3365
3526
|
}
|
|
3366
3527
|
))))))
|
|
3367
|
-
) : /* @__PURE__ */
|
|
3528
|
+
) : /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
|
|
3368
3529
|
};
|
|
3369
3530
|
TeamUsage = ({
|
|
3370
3531
|
teams,
|
|
@@ -3376,22 +3537,22 @@ var init_TeamUsage = __esm({
|
|
|
3376
3537
|
canCreate,
|
|
3377
3538
|
onCreateTeam
|
|
3378
3539
|
}) => {
|
|
3379
|
-
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */
|
|
3540
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React9.createElement(
|
|
3380
3541
|
Button5,
|
|
3381
3542
|
{
|
|
3382
3543
|
variant: "contained",
|
|
3383
3544
|
color: "primary",
|
|
3384
3545
|
disableElevation: true,
|
|
3385
|
-
startIcon: /* @__PURE__ */
|
|
3546
|
+
startIcon: /* @__PURE__ */ React9.createElement(Add3, null),
|
|
3386
3547
|
onClick: onCreateTeam
|
|
3387
3548
|
},
|
|
3388
3549
|
"Create Team"
|
|
3389
3550
|
) : void 0;
|
|
3390
3551
|
if (loading) {
|
|
3391
|
-
return /* @__PURE__ */
|
|
3552
|
+
return /* @__PURE__ */ React9.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React9.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React9.createElement(Skeleton4, { variant: "rounded", height: 120 }));
|
|
3392
3553
|
}
|
|
3393
3554
|
if (!teams.length) {
|
|
3394
|
-
return /* @__PURE__ */
|
|
3555
|
+
return /* @__PURE__ */ React9.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React9.createElement(
|
|
3395
3556
|
EmptyState,
|
|
3396
3557
|
{
|
|
3397
3558
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -3399,14 +3560,14 @@ var init_TeamUsage = __esm({
|
|
|
3399
3560
|
}
|
|
3400
3561
|
));
|
|
3401
3562
|
}
|
|
3402
|
-
return /* @__PURE__ */
|
|
3563
|
+
return /* @__PURE__ */ React9.createElement(
|
|
3403
3564
|
SectionCard,
|
|
3404
3565
|
{
|
|
3405
3566
|
title: "Teams",
|
|
3406
3567
|
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
3407
3568
|
actions: createAction
|
|
3408
3569
|
},
|
|
3409
|
-
/* @__PURE__ */
|
|
3570
|
+
/* @__PURE__ */ React9.createElement(Box9, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React9.createElement(
|
|
3410
3571
|
TeamCard,
|
|
3411
3572
|
{
|
|
3412
3573
|
key: team.team_id,
|
|
@@ -3423,8 +3584,8 @@ var init_TeamUsage = __esm({
|
|
|
3423
3584
|
});
|
|
3424
3585
|
|
|
3425
3586
|
// src/components/ModelsTable.tsx
|
|
3426
|
-
import
|
|
3427
|
-
import
|
|
3587
|
+
import React10, { useState as useState7, useMemo as useMemo7, useCallback } from "react";
|
|
3588
|
+
import Box10 from "@mui/material/Box";
|
|
3428
3589
|
import Table5 from "@mui/material/Table";
|
|
3429
3590
|
import TableBody5 from "@mui/material/TableBody";
|
|
3430
3591
|
import TableCell5 from "@mui/material/TableCell";
|
|
@@ -3432,7 +3593,7 @@ import TableContainer5 from "@mui/material/TableContainer";
|
|
|
3432
3593
|
import TableHead5 from "@mui/material/TableHead";
|
|
3433
3594
|
import TableRow5 from "@mui/material/TableRow";
|
|
3434
3595
|
import TableSortLabel2 from "@mui/material/TableSortLabel";
|
|
3435
|
-
import
|
|
3596
|
+
import Typography10 from "@mui/material/Typography";
|
|
3436
3597
|
import MenuItem4 from "@mui/material/MenuItem";
|
|
3437
3598
|
import Select from "@mui/material/Select";
|
|
3438
3599
|
import FormControl from "@mui/material/FormControl";
|
|
@@ -3444,7 +3605,7 @@ import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
|
|
3444
3605
|
import Tooltip3 from "@mui/material/Tooltip";
|
|
3445
3606
|
import Snackbar from "@mui/material/Snackbar";
|
|
3446
3607
|
import Alert4 from "@mui/material/Alert";
|
|
3447
|
-
import { alpha as
|
|
3608
|
+
import { alpha as alpha7, useTheme as useTheme3 } from "@mui/material/styles";
|
|
3448
3609
|
function isModelAllowedByTeam2(model, teamModels) {
|
|
3449
3610
|
if (!teamModels || teamModels.length === 0 || teamModels.includes(ALL_PROXY_MODELS2)) return true;
|
|
3450
3611
|
return teamModels.includes(model.model_name) || !!model.access_groups?.some((group) => teamModels.includes(group));
|
|
@@ -3469,7 +3630,7 @@ var init_ModelsTable = __esm({
|
|
|
3469
3630
|
init_ui();
|
|
3470
3631
|
ALL_PROXY_MODELS2 = "all-proxy-models";
|
|
3471
3632
|
ModelsTable = ({ allModels, teams, loading }) => {
|
|
3472
|
-
const theme =
|
|
3633
|
+
const theme = useTheme3();
|
|
3473
3634
|
const [selectedTeamId, setSelectedTeamId] = useState7("");
|
|
3474
3635
|
const [filterText, setFilterText] = useState7("");
|
|
3475
3636
|
const [copiedSnackbar, setCopiedSnackbar] = useState7(false);
|
|
@@ -3555,13 +3716,13 @@ var init_ModelsTable = __esm({
|
|
|
3555
3716
|
return theme.palette.info.main;
|
|
3556
3717
|
}
|
|
3557
3718
|
};
|
|
3558
|
-
return /* @__PURE__ */
|
|
3719
|
+
return /* @__PURE__ */ React10.createElement(
|
|
3559
3720
|
SectionCard,
|
|
3560
3721
|
{
|
|
3561
3722
|
title: "Available Models",
|
|
3562
3723
|
subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
|
|
3563
3724
|
},
|
|
3564
|
-
/* @__PURE__ */
|
|
3725
|
+
/* @__PURE__ */ React10.createElement(Box10, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ React10.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React10.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React10.createElement(
|
|
3565
3726
|
Select,
|
|
3566
3727
|
{
|
|
3567
3728
|
labelId: "team-select-label",
|
|
@@ -3569,8 +3730,8 @@ var init_ModelsTable = __esm({
|
|
|
3569
3730
|
label: "Team",
|
|
3570
3731
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
3571
3732
|
},
|
|
3572
|
-
teams.map((t) => /* @__PURE__ */
|
|
3573
|
-
)), /* @__PURE__ */
|
|
3733
|
+
teams.map((t) => /* @__PURE__ */ React10.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
|
|
3734
|
+
)), /* @__PURE__ */ React10.createElement(
|
|
3574
3735
|
TextField5,
|
|
3575
3736
|
{
|
|
3576
3737
|
label: "Filter Models",
|
|
@@ -3580,33 +3741,33 @@ var init_ModelsTable = __esm({
|
|
|
3580
3741
|
disabled: !selectedTeamId,
|
|
3581
3742
|
sx: { minWidth: 240, mb: 2 },
|
|
3582
3743
|
InputProps: {
|
|
3583
|
-
startAdornment: /* @__PURE__ */
|
|
3744
|
+
startAdornment: /* @__PURE__ */ React10.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React10.createElement(Search2, { fontSize: "small" }))
|
|
3584
3745
|
}
|
|
3585
3746
|
}
|
|
3586
3747
|
)),
|
|
3587
|
-
loading && /* @__PURE__ */
|
|
3588
|
-
!loading && !selectedTeamId && /* @__PURE__ */
|
|
3748
|
+
loading && /* @__PURE__ */ React10.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
3749
|
+
!loading && !selectedTeamId && /* @__PURE__ */ React10.createElement(
|
|
3589
3750
|
EmptyState,
|
|
3590
3751
|
{
|
|
3591
3752
|
message: "Select a team to see available models",
|
|
3592
3753
|
hint: "Each team may have a different set of models assigned"
|
|
3593
3754
|
}
|
|
3594
3755
|
),
|
|
3595
|
-
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */
|
|
3756
|
+
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React10.createElement(
|
|
3596
3757
|
EmptyState,
|
|
3597
3758
|
{
|
|
3598
3759
|
message: "No models available for this team",
|
|
3599
3760
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
3600
3761
|
}
|
|
3601
3762
|
),
|
|
3602
|
-
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */
|
|
3763
|
+
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React10.createElement(
|
|
3603
3764
|
EmptyState,
|
|
3604
3765
|
{
|
|
3605
3766
|
message: "No models match your search",
|
|
3606
3767
|
hint: "Try a different search term"
|
|
3607
3768
|
}
|
|
3608
3769
|
),
|
|
3609
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */
|
|
3770
|
+
!loading && filteredModels.length > 0 && /* @__PURE__ */ React10.createElement(TableContainer5, null, /* @__PURE__ */ React10.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React10.createElement(TableHead5, null, /* @__PURE__ */ React10.createElement(TableRow5, null, /* @__PURE__ */ React10.createElement(TableCell5, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ React10.createElement(TableCell5, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ React10.createElement(TableSortLabel2, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ React10.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React10.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React10.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React10.createElement(TableCell5, null, /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React10.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React10.createElement(
|
|
3610
3771
|
"span",
|
|
3611
3772
|
{
|
|
3612
3773
|
role: "button",
|
|
@@ -3616,9 +3777,9 @@ var init_ModelsTable = __esm({
|
|
|
3616
3777
|
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
3617
3778
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3618
3779
|
},
|
|
3619
|
-
/* @__PURE__ */
|
|
3620
|
-
)), /* @__PURE__ */
|
|
3621
|
-
|
|
3780
|
+
/* @__PURE__ */ React10.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
3781
|
+
)), /* @__PURE__ */ React10.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React10.createElement(
|
|
3782
|
+
Box10,
|
|
3622
3783
|
{
|
|
3623
3784
|
component: "span",
|
|
3624
3785
|
role: "button",
|
|
@@ -3628,9 +3789,9 @@ var init_ModelsTable = __esm({
|
|
|
3628
3789
|
sx: quietIconButtonSx("accent"),
|
|
3629
3790
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3630
3791
|
},
|
|
3631
|
-
/* @__PURE__ */
|
|
3632
|
-
)))), /* @__PURE__ */
|
|
3633
|
-
|
|
3792
|
+
/* @__PURE__ */ React10.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
|
|
3793
|
+
)))), /* @__PURE__ */ React10.createElement(TableCell5, null, /* @__PURE__ */ React10.createElement(
|
|
3794
|
+
Box10,
|
|
3634
3795
|
{
|
|
3635
3796
|
component: "span",
|
|
3636
3797
|
sx: {
|
|
@@ -3641,13 +3802,13 @@ var init_ModelsTable = __esm({
|
|
|
3641
3802
|
fontSize: 11,
|
|
3642
3803
|
fontWeight: 600,
|
|
3643
3804
|
textTransform: "capitalize",
|
|
3644
|
-
bgcolor:
|
|
3805
|
+
bgcolor: alpha7(modeColor(m.mode), 0.1),
|
|
3645
3806
|
color: modeColor(m.mode)
|
|
3646
3807
|
}
|
|
3647
3808
|
},
|
|
3648
3809
|
m.mode
|
|
3649
|
-
)), /* @__PURE__ */
|
|
3650
|
-
/* @__PURE__ */
|
|
3810
|
+
)), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React10.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React10.createElement(TableCell5, null, /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React10.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React10.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
|
|
3811
|
+
/* @__PURE__ */ React10.createElement(
|
|
3651
3812
|
Snackbar,
|
|
3652
3813
|
{
|
|
3653
3814
|
open: copiedSnackbar,
|
|
@@ -3655,7 +3816,7 @@ var init_ModelsTable = __esm({
|
|
|
3655
3816
|
onClose: () => setCopiedSnackbar(false),
|
|
3656
3817
|
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
3657
3818
|
},
|
|
3658
|
-
/* @__PURE__ */
|
|
3819
|
+
/* @__PURE__ */ React10.createElement(Alert4, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
3659
3820
|
)
|
|
3660
3821
|
);
|
|
3661
3822
|
};
|
|
@@ -3663,9 +3824,9 @@ var init_ModelsTable = __esm({
|
|
|
3663
3824
|
});
|
|
3664
3825
|
|
|
3665
3826
|
// src/components/AuditLog.tsx
|
|
3666
|
-
import
|
|
3667
|
-
import
|
|
3668
|
-
import
|
|
3827
|
+
import React11, { useState as useState8, useMemo as useMemo8 } from "react";
|
|
3828
|
+
import Box11 from "@mui/material/Box";
|
|
3829
|
+
import Typography11 from "@mui/material/Typography";
|
|
3669
3830
|
import Table6 from "@mui/material/Table";
|
|
3670
3831
|
import TableBody6 from "@mui/material/TableBody";
|
|
3671
3832
|
import TableCell6 from "@mui/material/TableCell";
|
|
@@ -3679,7 +3840,7 @@ import Skeleton5 from "@mui/material/Skeleton";
|
|
|
3679
3840
|
import Collapse3 from "@mui/material/Collapse";
|
|
3680
3841
|
import IconButton6 from "@mui/material/IconButton";
|
|
3681
3842
|
import Alert5 from "@mui/material/Alert";
|
|
3682
|
-
import { alpha as
|
|
3843
|
+
import { alpha as alpha8 } from "@mui/material/styles";
|
|
3683
3844
|
import { KeyboardArrowDown } from "@mui/icons-material";
|
|
3684
3845
|
import { useAsync as useAsync2 } from "react-use";
|
|
3685
3846
|
function actionTone(action) {
|
|
@@ -3702,10 +3863,10 @@ function formatDateTime(iso) {
|
|
|
3702
3863
|
}
|
|
3703
3864
|
function renderAuditLogBody(loading, entries) {
|
|
3704
3865
|
if (loading) {
|
|
3705
|
-
return /* @__PURE__ */
|
|
3866
|
+
return /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React11.createElement(Skeleton5, { variant: "rounded", height: 160 })));
|
|
3706
3867
|
}
|
|
3707
3868
|
if (entries.length === 0) {
|
|
3708
|
-
return /* @__PURE__ */
|
|
3869
|
+
return /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React11.createElement(
|
|
3709
3870
|
EmptyState,
|
|
3710
3871
|
{
|
|
3711
3872
|
message: "No audit events found",
|
|
@@ -3713,7 +3874,7 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3713
3874
|
}
|
|
3714
3875
|
)));
|
|
3715
3876
|
}
|
|
3716
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
3877
|
+
return entries.map((entry) => /* @__PURE__ */ React11.createElement(DetailRow, { key: entry.id, entry }));
|
|
3717
3878
|
}
|
|
3718
3879
|
var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
3719
3880
|
var init_AuditLog = __esm({
|
|
@@ -3737,7 +3898,7 @@ var init_AuditLog = __esm({
|
|
|
3737
3898
|
DetailRow = ({ entry }) => {
|
|
3738
3899
|
const [open, setOpen] = useState8(false);
|
|
3739
3900
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3740
|
-
return /* @__PURE__ */
|
|
3901
|
+
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React11.createElement(
|
|
3741
3902
|
IconButton6,
|
|
3742
3903
|
{
|
|
3743
3904
|
size: "small",
|
|
@@ -3750,9 +3911,9 @@ var init_AuditLog = __esm({
|
|
|
3750
3911
|
transition: theme.transitions.create("transform")
|
|
3751
3912
|
})
|
|
3752
3913
|
},
|
|
3753
|
-
/* @__PURE__ */
|
|
3754
|
-
)), /* @__PURE__ */
|
|
3755
|
-
|
|
3914
|
+
/* @__PURE__ */ React11.createElement(KeyboardArrowDown, { fontSize: "small" })
|
|
3915
|
+
)), /* @__PURE__ */ React11.createElement(TableCell6, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React11.createElement(Typography11, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React11.createElement(TableCell6, null, entry.action && /* @__PURE__ */ React11.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React11.createElement(TableCell6, null, /* @__PURE__ */ React11.createElement(Typography11, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React11.createElement(TableCell6, null, /* @__PURE__ */ React11.createElement(
|
|
3916
|
+
Typography11,
|
|
3756
3917
|
{
|
|
3757
3918
|
variant: "body2",
|
|
3758
3919
|
component: "code",
|
|
@@ -3761,8 +3922,8 @@ var init_AuditLog = __esm({
|
|
|
3761
3922
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3762
3923
|
},
|
|
3763
3924
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3764
|
-
)), /* @__PURE__ */
|
|
3765
|
-
|
|
3925
|
+
)), /* @__PURE__ */ React11.createElement(TableCell6, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React11.createElement(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React11.createElement(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React11.createElement(Collapse3, { in: open, unmountOnExit: true }, /* @__PURE__ */ React11.createElement(
|
|
3926
|
+
Box11,
|
|
3766
3927
|
{
|
|
3767
3928
|
display: "flex",
|
|
3768
3929
|
gap: 2,
|
|
@@ -3771,11 +3932,11 @@ var init_AuditLog = __esm({
|
|
|
3771
3932
|
p: 2,
|
|
3772
3933
|
mb: 1.5,
|
|
3773
3934
|
borderRadius: 1.5,
|
|
3774
|
-
bgcolor:
|
|
3935
|
+
bgcolor: alpha8(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
3775
3936
|
})
|
|
3776
3937
|
},
|
|
3777
|
-
entry.before_value && /* @__PURE__ */
|
|
3778
|
-
|
|
3938
|
+
entry.before_value && /* @__PURE__ */ React11.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React11.createElement(
|
|
3939
|
+
Typography11,
|
|
3779
3940
|
{
|
|
3780
3941
|
variant: "caption",
|
|
3781
3942
|
color: "text.secondary",
|
|
@@ -3784,9 +3945,9 @@ var init_AuditLog = __esm({
|
|
|
3784
3945
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3785
3946
|
},
|
|
3786
3947
|
"Before"
|
|
3787
|
-
), /* @__PURE__ */
|
|
3788
|
-
entry.updated_values && /* @__PURE__ */
|
|
3789
|
-
|
|
3948
|
+
), /* @__PURE__ */ React11.createElement(Typography11, { 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))),
|
|
3949
|
+
entry.updated_values && /* @__PURE__ */ React11.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React11.createElement(
|
|
3950
|
+
Typography11,
|
|
3790
3951
|
{
|
|
3791
3952
|
variant: "caption",
|
|
3792
3953
|
color: "text.secondary",
|
|
@@ -3795,7 +3956,7 @@ var init_AuditLog = __esm({
|
|
|
3795
3956
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3796
3957
|
},
|
|
3797
3958
|
"After"
|
|
3798
|
-
), /* @__PURE__ */
|
|
3959
|
+
), /* @__PURE__ */ React11.createElement(Typography11, { 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)))
|
|
3799
3960
|
)))));
|
|
3800
3961
|
};
|
|
3801
3962
|
AuditLog = ({ api }) => {
|
|
@@ -3812,13 +3973,13 @@ var init_AuditLog = __esm({
|
|
|
3812
3973
|
);
|
|
3813
3974
|
const entries = value?.audit_logs ?? [];
|
|
3814
3975
|
const total = value?.total ?? 0;
|
|
3815
|
-
return /* @__PURE__ */
|
|
3976
|
+
return /* @__PURE__ */ React11.createElement(
|
|
3816
3977
|
SectionCard,
|
|
3817
3978
|
{
|
|
3818
3979
|
title: "Audit Log",
|
|
3819
3980
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
3820
3981
|
flush: true,
|
|
3821
|
-
actions: /* @__PURE__ */
|
|
3982
|
+
actions: /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(
|
|
3822
3983
|
TextField6,
|
|
3823
3984
|
{
|
|
3824
3985
|
size: "small",
|
|
@@ -3831,12 +3992,12 @@ var init_AuditLog = __esm({
|
|
|
3831
3992
|
},
|
|
3832
3993
|
sx: { minWidth: 150 }
|
|
3833
3994
|
},
|
|
3834
|
-
/* @__PURE__ */
|
|
3835
|
-
/* @__PURE__ */
|
|
3836
|
-
/* @__PURE__ */
|
|
3837
|
-
/* @__PURE__ */
|
|
3838
|
-
/* @__PURE__ */
|
|
3839
|
-
), /* @__PURE__ */
|
|
3995
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "" }, "All actions"),
|
|
3996
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "created" }, "Created"),
|
|
3997
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "updated" }, "Updated"),
|
|
3998
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
|
|
3999
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "blocked" }, "Blocked")
|
|
4000
|
+
), /* @__PURE__ */ React11.createElement(
|
|
3840
4001
|
TextField6,
|
|
3841
4002
|
{
|
|
3842
4003
|
size: "small",
|
|
@@ -3849,13 +4010,13 @@ var init_AuditLog = __esm({
|
|
|
3849
4010
|
},
|
|
3850
4011
|
sx: { minWidth: 150 }
|
|
3851
4012
|
},
|
|
3852
|
-
/* @__PURE__ */
|
|
3853
|
-
/* @__PURE__ */
|
|
3854
|
-
/* @__PURE__ */
|
|
3855
|
-
/* @__PURE__ */
|
|
3856
|
-
/* @__PURE__ */
|
|
3857
|
-
/* @__PURE__ */
|
|
3858
|
-
), /* @__PURE__ */
|
|
4013
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "" }, "All tables"),
|
|
4014
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
4015
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
4016
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
|
|
4017
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
|
|
4018
|
+
/* @__PURE__ */ React11.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
|
|
4019
|
+
), /* @__PURE__ */ React11.createElement(
|
|
3859
4020
|
TextField6,
|
|
3860
4021
|
{
|
|
3861
4022
|
size: "small",
|
|
@@ -3869,9 +4030,9 @@ var init_AuditLog = __esm({
|
|
|
3869
4030
|
}
|
|
3870
4031
|
))
|
|
3871
4032
|
},
|
|
3872
|
-
error && /* @__PURE__ */
|
|
3873
|
-
/* @__PURE__ */
|
|
3874
|
-
/* @__PURE__ */
|
|
4033
|
+
error && /* @__PURE__ */ React11.createElement(Box11, { px: 2.5, pb: 2 }, /* @__PURE__ */ React11.createElement(Alert5, { severity: "error" }, error.message)),
|
|
4034
|
+
/* @__PURE__ */ React11.createElement(TableContainer6, null, /* @__PURE__ */ React11.createElement(Table6, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React11.createElement(TableHead6, null, /* @__PURE__ */ React11.createElement(TableRow6, null, /* @__PURE__ */ React11.createElement(TableCell6, { sx: { width: 40 } }), /* @__PURE__ */ React11.createElement(TableCell6, null, "Time"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Action"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Table"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Object ID"), /* @__PURE__ */ React11.createElement(TableCell6, null, "Changed By"))), /* @__PURE__ */ React11.createElement(TableBody6, null, renderAuditLogBody(loading, entries)))),
|
|
4035
|
+
/* @__PURE__ */ React11.createElement(
|
|
3875
4036
|
TablePagination,
|
|
3876
4037
|
{
|
|
3877
4038
|
component: "div",
|
|
@@ -3926,13 +4087,13 @@ var LiteLLMPage_exports = {};
|
|
|
3926
4087
|
__export(LiteLLMPage_exports, {
|
|
3927
4088
|
LiteLLMPage: () => LiteLLMPage
|
|
3928
4089
|
});
|
|
3929
|
-
import
|
|
3930
|
-
import
|
|
4090
|
+
import React12, { useState as useState9, useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo9 } from "react";
|
|
4091
|
+
import Box12 from "@mui/material/Box";
|
|
3931
4092
|
import Snackbar2 from "@mui/material/Snackbar";
|
|
3932
4093
|
import Alert6 from "@mui/material/Alert";
|
|
3933
4094
|
import CircularProgress6 from "@mui/material/CircularProgress";
|
|
3934
|
-
import
|
|
3935
|
-
import
|
|
4095
|
+
import Typography12 from "@mui/material/Typography";
|
|
4096
|
+
import Paper8 from "@mui/material/Paper";
|
|
3936
4097
|
import Tabs2 from "@mui/material/Tabs";
|
|
3937
4098
|
import Tab2 from "@mui/material/Tab";
|
|
3938
4099
|
import Grid2 from "@mui/material/Grid";
|
|
@@ -3987,6 +4148,21 @@ var init_LiteLLMPage = __esm({
|
|
|
3987
4148
|
const t = searchParams.get("tab");
|
|
3988
4149
|
return isPageTab(t) ? t : "overview";
|
|
3989
4150
|
});
|
|
4151
|
+
const [generateDialogOpen, setGenerateDialogOpen] = useState9(
|
|
4152
|
+
() => searchParams.get("generate") === "1"
|
|
4153
|
+
);
|
|
4154
|
+
useEffect4(() => {
|
|
4155
|
+
if (searchParams.get("generate") !== "1") return;
|
|
4156
|
+
setGenerateDialogOpen(true);
|
|
4157
|
+
setSearchParams(
|
|
4158
|
+
(prev) => {
|
|
4159
|
+
const next = new URLSearchParams(prev);
|
|
4160
|
+
next.delete("generate");
|
|
4161
|
+
return next;
|
|
4162
|
+
},
|
|
4163
|
+
{ replace: true }
|
|
4164
|
+
);
|
|
4165
|
+
}, [searchParams, setSearchParams]);
|
|
3990
4166
|
const selectTab = useCallback2(
|
|
3991
4167
|
(tab) => {
|
|
3992
4168
|
setActiveTab(tab);
|
|
@@ -4002,7 +4178,6 @@ var init_LiteLLMPage = __esm({
|
|
|
4002
4178
|
[setSearchParams]
|
|
4003
4179
|
);
|
|
4004
4180
|
const [snackbar, setSnackbar] = useState9(null);
|
|
4005
|
-
const [generateDialogOpen, setGenerateDialogOpen] = useState9(false);
|
|
4006
4181
|
const [manageTeam, setManageTeam] = useState9(null);
|
|
4007
4182
|
const [teamUsageCache, setTeamUsageCache] = useState9({});
|
|
4008
4183
|
const [teamUsageLoading, setTeamUsageLoading] = useState9({});
|
|
@@ -4196,14 +4371,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4196
4371
|
);
|
|
4197
4372
|
const isInitialLoading = userLoading && !userInfo;
|
|
4198
4373
|
if (isInitialLoading) {
|
|
4199
|
-
return /* @__PURE__ */
|
|
4374
|
+
return /* @__PURE__ */ React12.createElement(Box12, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React12.createElement(CircularProgress6, null));
|
|
4200
4375
|
}
|
|
4201
4376
|
if (userError || !userInfo) {
|
|
4202
4377
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
4203
4378
|
const hint = userError?.body?.hint;
|
|
4204
|
-
return /* @__PURE__ */
|
|
4379
|
+
return /* @__PURE__ */ React12.createElement(Box12, { p: 3 }, /* @__PURE__ */ React12.createElement(Paper8, { sx: { p: 3 } }, /* @__PURE__ */ React12.createElement(Typography12, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React12.createElement(Typography12, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React12.createElement(Typography12, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React12.createElement(Typography12, { 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.")));
|
|
4205
4380
|
}
|
|
4206
|
-
const pageTabs = /* @__PURE__ */
|
|
4381
|
+
const pageTabs = /* @__PURE__ */ React12.createElement(
|
|
4207
4382
|
Tabs2,
|
|
4208
4383
|
{
|
|
4209
4384
|
value: activeTab,
|
|
@@ -4216,13 +4391,13 @@ var init_LiteLLMPage = __esm({
|
|
|
4216
4391
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
4217
4392
|
}
|
|
4218
4393
|
},
|
|
4219
|
-
/* @__PURE__ */
|
|
4220
|
-
/* @__PURE__ */
|
|
4221
|
-
/* @__PURE__ */
|
|
4222
|
-
/* @__PURE__ */
|
|
4223
|
-
userInfo.can_view_audit && /* @__PURE__ */
|
|
4394
|
+
/* @__PURE__ */ React12.createElement(Tab2, { label: "Overview", value: "overview" }),
|
|
4395
|
+
/* @__PURE__ */ React12.createElement(Tab2, { label: "Keys", value: "keys" }),
|
|
4396
|
+
/* @__PURE__ */ React12.createElement(Tab2, { label: "Teams", value: "teams" }),
|
|
4397
|
+
/* @__PURE__ */ React12.createElement(Tab2, { label: "Models", value: "models" }),
|
|
4398
|
+
userInfo.can_view_audit && /* @__PURE__ */ React12.createElement(Tab2, { label: "Audit Log", value: "audit" })
|
|
4224
4399
|
);
|
|
4225
|
-
return /* @__PURE__ */
|
|
4400
|
+
return /* @__PURE__ */ React12.createElement(Box12, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React12.createElement(
|
|
4226
4401
|
DashboardHeader,
|
|
4227
4402
|
{
|
|
4228
4403
|
userInfo,
|
|
@@ -4232,7 +4407,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4232
4407
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4233
4408
|
tabs: pageTabs
|
|
4234
4409
|
}
|
|
4235
|
-
), activeTab === "overview" && /* @__PURE__ */
|
|
4410
|
+
), activeTab === "overview" && /* @__PURE__ */ React12.createElement(Grid2, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ React12.createElement(
|
|
4236
4411
|
UsageStats,
|
|
4237
4412
|
{
|
|
4238
4413
|
usage: usage ?? null,
|
|
@@ -4243,7 +4418,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4243
4418
|
loading: usageLoading,
|
|
4244
4419
|
userInfo
|
|
4245
4420
|
}
|
|
4246
|
-
)), /* @__PURE__ */
|
|
4421
|
+
)), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ React12.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ React12.createElement(
|
|
4247
4422
|
KeysTable,
|
|
4248
4423
|
{
|
|
4249
4424
|
keys: keys ?? [],
|
|
@@ -4262,7 +4437,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4262
4437
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
4263
4438
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
4264
4439
|
const visibleTeams = Array.from(teamsById.values());
|
|
4265
|
-
return /* @__PURE__ */
|
|
4440
|
+
return /* @__PURE__ */ React12.createElement(
|
|
4266
4441
|
TeamUsage,
|
|
4267
4442
|
{
|
|
4268
4443
|
teams: visibleTeams,
|
|
@@ -4278,14 +4453,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4278
4453
|
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
4279
4454
|
}
|
|
4280
4455
|
);
|
|
4281
|
-
})(), activeTab === "models" && /* @__PURE__ */
|
|
4456
|
+
})(), activeTab === "models" && /* @__PURE__ */ React12.createElement(
|
|
4282
4457
|
ModelsTable,
|
|
4283
4458
|
{
|
|
4284
4459
|
allModels: allModels ?? [],
|
|
4285
4460
|
teams: teams ?? [],
|
|
4286
4461
|
loading: modelsLoading
|
|
4287
4462
|
}
|
|
4288
|
-
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */
|
|
4463
|
+
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React12.createElement(AuditLog, { api }), /* @__PURE__ */ React12.createElement(
|
|
4289
4464
|
GenerateKeyDialog,
|
|
4290
4465
|
{
|
|
4291
4466
|
open: generateDialogOpen,
|
|
@@ -4298,7 +4473,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4298
4473
|
onGenerateKey: handleGenerateKey,
|
|
4299
4474
|
onGetConfig: () => api.getConfig()
|
|
4300
4475
|
}
|
|
4301
|
-
), /* @__PURE__ */
|
|
4476
|
+
), /* @__PURE__ */ React12.createElement(
|
|
4302
4477
|
ManageTeamDialog,
|
|
4303
4478
|
{
|
|
4304
4479
|
open: !!manageTeam,
|
|
@@ -4360,7 +4535,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4360
4535
|
refreshTeams();
|
|
4361
4536
|
}
|
|
4362
4537
|
}
|
|
4363
|
-
), /* @__PURE__ */
|
|
4538
|
+
), /* @__PURE__ */ React12.createElement(
|
|
4364
4539
|
Snackbar2,
|
|
4365
4540
|
{
|
|
4366
4541
|
open: !!snackbar,
|
|
@@ -4368,7 +4543,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4368
4543
|
onClose: () => setSnackbar(null),
|
|
4369
4544
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
4370
4545
|
},
|
|
4371
|
-
snackbar ? /* @__PURE__ */
|
|
4546
|
+
snackbar ? /* @__PURE__ */ React12.createElement(Alert6, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
4372
4547
|
));
|
|
4373
4548
|
};
|
|
4374
4549
|
}
|
|
@@ -4376,7 +4551,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4376
4551
|
|
|
4377
4552
|
// src/plugin.tsx
|
|
4378
4553
|
init_api();
|
|
4379
|
-
import
|
|
4554
|
+
import React13 from "react";
|
|
4380
4555
|
import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
|
|
4381
4556
|
import {
|
|
4382
4557
|
createFrontendPlugin,
|
|
@@ -4395,10 +4570,10 @@ var liteLlmPage = PageBlueprint.make({
|
|
|
4395
4570
|
params: {
|
|
4396
4571
|
path: "/litellm",
|
|
4397
4572
|
title: "LiteLLM",
|
|
4398
|
-
icon: /* @__PURE__ */
|
|
4573
|
+
icon: /* @__PURE__ */ React13.createElement(TrendingUpIcon, null),
|
|
4399
4574
|
loader: async () => {
|
|
4400
4575
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
4401
|
-
return /* @__PURE__ */
|
|
4576
|
+
return /* @__PURE__ */ React13.createElement(LiteLLMPage2, null);
|
|
4402
4577
|
}
|
|
4403
4578
|
}
|
|
4404
4579
|
});
|
|
@@ -4417,10 +4592,10 @@ init_TeamUsage();
|
|
|
4417
4592
|
// src/components/LiteLLMHomeWidget.tsx
|
|
4418
4593
|
init_api();
|
|
4419
4594
|
init_format();
|
|
4420
|
-
import
|
|
4421
|
-
import
|
|
4422
|
-
import
|
|
4423
|
-
import
|
|
4595
|
+
import React14, { useState as useState10, useEffect as useEffect5 } from "react";
|
|
4596
|
+
import Paper9 from "@mui/material/Paper";
|
|
4597
|
+
import Box13 from "@mui/material/Box";
|
|
4598
|
+
import Typography13 from "@mui/material/Typography";
|
|
4424
4599
|
import FormControl2 from "@mui/material/FormControl";
|
|
4425
4600
|
import Select2 from "@mui/material/Select";
|
|
4426
4601
|
import MenuItem6 from "@mui/material/MenuItem";
|
|
@@ -4441,7 +4616,7 @@ function presetToDateRange(preset) {
|
|
|
4441
4616
|
}
|
|
4442
4617
|
return { start, end };
|
|
4443
4618
|
}
|
|
4444
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
4619
|
+
var Kpi = ({ label, value }) => /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Typography13, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React14.createElement(Typography13, { variant: "subtitle1", fontWeight: 600 }, value));
|
|
4445
4620
|
var LiteLLMHomeWidget = ({
|
|
4446
4621
|
defaultPeriod = "7d",
|
|
4447
4622
|
title = "LiteLLM Usage"
|
|
@@ -4453,7 +4628,7 @@ var LiteLLMHomeWidget = ({
|
|
|
4453
4628
|
const [keysError, setKeysError] = useState10(null);
|
|
4454
4629
|
const [usage, setUsage] = useState10(null);
|
|
4455
4630
|
const [keys, setKeys] = useState10([]);
|
|
4456
|
-
|
|
4631
|
+
useEffect5(() => {
|
|
4457
4632
|
let cancelled = false;
|
|
4458
4633
|
setLoading(true);
|
|
4459
4634
|
setUsageError(null);
|
|
@@ -4490,17 +4665,17 @@ var LiteLLMHomeWidget = ({
|
|
|
4490
4665
|
spend: d.spend
|
|
4491
4666
|
}));
|
|
4492
4667
|
const hasSparkline = dailyData.length > 0;
|
|
4493
|
-
return /* @__PURE__ */
|
|
4668
|
+
return /* @__PURE__ */ React14.createElement(Paper9, { sx: { p: 2 } }, /* @__PURE__ */ React14.createElement(Box13, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React14.createElement(Typography13, { variant: "h6" }, title), /* @__PURE__ */ React14.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React14.createElement(
|
|
4494
4669
|
Select2,
|
|
4495
4670
|
{
|
|
4496
4671
|
value: period,
|
|
4497
4672
|
onChange: (e) => setPeriod(e.target.value),
|
|
4498
4673
|
displayEmpty: true
|
|
4499
4674
|
},
|
|
4500
|
-
/* @__PURE__ */
|
|
4501
|
-
/* @__PURE__ */
|
|
4502
|
-
/* @__PURE__ */
|
|
4503
|
-
))), loading && /* @__PURE__ */
|
|
4675
|
+
/* @__PURE__ */ React14.createElement(MenuItem6, { value: "today" }, "Today"),
|
|
4676
|
+
/* @__PURE__ */ React14.createElement(MenuItem6, { value: "7d" }, "7d"),
|
|
4677
|
+
/* @__PURE__ */ React14.createElement(MenuItem6, { value: "30d" }, "30d")
|
|
4678
|
+
))), loading && /* @__PURE__ */ React14.createElement(Box13, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React14.createElement(CircularProgress7, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React14.createElement(Alert7, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React14.createElement(React14.Fragment, null, partialFailure && /* @__PURE__ */ React14.createElement(Alert7, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React14.createElement(Grid3, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React14.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React14.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React14.createElement(Box13, { height: 120 }, /* @__PURE__ */ React14.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React14.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React14.createElement(
|
|
4504
4679
|
Area3,
|
|
4505
4680
|
{
|
|
4506
4681
|
type: "monotone",
|
|
@@ -4516,6 +4691,307 @@ var LiteLLMHomeWidget = ({
|
|
|
4516
4691
|
|
|
4517
4692
|
// src/index.ts
|
|
4518
4693
|
init_LiteLLMBudgetWidget();
|
|
4694
|
+
|
|
4695
|
+
// src/components/LiteLLMBudgetGauges.tsx
|
|
4696
|
+
init_api();
|
|
4697
|
+
init_format();
|
|
4698
|
+
init_ui();
|
|
4699
|
+
init_BudgetLimitList();
|
|
4700
|
+
init_budget();
|
|
4701
|
+
import React15, { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo10, useState as useState11 } from "react";
|
|
4702
|
+
import Paper10 from "@mui/material/Paper";
|
|
4703
|
+
import Box14 from "@mui/material/Box";
|
|
4704
|
+
import Button6 from "@mui/material/Button";
|
|
4705
|
+
import Typography14 from "@mui/material/Typography";
|
|
4706
|
+
import CircularProgress8 from "@mui/material/CircularProgress";
|
|
4707
|
+
import Alert8 from "@mui/material/Alert";
|
|
4708
|
+
import Collapse4 from "@mui/material/Collapse";
|
|
4709
|
+
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
4710
|
+
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
4711
|
+
import { Link as Link2 } from "@backstage/core-components";
|
|
4712
|
+
var MODULE_PATH = "/litellm";
|
|
4713
|
+
var LEVEL = {
|
|
4714
|
+
key: { name: "Key", none: "No key budget" },
|
|
4715
|
+
user: { name: "User", none: "No personal budget" },
|
|
4716
|
+
team: { name: "Team", none: "No team budget" }
|
|
4717
|
+
};
|
|
4718
|
+
var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
|
|
4719
|
+
var CTA_LABELS = {
|
|
4720
|
+
"new-key": "New key",
|
|
4721
|
+
module: "Open module",
|
|
4722
|
+
"all-limits": "All limits"
|
|
4723
|
+
};
|
|
4724
|
+
function fmtUsdShort(n) {
|
|
4725
|
+
const v = n ?? 0;
|
|
4726
|
+
if (v >= 100 && Number.isInteger(v)) return `$${v}`;
|
|
4727
|
+
return fmtUsd(v);
|
|
4728
|
+
}
|
|
4729
|
+
function spendCaption(limit) {
|
|
4730
|
+
if (limit.hidden) return "hidden by admin";
|
|
4731
|
+
return `${fmtUsdShort(limit.spend)} / ${fmtUsdShort(limit.budget)}`;
|
|
4732
|
+
}
|
|
4733
|
+
function resetLabel(limit) {
|
|
4734
|
+
const window2 = fmtBudgetDuration(limit.budgetDuration);
|
|
4735
|
+
return window2 ? `resets ${window2}` : "never resets";
|
|
4736
|
+
}
|
|
4737
|
+
var LevelGauge = ({
|
|
4738
|
+
gauge,
|
|
4739
|
+
size,
|
|
4740
|
+
keysHref
|
|
4741
|
+
}) => {
|
|
4742
|
+
const { name, none } = LEVEL[gauge.kind];
|
|
4743
|
+
const limit = gauge.limit;
|
|
4744
|
+
const tone = limit ? budgetTone2(limit.pct) : "neutral";
|
|
4745
|
+
const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
|
|
4746
|
+
return /* @__PURE__ */ React15.createElement(
|
|
4747
|
+
Box14,
|
|
4748
|
+
{
|
|
4749
|
+
sx: {
|
|
4750
|
+
display: "flex",
|
|
4751
|
+
flexDirection: "column",
|
|
4752
|
+
alignItems: "center",
|
|
4753
|
+
minWidth: 0,
|
|
4754
|
+
flex: 1
|
|
4755
|
+
}
|
|
4756
|
+
},
|
|
4757
|
+
/* @__PURE__ */ React15.createElement(
|
|
4758
|
+
Gauge,
|
|
4759
|
+
{
|
|
4760
|
+
value: limit?.pct ?? 0,
|
|
4761
|
+
tone,
|
|
4762
|
+
size,
|
|
4763
|
+
label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
|
|
4764
|
+
}
|
|
4765
|
+
),
|
|
4766
|
+
/* @__PURE__ */ React15.createElement(
|
|
4767
|
+
Typography14,
|
|
4768
|
+
{
|
|
4769
|
+
sx: (theme) => ({
|
|
4770
|
+
mt: 0.75,
|
|
4771
|
+
fontSize: 10.5,
|
|
4772
|
+
fontWeight: 700,
|
|
4773
|
+
letterSpacing: "0.08em",
|
|
4774
|
+
textTransform: "uppercase",
|
|
4775
|
+
color: tone === "neutral" ? theme.palette.text.secondary : void 0
|
|
4776
|
+
})
|
|
4777
|
+
},
|
|
4778
|
+
name
|
|
4779
|
+
),
|
|
4780
|
+
limit ? /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
|
|
4781
|
+
Typography14,
|
|
4782
|
+
{
|
|
4783
|
+
variant: "caption",
|
|
4784
|
+
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
|
|
4785
|
+
sx: {
|
|
4786
|
+
mt: 0.25,
|
|
4787
|
+
fontSize: 11,
|
|
4788
|
+
fontWeight: 600,
|
|
4789
|
+
maxWidth: "100%",
|
|
4790
|
+
overflow: "hidden",
|
|
4791
|
+
textOverflow: "ellipsis",
|
|
4792
|
+
whiteSpace: "nowrap"
|
|
4793
|
+
}
|
|
4794
|
+
},
|
|
4795
|
+
limit.label
|
|
4796
|
+
), /* @__PURE__ */ React15.createElement(
|
|
4797
|
+
Typography14,
|
|
4798
|
+
{
|
|
4799
|
+
variant: "caption",
|
|
4800
|
+
color: "text.secondary",
|
|
4801
|
+
sx: {
|
|
4802
|
+
fontSize: 10.5,
|
|
4803
|
+
fontVariantNumeric: "tabular-nums",
|
|
4804
|
+
textAlign: "center"
|
|
4805
|
+
}
|
|
4806
|
+
},
|
|
4807
|
+
spendCaption(limit)
|
|
4808
|
+
), extra > 0 ? /* @__PURE__ */ React15.createElement(
|
|
4809
|
+
Typography14,
|
|
4810
|
+
{
|
|
4811
|
+
component: Link2,
|
|
4812
|
+
to: keysHref,
|
|
4813
|
+
variant: "caption",
|
|
4814
|
+
sx: {
|
|
4815
|
+
fontSize: 10.5,
|
|
4816
|
+
color: "primary.main",
|
|
4817
|
+
textDecoration: "none",
|
|
4818
|
+
"&:hover": { textDecoration: "underline" }
|
|
4819
|
+
}
|
|
4820
|
+
},
|
|
4821
|
+
"+",
|
|
4822
|
+
extra,
|
|
4823
|
+
" more"
|
|
4824
|
+
) : /* @__PURE__ */ React15.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ React15.createElement(
|
|
4825
|
+
Typography14,
|
|
4826
|
+
{
|
|
4827
|
+
variant: "caption",
|
|
4828
|
+
color: "text.secondary",
|
|
4829
|
+
sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
|
|
4830
|
+
},
|
|
4831
|
+
none
|
|
4832
|
+
)
|
|
4833
|
+
);
|
|
4834
|
+
};
|
|
4835
|
+
var LiteLLMBudgetGauges = ({
|
|
4836
|
+
title = "Budget",
|
|
4837
|
+
size = 72,
|
|
4838
|
+
keysHref,
|
|
4839
|
+
moduleHref = MODULE_PATH,
|
|
4840
|
+
onCreateKey,
|
|
4841
|
+
ctas,
|
|
4842
|
+
maxExpandedKeys = 8,
|
|
4843
|
+
expanded,
|
|
4844
|
+
defaultExpanded = false,
|
|
4845
|
+
onExpandedChange,
|
|
4846
|
+
action
|
|
4847
|
+
}) => {
|
|
4848
|
+
const api = useApi5(liteLlmApiRef);
|
|
4849
|
+
const [loading, setLoading] = useState11(true);
|
|
4850
|
+
const [error, setError] = useState11(null);
|
|
4851
|
+
const [user, setUser] = useState11(null);
|
|
4852
|
+
const [teams, setTeams] = useState11([]);
|
|
4853
|
+
const [keys, setKeys] = useState11([]);
|
|
4854
|
+
const isControlled = expanded !== void 0;
|
|
4855
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = useState11(defaultExpanded);
|
|
4856
|
+
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
4857
|
+
const toggleExpanded = useCallback3(() => {
|
|
4858
|
+
const next = !isExpanded;
|
|
4859
|
+
if (!isControlled) setUncontrolledExpanded(next);
|
|
4860
|
+
onExpandedChange?.(next);
|
|
4861
|
+
}, [isControlled, isExpanded, onExpandedChange]);
|
|
4862
|
+
const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
|
|
4863
|
+
useEffect6(() => {
|
|
4864
|
+
let cancelled = false;
|
|
4865
|
+
setLoading(true);
|
|
4866
|
+
setError(null);
|
|
4867
|
+
Promise.allSettled([api.getUserInfo(), api.getTeams(), api.listKeys()]).then(
|
|
4868
|
+
([userResult, teamsResult, keysResult]) => {
|
|
4869
|
+
if (cancelled) return;
|
|
4870
|
+
if (userResult.status === "fulfilled") {
|
|
4871
|
+
setUser(userResult.value);
|
|
4872
|
+
} else {
|
|
4873
|
+
setUser(null);
|
|
4874
|
+
setError(
|
|
4875
|
+
userResult.reason?.message ?? "Failed to load your LiteLLM profile"
|
|
4876
|
+
);
|
|
4877
|
+
}
|
|
4878
|
+
setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
|
|
4879
|
+
setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
|
|
4880
|
+
setLoading(false);
|
|
4881
|
+
}
|
|
4882
|
+
);
|
|
4883
|
+
return () => {
|
|
4884
|
+
cancelled = true;
|
|
4885
|
+
};
|
|
4886
|
+
}, [api]);
|
|
4887
|
+
const summary = useMemo10(
|
|
4888
|
+
() => buildBudgetSummary(user, teams, keys, keys.length),
|
|
4889
|
+
[user, teams, keys]
|
|
4890
|
+
);
|
|
4891
|
+
const gauges = useMemo10(() => buildBudgetGauges(summary), [summary]);
|
|
4892
|
+
const headline = useMemo10(() => budgetHeadline(summary), [summary]);
|
|
4893
|
+
const expandedSummary = useMemo10(
|
|
4894
|
+
() => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
|
|
4895
|
+
[user, teams, keys, maxExpandedKeys]
|
|
4896
|
+
);
|
|
4897
|
+
const expandedLimits = useMemo10(
|
|
4898
|
+
() => allBudgetLimits(expandedSummary),
|
|
4899
|
+
[expandedSummary]
|
|
4900
|
+
);
|
|
4901
|
+
const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
4902
|
+
const resolvedCtas = useMemo10(() => {
|
|
4903
|
+
const list = ctas ?? DEFAULT_CTAS;
|
|
4904
|
+
return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
|
|
4905
|
+
}, [ctas, headline.count]);
|
|
4906
|
+
const renderCta = (cta, index) => {
|
|
4907
|
+
const label = cta.label ?? CTA_LABELS[cta.kind];
|
|
4908
|
+
const key = `${cta.kind}-${index}`;
|
|
4909
|
+
switch (cta.kind) {
|
|
4910
|
+
case "new-key":
|
|
4911
|
+
return onCreateKey ? /* @__PURE__ */ React15.createElement(Button6, { key, size: "small", variant: "contained", onClick: onCreateKey }, label) : /* @__PURE__ */ React15.createElement(
|
|
4912
|
+
Button6,
|
|
4913
|
+
{
|
|
4914
|
+
key,
|
|
4915
|
+
size: "small",
|
|
4916
|
+
variant: "contained",
|
|
4917
|
+
component: Link2,
|
|
4918
|
+
to: `${moduleHref}?generate=1`
|
|
4919
|
+
},
|
|
4920
|
+
label
|
|
4921
|
+
);
|
|
4922
|
+
case "module":
|
|
4923
|
+
return /* @__PURE__ */ React15.createElement(Button6, { key, size: "small", variant: "outlined", component: Link2, to: moduleHref }, label);
|
|
4924
|
+
case "all-limits":
|
|
4925
|
+
return /* @__PURE__ */ React15.createElement(
|
|
4926
|
+
Button6,
|
|
4927
|
+
{
|
|
4928
|
+
key,
|
|
4929
|
+
size: "small",
|
|
4930
|
+
variant: "text",
|
|
4931
|
+
onClick: toggleExpanded,
|
|
4932
|
+
endIcon: /* @__PURE__ */ React15.createElement(
|
|
4933
|
+
ExpandMore4,
|
|
4934
|
+
{
|
|
4935
|
+
sx: {
|
|
4936
|
+
transform: isExpanded ? "rotate(180deg)" : "none",
|
|
4937
|
+
transition: (theme) => theme.transitions.create("transform")
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4940
|
+
)
|
|
4941
|
+
},
|
|
4942
|
+
label
|
|
4943
|
+
);
|
|
4944
|
+
/* istanbul ignore next — exhaustiveness */
|
|
4945
|
+
default:
|
|
4946
|
+
return null;
|
|
4947
|
+
}
|
|
4948
|
+
};
|
|
4949
|
+
const hasFooter = resolvedCtas.length > 0 || !!action;
|
|
4950
|
+
return /* @__PURE__ */ React15.createElement(Paper10, { sx: { p: 2 } }, /* @__PURE__ */ React15.createElement(Box14, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ React15.createElement(Typography14, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ React15.createElement(
|
|
4951
|
+
StatusPill,
|
|
4952
|
+
{
|
|
4953
|
+
label: summaryText,
|
|
4954
|
+
tone: budgetTone2(headline.closest?.pct ?? 0),
|
|
4955
|
+
dot: headline.count > 0
|
|
4956
|
+
}
|
|
4957
|
+
)), loading && /* @__PURE__ */ React15.createElement(Box14, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ React15.createElement(CircularProgress8, { size: 28 })), !loading && error && /* @__PURE__ */ React15.createElement(Alert8, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Box14, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ React15.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref: keysTabHref }))), /* @__PURE__ */ React15.createElement(Collapse4, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ React15.createElement(
|
|
4958
|
+
Box14,
|
|
4959
|
+
{
|
|
4960
|
+
sx: {
|
|
4961
|
+
mt: 1.5,
|
|
4962
|
+
// Bound the expanded list so a user with many limits still
|
|
4963
|
+
// keeps the card compact and the toggle within reach.
|
|
4964
|
+
maxHeight: 300,
|
|
4965
|
+
overflowY: "auto"
|
|
4966
|
+
}
|
|
4967
|
+
},
|
|
4968
|
+
/* @__PURE__ */ React15.createElement(LimitListPanel, null, /* @__PURE__ */ React15.createElement(
|
|
4969
|
+
BudgetLimitList,
|
|
4970
|
+
{
|
|
4971
|
+
limits: expandedLimits,
|
|
4972
|
+
hiddenKeyCount: expandedSummary.hiddenBudgetedKeys,
|
|
4973
|
+
keysHref: keysTabHref
|
|
4974
|
+
}
|
|
4975
|
+
))
|
|
4976
|
+
))), hasFooter && /* @__PURE__ */ React15.createElement(
|
|
4977
|
+
Box14,
|
|
4978
|
+
{
|
|
4979
|
+
sx: (theme) => ({
|
|
4980
|
+
mt: 2,
|
|
4981
|
+
pt: 2,
|
|
4982
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
4983
|
+
display: "flex",
|
|
4984
|
+
alignItems: "center",
|
|
4985
|
+
gap: 1,
|
|
4986
|
+
flexWrap: "wrap"
|
|
4987
|
+
})
|
|
4988
|
+
},
|
|
4989
|
+
resolvedCtas.map(renderCta),
|
|
4990
|
+
action
|
|
4991
|
+
));
|
|
4992
|
+
};
|
|
4993
|
+
|
|
4994
|
+
// src/index.ts
|
|
4519
4995
|
init_GenerateKeyDialog();
|
|
4520
4996
|
init_ManageTeamDialog();
|
|
4521
4997
|
init_api();
|
|
@@ -4524,6 +5000,7 @@ export {
|
|
|
4524
5000
|
DashboardHeader,
|
|
4525
5001
|
GenerateKeyDialog,
|
|
4526
5002
|
KeysTable,
|
|
5003
|
+
LiteLLMBudgetGauges,
|
|
4527
5004
|
LiteLLMBudgetWidget,
|
|
4528
5005
|
LiteLLMHomeWidget,
|
|
4529
5006
|
LiteLLMPage,
|