@acarmisc/backstage-plugin-litellm 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/GenerateKeyButton.d.ts +18 -0
- package/dist/components/LiteLLMBudgetGauges.d.ts +23 -10
- package/dist/index.cjs.js +695 -516
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +561 -382
- package/dist/index.esm.js.map +4 -4
- package/package.json +1 -1
- package/dist/SearchInput.d.ts +0 -1
- package/dist/components/SearchInput.d.ts +0 -9
package/dist/index.cjs.js
CHANGED
|
@@ -858,6 +858,66 @@ var init_ui = __esm({
|
|
|
858
858
|
}
|
|
859
859
|
});
|
|
860
860
|
|
|
861
|
+
// src/components/GenerateKeyButton.tsx
|
|
862
|
+
var import_react2, import_Button, import_icons_material, import_core_components, GenerateKeyButton;
|
|
863
|
+
var init_GenerateKeyButton = __esm({
|
|
864
|
+
"src/components/GenerateKeyButton.tsx"() {
|
|
865
|
+
"use strict";
|
|
866
|
+
import_react2 = __toESM(require("react"));
|
|
867
|
+
import_Button = __toESM(require("@mui/material/Button"));
|
|
868
|
+
import_icons_material = require("@mui/icons-material");
|
|
869
|
+
import_core_components = require("@backstage/core-components");
|
|
870
|
+
GenerateKeyButton = ({
|
|
871
|
+
to,
|
|
872
|
+
onClick,
|
|
873
|
+
label = "Generate New Key",
|
|
874
|
+
size
|
|
875
|
+
}) => {
|
|
876
|
+
if (onClick) {
|
|
877
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
878
|
+
import_Button.default,
|
|
879
|
+
{
|
|
880
|
+
variant: "contained",
|
|
881
|
+
color: "primary",
|
|
882
|
+
disableElevation: true,
|
|
883
|
+
size,
|
|
884
|
+
startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
|
|
885
|
+
onClick
|
|
886
|
+
},
|
|
887
|
+
label
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
if (to) {
|
|
891
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
892
|
+
import_Button.default,
|
|
893
|
+
{
|
|
894
|
+
variant: "contained",
|
|
895
|
+
color: "primary",
|
|
896
|
+
disableElevation: true,
|
|
897
|
+
size,
|
|
898
|
+
startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
|
|
899
|
+
component: import_core_components.Link,
|
|
900
|
+
to
|
|
901
|
+
},
|
|
902
|
+
label
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
906
|
+
import_Button.default,
|
|
907
|
+
{
|
|
908
|
+
variant: "contained",
|
|
909
|
+
color: "primary",
|
|
910
|
+
disableElevation: true,
|
|
911
|
+
size,
|
|
912
|
+
startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
|
|
913
|
+
disabled: true
|
|
914
|
+
},
|
|
915
|
+
label
|
|
916
|
+
);
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
|
|
861
921
|
// src/components/DashboardHeader.tsx
|
|
862
922
|
function initials(name) {
|
|
863
923
|
const local = name.split("@")[0] ?? name;
|
|
@@ -865,24 +925,23 @@ function initials(name) {
|
|
|
865
925
|
if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
866
926
|
return local.slice(0, 2).toUpperCase();
|
|
867
927
|
}
|
|
868
|
-
var
|
|
928
|
+
var import_react3, import_Box2, import_Typography2, import_Skeleton, import_Paper2, import_Divider, import_styles2, CountStat, DashboardHeader;
|
|
869
929
|
var init_DashboardHeader = __esm({
|
|
870
930
|
"src/components/DashboardHeader.tsx"() {
|
|
871
931
|
"use strict";
|
|
872
|
-
|
|
932
|
+
import_react3 = __toESM(require("react"));
|
|
873
933
|
import_Box2 = __toESM(require("@mui/material/Box"));
|
|
874
934
|
import_Typography2 = __toESM(require("@mui/material/Typography"));
|
|
875
935
|
import_Skeleton = __toESM(require("@mui/material/Skeleton"));
|
|
876
936
|
import_Paper2 = __toESM(require("@mui/material/Paper"));
|
|
877
|
-
import_Button = __toESM(require("@mui/material/Button"));
|
|
878
937
|
import_Divider = __toESM(require("@mui/material/Divider"));
|
|
879
938
|
import_styles2 = require("@mui/material/styles");
|
|
880
|
-
import_icons_material = require("@mui/icons-material");
|
|
881
939
|
init_api();
|
|
882
940
|
init_ui();
|
|
941
|
+
init_GenerateKeyButton();
|
|
883
942
|
CountStat = ({ value, label, tone }) => {
|
|
884
943
|
const active = value > 0;
|
|
885
|
-
return /* @__PURE__ */
|
|
944
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ import_react3.default.createElement(
|
|
886
945
|
import_Box2.default,
|
|
887
946
|
{
|
|
888
947
|
sx: (theme) => ({
|
|
@@ -893,14 +952,14 @@ var init_DashboardHeader = __esm({
|
|
|
893
952
|
bgcolor: active ? tone === "danger" && theme.palette.error.main || tone === "warning" && theme.palette.warning.main || theme.palette.primary.main : (0, import_styles2.alpha)(theme.palette.text.primary, 0.25)
|
|
894
953
|
})
|
|
895
954
|
}
|
|
896
|
-
), /* @__PURE__ */
|
|
955
|
+
), /* @__PURE__ */ import_react3.default.createElement(
|
|
897
956
|
import_Typography2.default,
|
|
898
957
|
{
|
|
899
958
|
component: "span",
|
|
900
959
|
sx: { fontSize: 15, fontWeight: 700, lineHeight: 1, fontVariantNumeric: "tabular-nums" }
|
|
901
960
|
},
|
|
902
961
|
value
|
|
903
|
-
), /* @__PURE__ */
|
|
962
|
+
), /* @__PURE__ */ import_react3.default.createElement(
|
|
904
963
|
import_Typography2.default,
|
|
905
964
|
{
|
|
906
965
|
component: "span",
|
|
@@ -918,13 +977,13 @@ var init_DashboardHeader = __esm({
|
|
|
918
977
|
onGenerateKeyClick,
|
|
919
978
|
tabs
|
|
920
979
|
}) => {
|
|
921
|
-
const counts = (0,
|
|
980
|
+
const counts = (0, import_react3.useMemo)(() => {
|
|
922
981
|
const expired = keys.filter((k) => expiryStatus(k.expires_at) === "expired").length;
|
|
923
982
|
const expiringSoon = keys.filter((k) => expiryStatus(k.expires_at) === "soon").length;
|
|
924
983
|
return { total: keys.length, expired, expiringSoon };
|
|
925
984
|
}, [keys]);
|
|
926
985
|
const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;
|
|
927
|
-
return /* @__PURE__ */
|
|
986
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_Paper2.default, { sx: { borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ import_react3.default.createElement(
|
|
928
987
|
import_Box2.default,
|
|
929
988
|
{
|
|
930
989
|
sx: {
|
|
@@ -937,7 +996,7 @@ var init_DashboardHeader = __esm({
|
|
|
937
996
|
pb: 2
|
|
938
997
|
}
|
|
939
998
|
},
|
|
940
|
-
/* @__PURE__ */
|
|
999
|
+
/* @__PURE__ */ import_react3.default.createElement(
|
|
941
1000
|
import_Box2.default,
|
|
942
1001
|
{
|
|
943
1002
|
sx: (theme) => ({
|
|
@@ -957,7 +1016,7 @@ var init_DashboardHeader = __esm({
|
|
|
957
1016
|
},
|
|
958
1017
|
initials(displayName)
|
|
959
1018
|
),
|
|
960
|
-
/* @__PURE__ */
|
|
1019
|
+
/* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { flexGrow: 1, minWidth: 220 } }, /* @__PURE__ */ import_react3.default.createElement(import_Typography2.default, { variant: "h6", sx: { lineHeight: 1.25, wordBreak: "break-word" } }, displayName), /* @__PURE__ */ import_react3.default.createElement(
|
|
961
1020
|
import_Typography2.default,
|
|
962
1021
|
{
|
|
963
1022
|
variant: "caption",
|
|
@@ -965,7 +1024,7 @@ var init_DashboardHeader = __esm({
|
|
|
965
1024
|
sx: { display: "block", fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
966
1025
|
},
|
|
967
1026
|
userInfo.user_id
|
|
968
|
-
), /* @__PURE__ */
|
|
1027
|
+
), /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { display: "flex", gap: 0.75, flexWrap: "wrap", mt: 1, minHeight: 20 } }, loading ? /* @__PURE__ */ import_react3.default.createElement(import_Skeleton.default, { variant: "rounded", width: 140, height: 20 }) : teams.map((team) => /* @__PURE__ */ import_react3.default.createElement(
|
|
969
1028
|
TagChip,
|
|
970
1029
|
{
|
|
971
1030
|
key: team.team_id,
|
|
@@ -973,18 +1032,8 @@ var init_DashboardHeader = __esm({
|
|
|
973
1032
|
title: team.team_id
|
|
974
1033
|
}
|
|
975
1034
|
)))),
|
|
976
|
-
/* @__PURE__ */
|
|
977
|
-
|
|
978
|
-
{
|
|
979
|
-
variant: "contained",
|
|
980
|
-
color: "primary",
|
|
981
|
-
disableElevation: true,
|
|
982
|
-
startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
|
|
983
|
-
onClick: onGenerateKeyClick
|
|
984
|
-
},
|
|
985
|
-
"Generate New Key"
|
|
986
|
-
)
|
|
987
|
-
), /* @__PURE__ */ import_react2.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react2.default.createElement(
|
|
1035
|
+
/* @__PURE__ */ import_react3.default.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick })
|
|
1036
|
+
), /* @__PURE__ */ import_react3.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react3.default.createElement(
|
|
988
1037
|
import_Box2.default,
|
|
989
1038
|
{
|
|
990
1039
|
sx: {
|
|
@@ -996,12 +1045,12 @@ var init_DashboardHeader = __esm({
|
|
|
996
1045
|
py: 1.5
|
|
997
1046
|
}
|
|
998
1047
|
},
|
|
999
|
-
/* @__PURE__ */
|
|
1000
|
-
/* @__PURE__ */
|
|
1001
|
-
/* @__PURE__ */
|
|
1002
|
-
/* @__PURE__ */
|
|
1003
|
-
/* @__PURE__ */
|
|
1004
|
-
), tabs && /* @__PURE__ */
|
|
1048
|
+
/* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.total, label: "keys", tone: "accent" }),
|
|
1049
|
+
/* @__PURE__ */ import_react3.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1050
|
+
/* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.expired, label: "expired", tone: "danger" }),
|
|
1051
|
+
/* @__PURE__ */ import_react3.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1052
|
+
/* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.expiringSoon, label: "expiring soon", tone: "warning" })
|
|
1053
|
+
), tabs && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { px: 1 } }, tabs)));
|
|
1005
1054
|
};
|
|
1006
1055
|
}
|
|
1007
1056
|
});
|
|
@@ -1020,12 +1069,12 @@ function expiryTone(status) {
|
|
|
1020
1069
|
function ExpiryCell({ expiresAt }) {
|
|
1021
1070
|
const status = expiryStatus(expiresAt);
|
|
1022
1071
|
if (!status) {
|
|
1023
|
-
return /* @__PURE__ */
|
|
1072
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, "Never");
|
|
1024
1073
|
}
|
|
1025
1074
|
if (status === "ok") {
|
|
1026
|
-
return /* @__PURE__ */
|
|
1075
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, formatDate(expiresAt));
|
|
1027
1076
|
}
|
|
1028
|
-
return /* @__PURE__ */
|
|
1077
|
+
return /* @__PURE__ */ import_react4.default.createElement(
|
|
1029
1078
|
StatusPill,
|
|
1030
1079
|
{
|
|
1031
1080
|
label: expiryChipLabel(status, expiresAt),
|
|
@@ -1040,16 +1089,16 @@ function budgetTone(pct) {
|
|
|
1040
1089
|
return "accent";
|
|
1041
1090
|
}
|
|
1042
1091
|
function keyBlockIcon(isBlocking, blocked) {
|
|
1043
|
-
if (isBlocking) return /* @__PURE__ */
|
|
1044
|
-
if (blocked) return /* @__PURE__ */
|
|
1045
|
-
return /* @__PURE__ */
|
|
1092
|
+
if (isBlocking) return /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 18 });
|
|
1093
|
+
if (blocked) return /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.LockOpen, { fontSize: "small" });
|
|
1094
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Lock, { fontSize: "small" });
|
|
1046
1095
|
}
|
|
1047
1096
|
function BudgetCell({ spend, maxBudget }) {
|
|
1048
1097
|
if (!maxBudget) {
|
|
1049
|
-
return /* @__PURE__ */
|
|
1098
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, "$", spend.toFixed(2), /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { component: "span", variant: "caption", sx: { ml: 0.5, opacity: 0.7 } }, "/ \u221E"));
|
|
1050
1099
|
}
|
|
1051
1100
|
const pct = Math.min(100, spend / maxBudget * 100);
|
|
1052
|
-
return /* @__PURE__ */
|
|
1101
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { minWidth: 104 }, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums", mb: 0.5 } }, "$", spend.toFixed(2), /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 0.5 } }, "/ $", maxBudget)), /* @__PURE__ */ import_react4.default.createElement(Meter, { value: pct, tone: budgetTone(pct), height: 4 }));
|
|
1053
1102
|
}
|
|
1054
1103
|
function compareKeys(a, b, sortKey) {
|
|
1055
1104
|
switch (sortKey) {
|
|
@@ -1069,11 +1118,11 @@ function compareKeys(a, b, sortKey) {
|
|
|
1069
1118
|
return 0;
|
|
1070
1119
|
}
|
|
1071
1120
|
}
|
|
1072
|
-
var
|
|
1121
|
+
var import_react4, import_Table, import_TableBody, import_TableCell, import_TableContainer, import_TableHead, import_TableRow, import_TableSortLabel, import_Button2, import_IconButton, import_Box3, import_Typography3, import_Dialog, import_DialogTitle, import_DialogContent, import_DialogActions, import_TextField, import_CircularProgress, import_Skeleton2, import_InputAdornment, import_styles3, import_icons_material2, shortKeyId, formatDate, KeysTable;
|
|
1073
1122
|
var init_KeysTable = __esm({
|
|
1074
1123
|
"src/components/KeysTable.tsx"() {
|
|
1075
1124
|
"use strict";
|
|
1076
|
-
|
|
1125
|
+
import_react4 = __toESM(require("react"));
|
|
1077
1126
|
import_Table = __toESM(require("@mui/material/Table"));
|
|
1078
1127
|
import_TableBody = __toESM(require("@mui/material/TableBody"));
|
|
1079
1128
|
import_TableCell = __toESM(require("@mui/material/TableCell"));
|
|
@@ -1096,6 +1145,7 @@ var init_KeysTable = __esm({
|
|
|
1096
1145
|
import_styles3 = require("@mui/material/styles");
|
|
1097
1146
|
import_icons_material2 = require("@mui/icons-material");
|
|
1098
1147
|
init_api();
|
|
1148
|
+
init_GenerateKeyButton();
|
|
1099
1149
|
init_ui();
|
|
1100
1150
|
shortKeyId = (token) => {
|
|
1101
1151
|
if (!token) return "-";
|
|
@@ -1119,12 +1169,12 @@ var init_KeysTable = __esm({
|
|
|
1119
1169
|
onDeleteKey,
|
|
1120
1170
|
onPruneExpiredKeys
|
|
1121
1171
|
}) => {
|
|
1122
|
-
const [blockingKeyId, setBlockingKeyId] = (0,
|
|
1123
|
-
const [deleteConfirmId, setDeleteConfirmId] = (0,
|
|
1124
|
-
const [deleteSubmitting, setDeleteSubmitting] = (0,
|
|
1125
|
-
const [filterText, setFilterText] = (0,
|
|
1126
|
-
const [sortKey, setSortKey] = (0,
|
|
1127
|
-
const [sortDirection, setSortDirection] = (0,
|
|
1172
|
+
const [blockingKeyId, setBlockingKeyId] = (0, import_react4.useState)(null);
|
|
1173
|
+
const [deleteConfirmId, setDeleteConfirmId] = (0, import_react4.useState)(null);
|
|
1174
|
+
const [deleteSubmitting, setDeleteSubmitting] = (0, import_react4.useState)(false);
|
|
1175
|
+
const [filterText, setFilterText] = (0, import_react4.useState)("");
|
|
1176
|
+
const [sortKey, setSortKey] = (0, import_react4.useState)("created");
|
|
1177
|
+
const [sortDirection, setSortDirection] = (0, import_react4.useState)("desc");
|
|
1128
1178
|
const handleSort = (nextSortKey) => {
|
|
1129
1179
|
if (sortKey === nextSortKey) {
|
|
1130
1180
|
setSortDirection((current) => current === "asc" ? "desc" : "asc");
|
|
@@ -1133,7 +1183,7 @@ var init_KeysTable = __esm({
|
|
|
1133
1183
|
setSortDirection("asc");
|
|
1134
1184
|
}
|
|
1135
1185
|
};
|
|
1136
|
-
const filteredKeys = (0,
|
|
1186
|
+
const filteredKeys = (0, import_react4.useMemo)(() => {
|
|
1137
1187
|
let result = keys;
|
|
1138
1188
|
if (filterText.trim()) {
|
|
1139
1189
|
const q = filterText.toLowerCase();
|
|
@@ -1175,9 +1225,9 @@ var init_KeysTable = __esm({
|
|
|
1175
1225
|
const copyToClipboard = (text) => {
|
|
1176
1226
|
navigator.clipboard.writeText(text);
|
|
1177
1227
|
};
|
|
1178
|
-
const [pruneConfirmCount, setPruneConfirmCount] = (0,
|
|
1179
|
-
const [pruneSubmitting, setPruneSubmitting] = (0,
|
|
1180
|
-
const expiredKeys = (0,
|
|
1228
|
+
const [pruneConfirmCount, setPruneConfirmCount] = (0, import_react4.useState)(null);
|
|
1229
|
+
const [pruneSubmitting, setPruneSubmitting] = (0, import_react4.useState)(false);
|
|
1230
|
+
const expiredKeys = (0, import_react4.useMemo)(() => {
|
|
1181
1231
|
return keys.filter((k) => expiryStatus(k.expires_at) === "expired");
|
|
1182
1232
|
}, [keys]);
|
|
1183
1233
|
const handlePruneExpired = async () => {
|
|
@@ -1194,32 +1244,27 @@ var init_KeysTable = __esm({
|
|
|
1194
1244
|
};
|
|
1195
1245
|
const renderTableBody = () => {
|
|
1196
1246
|
if (loading) {
|
|
1197
|
-
return /* @__PURE__ */
|
|
1247
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { colSpan: 8, sx: { py: 2 } }, /* @__PURE__ */ import_react4.default.createElement(import_Skeleton2.default, { variant: "rounded", height: 140 })));
|
|
1198
1248
|
}
|
|
1199
1249
|
if (filteredKeys.length === 0) {
|
|
1200
|
-
return /* @__PURE__ */
|
|
1250
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { colSpan: 8 }, filterText ? /* @__PURE__ */ import_react4.default.createElement(
|
|
1201
1251
|
EmptyState,
|
|
1202
1252
|
{
|
|
1203
1253
|
message: "No keys match that filter",
|
|
1204
1254
|
hint: "Try a different alias or model name."
|
|
1205
1255
|
}
|
|
1206
|
-
) : /* @__PURE__ */
|
|
1256
|
+
) : /* @__PURE__ */ import_react4.default.createElement(
|
|
1207
1257
|
EmptyState,
|
|
1208
1258
|
{
|
|
1209
1259
|
message: "No keys yet",
|
|
1210
1260
|
hint: "Generate your first key to start calling models.",
|
|
1211
|
-
action: /* @__PURE__ */
|
|
1212
|
-
|
|
1261
|
+
action: /* @__PURE__ */ import_react4.default.createElement(
|
|
1262
|
+
GenerateKeyButton,
|
|
1213
1263
|
{
|
|
1214
|
-
variant: "contained",
|
|
1215
|
-
color: "primary",
|
|
1216
1264
|
size: "small",
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
sx: { mt: 1 }
|
|
1221
|
-
},
|
|
1222
|
-
"Generate Your First Key"
|
|
1265
|
+
label: "Generate Your First Key",
|
|
1266
|
+
onClick: onGenerateKeyClick
|
|
1267
|
+
}
|
|
1223
1268
|
)
|
|
1224
1269
|
}
|
|
1225
1270
|
)));
|
|
@@ -1228,7 +1273,7 @@ var init_KeysTable = __esm({
|
|
|
1228
1273
|
const keyId = key.token ?? key.key;
|
|
1229
1274
|
const isBlocking = blockingKeyId === keyId;
|
|
1230
1275
|
const keyModels = key.models ?? [];
|
|
1231
|
-
return /* @__PURE__ */
|
|
1276
|
+
return /* @__PURE__ */ import_react4.default.createElement(
|
|
1232
1277
|
import_TableRow.default,
|
|
1233
1278
|
{
|
|
1234
1279
|
key: keyId,
|
|
@@ -1239,8 +1284,8 @@ var init_KeysTable = __esm({
|
|
|
1239
1284
|
"& td": { color: theme.palette.text.secondary }
|
|
1240
1285
|
}) : void 0
|
|
1241
1286
|
},
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */
|
|
1287
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2" }, key.key_alias || "\u2014"), key.blocked && /* @__PURE__ */ import_react4.default.createElement(StatusPill, { label: "Blocked", tone: "danger" }))),
|
|
1288
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ import_react4.default.createElement(
|
|
1244
1289
|
import_Typography3.default,
|
|
1245
1290
|
{
|
|
1246
1291
|
variant: "body2",
|
|
@@ -1257,7 +1302,7 @@ var init_KeysTable = __esm({
|
|
|
1257
1302
|
})
|
|
1258
1303
|
},
|
|
1259
1304
|
shortKeyId(keyId)
|
|
1260
|
-
), /* @__PURE__ */
|
|
1305
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
1261
1306
|
import_IconButton.default,
|
|
1262
1307
|
{
|
|
1263
1308
|
size: "small",
|
|
@@ -1265,13 +1310,13 @@ var init_KeysTable = __esm({
|
|
|
1265
1310
|
title: "Copy Key ID",
|
|
1266
1311
|
sx: quietIconButtonSx("accent")
|
|
1267
1312
|
},
|
|
1268
|
-
/* @__PURE__ */
|
|
1313
|
+
/* @__PURE__ */ import_react4.default.createElement(import_icons_material2.ContentCopy, { sx: { fontSize: 15 } })
|
|
1269
1314
|
))),
|
|
1270
|
-
/* @__PURE__ */
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
/* @__PURE__ */
|
|
1273
|
-
/* @__PURE__ */
|
|
1274
|
-
/* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, formatDate(key.created_at))),
|
|
1316
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(ExpiryCell, { expiresAt: key.expires_at })),
|
|
1317
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })),
|
|
1318
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, key.tpm_limit ?? "\u221E", " / ", key.rpm_limit ?? "\u221E")),
|
|
1319
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, keyModels.length === 0 ? /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, "All models") : /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, keyModels.slice(0, 2).map((model) => /* @__PURE__ */ import_react4.default.createElement(TagChip, { key: model, label: model, title: model })), keyModels.length > 2 && /* @__PURE__ */ import_react4.default.createElement(
|
|
1275
1320
|
TagChip,
|
|
1276
1321
|
{
|
|
1277
1322
|
label: `+${keyModels.length - 2}`,
|
|
@@ -1279,7 +1324,7 @@ var init_KeysTable = __esm({
|
|
|
1279
1324
|
title: keyModels.slice(2).join(", ")
|
|
1280
1325
|
}
|
|
1281
1326
|
))),
|
|
1282
|
-
/* @__PURE__ */
|
|
1327
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { align: "right" }, /* @__PURE__ */ import_react4.default.createElement(import_Box3.default, { display: "flex", justifyContent: "flex-end", gap: 0.25 }, /* @__PURE__ */ import_react4.default.createElement(
|
|
1283
1328
|
import_IconButton.default,
|
|
1284
1329
|
{
|
|
1285
1330
|
size: "small",
|
|
@@ -1287,8 +1332,8 @@ var init_KeysTable = __esm({
|
|
|
1287
1332
|
title: "Edit key",
|
|
1288
1333
|
sx: quietIconButtonSx("accent")
|
|
1289
1334
|
},
|
|
1290
|
-
/* @__PURE__ */
|
|
1291
|
-
), /* @__PURE__ */
|
|
1335
|
+
/* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Edit, { fontSize: "small" })
|
|
1336
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
1292
1337
|
import_IconButton.default,
|
|
1293
1338
|
{
|
|
1294
1339
|
size: "small",
|
|
@@ -1298,7 +1343,7 @@ var init_KeysTable = __esm({
|
|
|
1298
1343
|
sx: quietIconButtonSx("warning")
|
|
1299
1344
|
},
|
|
1300
1345
|
keyBlockIcon(isBlocking, key.blocked)
|
|
1301
|
-
), /* @__PURE__ */
|
|
1346
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
1302
1347
|
import_IconButton.default,
|
|
1303
1348
|
{
|
|
1304
1349
|
size: "small",
|
|
@@ -1306,18 +1351,18 @@ var init_KeysTable = __esm({
|
|
|
1306
1351
|
title: "Revoke key",
|
|
1307
1352
|
sx: quietIconButtonSx("danger")
|
|
1308
1353
|
},
|
|
1309
|
-
/* @__PURE__ */
|
|
1354
|
+
/* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Delete, { fontSize: "small" })
|
|
1310
1355
|
)))
|
|
1311
1356
|
);
|
|
1312
1357
|
});
|
|
1313
1358
|
};
|
|
1314
|
-
return /* @__PURE__ */
|
|
1359
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
|
|
1315
1360
|
SectionCard,
|
|
1316
1361
|
{
|
|
1317
1362
|
title: "Virtual Keys",
|
|
1318
1363
|
subtitle: loading ? "Loading\u2026" : `${filteredKeys.length}${filterText ? ` of ${keys.length}` : ""} key${filteredKeys.length === 1 ? "" : "s"}`,
|
|
1319
1364
|
flush: true,
|
|
1320
|
-
actions: /* @__PURE__ */
|
|
1365
|
+
actions: /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
|
|
1321
1366
|
import_TextField.default,
|
|
1322
1367
|
{
|
|
1323
1368
|
size: "small",
|
|
@@ -1327,15 +1372,15 @@ var init_KeysTable = __esm({
|
|
|
1327
1372
|
onChange: (e) => setFilterText(e.target.value),
|
|
1328
1373
|
sx: { minWidth: 240 },
|
|
1329
1374
|
InputProps: {
|
|
1330
|
-
startAdornment: /* @__PURE__ */
|
|
1375
|
+
startAdornment: /* @__PURE__ */ import_react4.default.createElement(import_InputAdornment.default, { position: "start" }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Search, { fontSize: "small", color: "disabled" }))
|
|
1331
1376
|
}
|
|
1332
1377
|
}
|
|
1333
|
-
), expiredKeys.length > 0 && /* @__PURE__ */
|
|
1378
|
+
), expiredKeys.length > 0 && /* @__PURE__ */ import_react4.default.createElement(
|
|
1334
1379
|
import_Button2.default,
|
|
1335
1380
|
{
|
|
1336
1381
|
variant: "outlined",
|
|
1337
1382
|
color: "inherit",
|
|
1338
|
-
startIcon: /* @__PURE__ */
|
|
1383
|
+
startIcon: /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Autorenew, null),
|
|
1339
1384
|
onClick: () => setPruneConfirmCount(expiredKeys.length),
|
|
1340
1385
|
sx: (theme) => ({
|
|
1341
1386
|
color: theme.palette.text.secondary,
|
|
@@ -1350,20 +1395,10 @@ var init_KeysTable = __esm({
|
|
|
1350
1395
|
"Prune expired (",
|
|
1351
1396
|
expiredKeys.length,
|
|
1352
1397
|
")"
|
|
1353
|
-
), /* @__PURE__ */
|
|
1354
|
-
import_Button2.default,
|
|
1355
|
-
{
|
|
1356
|
-
variant: "contained",
|
|
1357
|
-
color: "primary",
|
|
1358
|
-
disableElevation: true,
|
|
1359
|
-
startIcon: /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Add, null),
|
|
1360
|
-
onClick: onGenerateKeyClick
|
|
1361
|
-
},
|
|
1362
|
-
"Generate New Key"
|
|
1363
|
-
))
|
|
1398
|
+
), /* @__PURE__ */ import_react4.default.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick }))
|
|
1364
1399
|
},
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
), /* @__PURE__ */
|
|
1400
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableContainer.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Table.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react4.default.createElement(import_TableHead.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableRow.default, null, /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "alias" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "alias", direction: sortKey === "alias" ? sortDirection : "asc", onClick: () => handleSort("alias") }, "Alias")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, "Key ID"), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "created" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "created", direction: sortKey === "created" ? sortDirection : "asc", onClick: () => handleSort("created") }, "Created")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "expires" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "expires", direction: sortKey === "expires" ? sortDirection : "asc", onClick: () => handleSort("expires") }, "Expires")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "budget" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "budget", direction: sortKey === "budget" ? sortDirection : "asc", onClick: () => handleSort("budget") }, "Budget")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "limits" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "limits", direction: sortKey === "limits" ? sortDirection : "asc", onClick: () => handleSort("limits") }, "TPM / RPM")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { sortDirection: sortKey === "models" ? sortDirection : false }, /* @__PURE__ */ import_react4.default.createElement(import_TableSortLabel.default, { active: sortKey === "models", direction: sortKey === "models" ? sortDirection : "asc", onClick: () => handleSort("models") }, "Models")), /* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, { align: "right" }, "Actions"))), /* @__PURE__ */ import_react4.default.createElement(import_TableBody.default, null, renderTableBody())))
|
|
1401
|
+
), /* @__PURE__ */ import_react4.default.createElement(import_Dialog.default, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react4.default.createElement(import_DialogTitle.default, null, "Revoke Key?"), /* @__PURE__ */ import_react4.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ import_react4.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Button2.default, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ import_react4.default.createElement(
|
|
1367
1402
|
import_Button2.default,
|
|
1368
1403
|
{
|
|
1369
1404
|
onClick: handleConfirmDelete,
|
|
@@ -1371,8 +1406,8 @@ var init_KeysTable = __esm({
|
|
|
1371
1406
|
color: "error",
|
|
1372
1407
|
disabled: deleteSubmitting
|
|
1373
1408
|
},
|
|
1374
|
-
deleteSubmitting ? /* @__PURE__ */
|
|
1375
|
-
))), /* @__PURE__ */
|
|
1409
|
+
deleteSubmitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 20 }) : "Revoke"
|
|
1410
|
+
))), /* @__PURE__ */ import_react4.default.createElement(import_Dialog.default, { open: pruneConfirmCount !== null, onClose: () => setPruneConfirmCount(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react4.default.createElement(import_DialogTitle.default, null, "Prune Expired Keys?"), /* @__PURE__ */ import_react4.default.createElement(import_DialogContent.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, null, "This will permanently delete ", pruneConfirmCount, " expired key", pruneConfirmCount !== 1 ? "s" : "", " from LiteLLM. Any integrations using them will stop working immediately.")), /* @__PURE__ */ import_react4.default.createElement(import_DialogActions.default, null, /* @__PURE__ */ import_react4.default.createElement(import_Button2.default, { onClick: () => setPruneConfirmCount(null), disabled: pruneSubmitting }, "Cancel"), /* @__PURE__ */ import_react4.default.createElement(
|
|
1376
1411
|
import_Button2.default,
|
|
1377
1412
|
{
|
|
1378
1413
|
onClick: handlePruneExpired,
|
|
@@ -1380,7 +1415,7 @@ var init_KeysTable = __esm({
|
|
|
1380
1415
|
color: "error",
|
|
1381
1416
|
disabled: pruneSubmitting
|
|
1382
1417
|
},
|
|
1383
|
-
pruneSubmitting ? /* @__PURE__ */
|
|
1418
|
+
pruneSubmitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 20 }) : "Prune"
|
|
1384
1419
|
))));
|
|
1385
1420
|
};
|
|
1386
1421
|
}
|
|
@@ -1518,11 +1553,11 @@ function formatContextWindow(maxInput, maxOutput) {
|
|
|
1518
1553
|
if (outPart) return `ctx ${outPart} out`;
|
|
1519
1554
|
return null;
|
|
1520
1555
|
}
|
|
1521
|
-
var
|
|
1556
|
+
var import_react5, import_Box4, import_Typography4, import_Dialog2, import_DialogTitle2, import_DialogContent2, import_DialogActions2, import_TextField2, import_MenuItem, import_Button3, import_IconButton2, import_CircularProgress2, import_Autocomplete, import_Checkbox, import_FormControlLabel, import_Alert, import_Accordion, import_AccordionSummary, import_AccordionDetails, import_Tabs, import_Tab, import_icons_material3, generateDefaultAlias, createForm, editForm, ALL_PROXY_MODELS, SNIPPET_FILE_HINTS, SnippetTabs, KeyFormDialog;
|
|
1522
1557
|
var init_KeyFormDialog = __esm({
|
|
1523
1558
|
"src/components/KeyFormDialog.tsx"() {
|
|
1524
1559
|
"use strict";
|
|
1525
|
-
|
|
1560
|
+
import_react5 = __toESM(require("react"));
|
|
1526
1561
|
import_Box4 = __toESM(require("@mui/material/Box"));
|
|
1527
1562
|
import_Typography4 = __toESM(require("@mui/material/Typography"));
|
|
1528
1563
|
import_Dialog2 = __toESM(require("@mui/material/Dialog"));
|
|
@@ -1574,18 +1609,18 @@ var init_KeyFormDialog = __esm({
|
|
|
1574
1609
|
"claude-code": "Requires Claude Code CLI installed"
|
|
1575
1610
|
};
|
|
1576
1611
|
SnippetTabs = ({ snippets, model, onCopy }) => {
|
|
1577
|
-
const [tab, setTab] = (0,
|
|
1612
|
+
const [tab, setTab] = (0, import_react5.useState)("curl");
|
|
1578
1613
|
const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
|
|
1579
1614
|
const code = snippets[snippetKey];
|
|
1580
1615
|
const fileHint = SNIPPET_FILE_HINTS[tab];
|
|
1581
|
-
return /* @__PURE__ */
|
|
1616
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Tabs.default, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 }, variant: "scrollable" }, /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "curl", value: "curl" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "OpenAI SDK", value: "openai" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "opencode", value: "opencode" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "pi", value: "pi" }), /* @__PURE__ */ import_react5.default.createElement(import_Tab.default, { label: "Claude Code", value: "claude-code" })), fileHint && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, fileHint), /* @__PURE__ */ import_react5.default.createElement(
|
|
1582
1617
|
import_Box4.default,
|
|
1583
1618
|
{
|
|
1584
1619
|
position: "relative",
|
|
1585
1620
|
p: 1.5,
|
|
1586
1621
|
sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
|
|
1587
1622
|
},
|
|
1588
|
-
/* @__PURE__ */
|
|
1623
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1589
1624
|
import_IconButton2.default,
|
|
1590
1625
|
{
|
|
1591
1626
|
size: "small",
|
|
@@ -1593,9 +1628,9 @@ var init_KeyFormDialog = __esm({
|
|
|
1593
1628
|
title: "Copy snippet",
|
|
1594
1629
|
sx: { position: "absolute", top: 4, right: 4 }
|
|
1595
1630
|
},
|
|
1596
|
-
/* @__PURE__ */
|
|
1631
|
+
/* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" })
|
|
1597
1632
|
),
|
|
1598
|
-
/* @__PURE__ */
|
|
1633
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1599
1634
|
import_Typography4.default,
|
|
1600
1635
|
{
|
|
1601
1636
|
component: "pre",
|
|
@@ -1610,7 +1645,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1610
1645
|
},
|
|
1611
1646
|
code
|
|
1612
1647
|
),
|
|
1613
|
-
model && /* @__PURE__ */
|
|
1648
|
+
model && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mt: 1 }, "Using model \u201C", model, "\u201D \u2014 swap it for any model you have access to.")
|
|
1614
1649
|
));
|
|
1615
1650
|
};
|
|
1616
1651
|
KeyFormDialog = ({
|
|
@@ -1629,18 +1664,18 @@ var init_KeyFormDialog = __esm({
|
|
|
1629
1664
|
onGetConfig
|
|
1630
1665
|
}) => {
|
|
1631
1666
|
const isCreate = mode === "create";
|
|
1632
|
-
const [generateForm, setGenerateForm] = (0,
|
|
1633
|
-
const [editFormState, setEditFormState] = (0,
|
|
1634
|
-
const [unlimitedBudget, setUnlimitedBudget] = (0,
|
|
1635
|
-
const [submitting, setSubmitting] = (0,
|
|
1636
|
-
const [newKeyValue, setNewKeyValue] = (0,
|
|
1637
|
-
const [newKeySnippets, setNewKeySnippets] = (0,
|
|
1638
|
-
const [newKeyModel, setNewKeyModel] = (0,
|
|
1639
|
-
const [generateError, setGenerateError] = (0,
|
|
1640
|
-
const [editError, setEditError] = (0,
|
|
1641
|
-
const [resetSpendConfirm, setResetSpendConfirm] = (0,
|
|
1642
|
-
const [resetSpendSubmitting, setResetSpendSubmitting] = (0,
|
|
1643
|
-
(0,
|
|
1667
|
+
const [generateForm, setGenerateForm] = (0, import_react5.useState)(createForm(username));
|
|
1668
|
+
const [editFormState, setEditFormState] = (0, import_react5.useState)({});
|
|
1669
|
+
const [unlimitedBudget, setUnlimitedBudget] = (0, import_react5.useState)(false);
|
|
1670
|
+
const [submitting, setSubmitting] = (0, import_react5.useState)(false);
|
|
1671
|
+
const [newKeyValue, setNewKeyValue] = (0, import_react5.useState)(null);
|
|
1672
|
+
const [newKeySnippets, setNewKeySnippets] = (0, import_react5.useState)(null);
|
|
1673
|
+
const [newKeyModel, setNewKeyModel] = (0, import_react5.useState)("");
|
|
1674
|
+
const [generateError, setGenerateError] = (0, import_react5.useState)(null);
|
|
1675
|
+
const [editError, setEditError] = (0, import_react5.useState)(null);
|
|
1676
|
+
const [resetSpendConfirm, setResetSpendConfirm] = (0, import_react5.useState)(false);
|
|
1677
|
+
const [resetSpendSubmitting, setResetSpendSubmitting] = (0, import_react5.useState)(false);
|
|
1678
|
+
(0, import_react5.useEffect)(() => {
|
|
1644
1679
|
if (open) {
|
|
1645
1680
|
if (isCreate) {
|
|
1646
1681
|
setGenerateForm(createForm(username));
|
|
@@ -1677,7 +1712,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1677
1712
|
else setFormData({ key_alias: value });
|
|
1678
1713
|
};
|
|
1679
1714
|
const selectedTeam = teams.find((t) => t.team_id === (isCreate ? generateForm.team_id : void 0)) ?? null;
|
|
1680
|
-
const availableModels = (0,
|
|
1715
|
+
const availableModels = (0, import_react5.useMemo)(() => {
|
|
1681
1716
|
return models.filter((m) => isModelAllowedByTeam(m, selectedTeam?.models));
|
|
1682
1717
|
}, [models, selectedTeam]);
|
|
1683
1718
|
const selectedModels = availableModels.filter((m) => (formData.models || []).includes(m.model_name));
|
|
@@ -1686,7 +1721,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1686
1721
|
const budgetInvalid = !unlimitedBudget && (formData.max_budget === void 0 || formData.max_budget === null || formData.max_budget <= 0);
|
|
1687
1722
|
const canSubmit = !aliasError && !teamError && !budgetInvalid && !submitting;
|
|
1688
1723
|
const aliasDuplicate = !!currentAlias && keys.some((k) => k.key_alias === currentAlias && k !== keyToEdit);
|
|
1689
|
-
const budgetEstimate = (0,
|
|
1724
|
+
const budgetEstimate = (0, import_react5.useMemo)(() => {
|
|
1690
1725
|
if (unlimitedBudget || budgetInvalid) return null;
|
|
1691
1726
|
const inputCosts = selectedModels.map((m) => m.input_cost_per_token).filter((c) => typeof c === "number" && c > 0);
|
|
1692
1727
|
if (inputCosts.length === 0) return null;
|
|
@@ -1765,11 +1800,11 @@ var init_KeyFormDialog = __esm({
|
|
|
1765
1800
|
const inCost = fmtCost(m.input_cost_per_token);
|
|
1766
1801
|
const outCost = fmtCost(m.output_cost_per_token);
|
|
1767
1802
|
const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
|
|
1768
|
-
return /* @__PURE__ */
|
|
1803
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react5.default.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
|
|
1769
1804
|
};
|
|
1770
1805
|
const renderTeamField = () => {
|
|
1771
1806
|
if (teams.length > 0) {
|
|
1772
|
-
return /* @__PURE__ */
|
|
1807
|
+
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1773
1808
|
import_Autocomplete.default,
|
|
1774
1809
|
{
|
|
1775
1810
|
options: teams,
|
|
@@ -1783,7 +1818,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1783
1818
|
setFormData({ team_id: team?.team_id, models: restrictedModels });
|
|
1784
1819
|
},
|
|
1785
1820
|
disabled: !isCreate,
|
|
1786
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1821
|
+
renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
|
|
1787
1822
|
import_TextField2.default,
|
|
1788
1823
|
{
|
|
1789
1824
|
...params,
|
|
@@ -1798,14 +1833,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1798
1833
|
);
|
|
1799
1834
|
}
|
|
1800
1835
|
if (teamRequired && isCreate) {
|
|
1801
|
-
return /* @__PURE__ */
|
|
1836
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.");
|
|
1802
1837
|
}
|
|
1803
1838
|
return null;
|
|
1804
1839
|
};
|
|
1805
1840
|
const editKeyId = keyToEdit?.token ?? keyToEdit?.key ?? "";
|
|
1806
1841
|
const renderDangerZone = () => {
|
|
1807
1842
|
if (isCreate || !onResetKeySpend) return null;
|
|
1808
|
-
return /* @__PURE__ */
|
|
1843
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ import_react5.default.createElement(
|
|
1809
1844
|
import_Button3.default,
|
|
1810
1845
|
{
|
|
1811
1846
|
size: "small",
|
|
@@ -1814,7 +1849,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1814
1849
|
onClick: () => setResetSpendConfirm(true)
|
|
1815
1850
|
},
|
|
1816
1851
|
"Reset Spend to $0"
|
|
1817
|
-
) : /* @__PURE__ */
|
|
1852
|
+
) : /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ import_react5.default.createElement(
|
|
1818
1853
|
import_Button3.default,
|
|
1819
1854
|
{
|
|
1820
1855
|
size: "small",
|
|
@@ -1823,8 +1858,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1823
1858
|
disabled: resetSpendSubmitting,
|
|
1824
1859
|
onClick: handleResetSpend
|
|
1825
1860
|
},
|
|
1826
|
-
resetSpendSubmitting ? /* @__PURE__ */
|
|
1827
|
-
), /* @__PURE__ */
|
|
1861
|
+
resetSpendSubmitting ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress2.default, { size: 16 }) : "Confirm"
|
|
1862
|
+
), /* @__PURE__ */ import_react5.default.createElement(import_Button3.default, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel")));
|
|
1828
1863
|
};
|
|
1829
1864
|
const maskKey = (key) => {
|
|
1830
1865
|
if (key.length <= 8) return "***";
|
|
@@ -1834,7 +1869,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1834
1869
|
if (newKeyValue) dialogTitle = "Key Generated";
|
|
1835
1870
|
else if (isCreate) dialogTitle = "Generate New Key";
|
|
1836
1871
|
const submitLabel = isCreate ? "Generate" : "Save";
|
|
1837
|
-
return /* @__PURE__ */
|
|
1872
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_Dialog2.default, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react5.default.createElement(import_DialogTitle2.default, null, dialogTitle), /* @__PURE__ */ import_react5.default.createElement(import_DialogContent2.default, null, newKeyValue ? /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ import_react5.default.createElement(
|
|
1838
1873
|
import_Box4.default,
|
|
1839
1874
|
{
|
|
1840
1875
|
display: "flex",
|
|
@@ -1849,7 +1884,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1849
1884
|
borderRadius: 1
|
|
1850
1885
|
}
|
|
1851
1886
|
},
|
|
1852
|
-
/* @__PURE__ */
|
|
1887
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1853
1888
|
import_Typography4.default,
|
|
1854
1889
|
{
|
|
1855
1890
|
component: "code",
|
|
@@ -1858,8 +1893,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1858
1893
|
},
|
|
1859
1894
|
newKeyValue
|
|
1860
1895
|
),
|
|
1861
|
-
/* @__PURE__ */
|
|
1862
|
-
), newKeySnippets && /* @__PURE__ */
|
|
1896
|
+
/* @__PURE__ */ import_react5.default.createElement(import_IconButton2.default, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, null))
|
|
1897
|
+
), newKeySnippets && /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { mt: 2 }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "Public endpoint \u2014 paste into any tool's base URL / API base field"), /* @__PURE__ */ import_react5.default.createElement(
|
|
1863
1898
|
import_Box4.default,
|
|
1864
1899
|
{
|
|
1865
1900
|
display: "flex",
|
|
@@ -1873,7 +1908,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1873
1908
|
borderRadius: 1
|
|
1874
1909
|
}
|
|
1875
1910
|
},
|
|
1876
|
-
/* @__PURE__ */
|
|
1911
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1877
1912
|
import_Typography4.default,
|
|
1878
1913
|
{
|
|
1879
1914
|
component: "code",
|
|
@@ -1882,8 +1917,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1882
1917
|
},
|
|
1883
1918
|
newKeySnippets.publicEndpoint
|
|
1884
1919
|
),
|
|
1885
|
-
/* @__PURE__ */
|
|
1886
|
-
)), newKeySnippets && /* @__PURE__ */
|
|
1920
|
+
/* @__PURE__ */ import_react5.default.createElement(import_IconButton2.default, { size: "small", onClick: () => copyToClipboard(newKeySnippets.publicEndpoint) }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" }))
|
|
1921
|
+
)), newKeySnippets && /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { mt: 3 }, /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ import_react5.default.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ import_react5.default.createElement(import_Box4.default, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, !isCreate && /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ import_react5.default.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editKeyId))), generateError && /* @__PURE__ */ import_react5.default.createElement(import_Alert.default, { severity: "error", onClose: () => setGenerateError(null) }, generateError), editError && /* @__PURE__ */ import_react5.default.createElement(import_Alert.default, { severity: "error", onClose: () => setEditError(null) }, editError), /* @__PURE__ */ import_react5.default.createElement(
|
|
1887
1922
|
import_TextField2.default,
|
|
1888
1923
|
{
|
|
1889
1924
|
label: "Alias",
|
|
@@ -1899,7 +1934,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1899
1934
|
required: true,
|
|
1900
1935
|
fullWidth: true
|
|
1901
1936
|
}
|
|
1902
|
-
), !isCreate ? null : /* @__PURE__ */
|
|
1937
|
+
), !isCreate ? null : /* @__PURE__ */ import_react5.default.createElement(
|
|
1903
1938
|
import_TextField2.default,
|
|
1904
1939
|
{
|
|
1905
1940
|
select: true,
|
|
@@ -1908,12 +1943,12 @@ var init_KeyFormDialog = __esm({
|
|
|
1908
1943
|
onChange: (e) => setFormData({ duration: e.target.value }),
|
|
1909
1944
|
fullWidth: true
|
|
1910
1945
|
},
|
|
1911
|
-
/* @__PURE__ */
|
|
1912
|
-
/* @__PURE__ */
|
|
1913
|
-
/* @__PURE__ */
|
|
1914
|
-
/* @__PURE__ */
|
|
1915
|
-
/* @__PURE__ */
|
|
1916
|
-
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */
|
|
1946
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "1d" }, "1 Day"),
|
|
1947
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "7d" }, "7 Days"),
|
|
1948
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "30d" }, "30 Days"),
|
|
1949
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "90d" }, "90 Days"),
|
|
1950
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "1y" }, "1 Year")
|
|
1951
|
+
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ import_react5.default.createElement(
|
|
1917
1952
|
import_Autocomplete.default,
|
|
1918
1953
|
{
|
|
1919
1954
|
multiple: true,
|
|
@@ -1922,8 +1957,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1922
1957
|
getOptionLabel: (m) => m.model_name,
|
|
1923
1958
|
value: selectedModels,
|
|
1924
1959
|
onChange: (_e, selected) => setFormData({ models: selected.map((m) => m.model_name) }),
|
|
1925
|
-
renderOption: (props, m) => /* @__PURE__ */
|
|
1926
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1960
|
+
renderOption: (props, m) => /* @__PURE__ */ import_react5.default.createElement("li", { ...props }, modelOption(m)),
|
|
1961
|
+
renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
|
|
1927
1962
|
import_TextField2.default,
|
|
1928
1963
|
{
|
|
1929
1964
|
...params,
|
|
@@ -1933,10 +1968,10 @@ var init_KeyFormDialog = __esm({
|
|
|
1933
1968
|
}
|
|
1934
1969
|
)
|
|
1935
1970
|
}
|
|
1936
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
1971
|
+
), allowUnlimitedBudget && /* @__PURE__ */ import_react5.default.createElement(
|
|
1937
1972
|
import_FormControlLabel.default,
|
|
1938
1973
|
{
|
|
1939
|
-
control: /* @__PURE__ */
|
|
1974
|
+
control: /* @__PURE__ */ import_react5.default.createElement(
|
|
1940
1975
|
import_Checkbox.default,
|
|
1941
1976
|
{
|
|
1942
1977
|
checked: unlimitedBudget,
|
|
@@ -1952,7 +1987,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1952
1987
|
),
|
|
1953
1988
|
label: "Unlimited budget"
|
|
1954
1989
|
}
|
|
1955
|
-
), /* @__PURE__ */
|
|
1990
|
+
), /* @__PURE__ */ import_react5.default.createElement(
|
|
1956
1991
|
import_TextField2.default,
|
|
1957
1992
|
{
|
|
1958
1993
|
label: "Max Budget (USD)",
|
|
@@ -1965,7 +2000,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1965
2000
|
required: true,
|
|
1966
2001
|
fullWidth: true
|
|
1967
2002
|
}
|
|
1968
|
-
), /* @__PURE__ */
|
|
2003
|
+
), /* @__PURE__ */ import_react5.default.createElement(import_Accordion.default, { disableGutters: true, elevation: 0, sx: { border: "1px solid", borderColor: "divider", "&:before": { display: "none" } } }, /* @__PURE__ */ import_react5.default.createElement(import_AccordionSummary.default, { expandIcon: /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ExpandMore, null) }, /* @__PURE__ */ import_react5.default.createElement(import_Typography4.default, { variant: "body2", fontWeight: 600 }, "Advanced")), /* @__PURE__ */ import_react5.default.createElement(import_AccordionDetails.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react5.default.createElement(
|
|
1969
2004
|
import_TextField2.default,
|
|
1970
2005
|
{
|
|
1971
2006
|
label: "TPM Limit",
|
|
@@ -1975,7 +2010,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1975
2010
|
helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
|
|
1976
2011
|
fullWidth: true
|
|
1977
2012
|
}
|
|
1978
|
-
), /* @__PURE__ */
|
|
2013
|
+
), /* @__PURE__ */ import_react5.default.createElement(
|
|
1979
2014
|
import_TextField2.default,
|
|
1980
2015
|
{
|
|
1981
2016
|
label: "RPM Limit",
|
|
@@ -1985,7 +2020,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1985
2020
|
helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
|
|
1986
2021
|
fullWidth: true
|
|
1987
2022
|
}
|
|
1988
|
-
))), renderDangerZone())), /* @__PURE__ */
|
|
2023
|
+
))), renderDangerZone())), /* @__PURE__ */ import_react5.default.createElement(import_DialogActions2.default, null, newKeyValue ? /* @__PURE__ */ import_react5.default.createElement(import_Button3.default, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_Button3.default, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ import_react5.default.createElement(
|
|
1989
2024
|
import_Button3.default,
|
|
1990
2025
|
{
|
|
1991
2026
|
onClick: isCreate ? handleGenerate : handleUpdate,
|
|
@@ -1993,18 +2028,18 @@ var init_KeyFormDialog = __esm({
|
|
|
1993
2028
|
color: "primary",
|
|
1994
2029
|
disabled: !canSubmit
|
|
1995
2030
|
},
|
|
1996
|
-
submitting ? /* @__PURE__ */
|
|
2031
|
+
submitting ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress2.default, { size: 24 }) : submitLabel
|
|
1997
2032
|
))));
|
|
1998
2033
|
};
|
|
1999
2034
|
}
|
|
2000
2035
|
});
|
|
2001
2036
|
|
|
2002
2037
|
// src/components/ManageTeamDialog.tsx
|
|
2003
|
-
var
|
|
2038
|
+
var import_react6, import_Dialog3, import_DialogTitle3, import_DialogContent3, import_DialogActions3, import_TextField3, import_Button4, import_Alert2, import_Checkbox2, import_FormControlLabel2, import_Autocomplete2, import_CircularProgress3, import_MenuItem2, import_Box5, import_Divider2, import_Typography5, import_IconButton3, import_Paper3, import_Table2, import_TableBody2, import_TableCell2, import_TableContainer2, import_TableHead2, import_TableRow2, import_icons_material4, import_core_plugin_api2, import_plugin_catalog_react, import_catalog_model, import_react_use, BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
|
|
2004
2039
|
var init_ManageTeamDialog = __esm({
|
|
2005
2040
|
"src/components/ManageTeamDialog.tsx"() {
|
|
2006
2041
|
"use strict";
|
|
2007
|
-
|
|
2042
|
+
import_react6 = __toESM(require("react"));
|
|
2008
2043
|
import_Dialog3 = __toESM(require("@mui/material/Dialog"));
|
|
2009
2044
|
import_DialogTitle3 = __toESM(require("@mui/material/DialogTitle"));
|
|
2010
2045
|
import_DialogContent3 = __toESM(require("@mui/material/DialogContent"));
|
|
@@ -2061,29 +2096,29 @@ var init_ManageTeamDialog = __esm({
|
|
|
2061
2096
|
mcpServers,
|
|
2062
2097
|
onSaveMcpServers
|
|
2063
2098
|
}) => {
|
|
2064
|
-
const [alias, setAlias] = (0,
|
|
2065
|
-
const [models, setModels] = (0,
|
|
2066
|
-
const [maxBudget, setMaxBudget] = (0,
|
|
2067
|
-
const [unlimited, setUnlimited] = (0,
|
|
2068
|
-
const [budgetDuration, setBudgetDuration] = (0,
|
|
2069
|
-
const [submitting, setSubmitting] = (0,
|
|
2070
|
-
const [error, setError] = (0,
|
|
2071
|
-
const [memberQuery, setMemberQuery] = (0,
|
|
2072
|
-
const [memberRef, setMemberRef] = (0,
|
|
2073
|
-
const [memberBudget, setMemberBudget] = (0,
|
|
2074
|
-
const [memberBusy, setMemberBusy] = (0,
|
|
2075
|
-
const [memberError, setMemberError] = (0,
|
|
2076
|
-
const [kbIds, setKbIds] = (0,
|
|
2077
|
-
const [kbBusy, setKbBusy] = (0,
|
|
2078
|
-
const [kbError, setKbError] = (0,
|
|
2079
|
-
const [mcpIds, setMcpIds] = (0,
|
|
2080
|
-
const [mcpBusy, setMcpBusy] = (0,
|
|
2081
|
-
const [mcpError, setMcpError] = (0,
|
|
2099
|
+
const [alias, setAlias] = (0, import_react6.useState)("");
|
|
2100
|
+
const [models, setModels] = (0, import_react6.useState)([]);
|
|
2101
|
+
const [maxBudget, setMaxBudget] = (0, import_react6.useState)("");
|
|
2102
|
+
const [unlimited, setUnlimited] = (0, import_react6.useState)(false);
|
|
2103
|
+
const [budgetDuration, setBudgetDuration] = (0, import_react6.useState)("");
|
|
2104
|
+
const [submitting, setSubmitting] = (0, import_react6.useState)(false);
|
|
2105
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
2106
|
+
const [memberQuery, setMemberQuery] = (0, import_react6.useState)("");
|
|
2107
|
+
const [memberRef, setMemberRef] = (0, import_react6.useState)("");
|
|
2108
|
+
const [memberBudget, setMemberBudget] = (0, import_react6.useState)("");
|
|
2109
|
+
const [memberBusy, setMemberBusy] = (0, import_react6.useState)(false);
|
|
2110
|
+
const [memberError, setMemberError] = (0, import_react6.useState)(null);
|
|
2111
|
+
const [kbIds, setKbIds] = (0, import_react6.useState)([]);
|
|
2112
|
+
const [kbBusy, setKbBusy] = (0, import_react6.useState)(false);
|
|
2113
|
+
const [kbError, setKbError] = (0, import_react6.useState)(null);
|
|
2114
|
+
const [mcpIds, setMcpIds] = (0, import_react6.useState)([]);
|
|
2115
|
+
const [mcpBusy, setMcpBusy] = (0, import_react6.useState)(false);
|
|
2116
|
+
const [mcpError, setMcpError] = (0, import_react6.useState)(null);
|
|
2082
2117
|
const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
|
|
2083
2118
|
const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
|
|
2084
2119
|
const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
|
|
2085
2120
|
const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
|
|
2086
|
-
const members = (0,
|
|
2121
|
+
const members = (0, import_react6.useMemo)(() => team?.members_with_roles ?? [], [team]);
|
|
2087
2122
|
const showMembers = mode === "edit" && !!canManageMembers;
|
|
2088
2123
|
const catalogApi = (0, import_core_plugin_api2.useApi)(import_plugin_catalog_react.catalogApiRef);
|
|
2089
2124
|
const { value: catalogUsers, loading: usersLoading } = (0, import_react_use.useAsync)(async () => {
|
|
@@ -2098,7 +2133,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2098
2133
|
return [];
|
|
2099
2134
|
}
|
|
2100
2135
|
}, [open, showMembers, catalogApi]);
|
|
2101
|
-
const userOptions = (0,
|
|
2136
|
+
const userOptions = (0, import_react6.useMemo)(() => {
|
|
2102
2137
|
const existingEmails = new Set(
|
|
2103
2138
|
members.map((m) => m.user_email?.toLowerCase()).filter((e) => !!e)
|
|
2104
2139
|
);
|
|
@@ -2113,7 +2148,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2113
2148
|
};
|
|
2114
2149
|
}).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
|
|
2115
2150
|
}, [catalogUsers, members]);
|
|
2116
|
-
(0,
|
|
2151
|
+
(0, import_react6.useEffect)(() => {
|
|
2117
2152
|
if (open) {
|
|
2118
2153
|
if (mode === "edit" && team) {
|
|
2119
2154
|
setAlias(team.team_alias ?? "");
|
|
@@ -2269,7 +2304,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2269
2304
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
2270
2305
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
2271
2306
|
const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
|
|
2272
|
-
return /* @__PURE__ */
|
|
2307
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_Dialog3.default, { open, onClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react6.default.createElement(import_DialogTitle3.default, null, mode === "create" ? "Create Team" : "Edit Team"), /* @__PURE__ */ import_react6.default.createElement(import_DialogContent3.default, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 } }, error && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error" }, error), /* @__PURE__ */ import_react6.default.createElement(
|
|
2273
2308
|
import_TextField3.default,
|
|
2274
2309
|
{
|
|
2275
2310
|
label: "Team Alias",
|
|
@@ -2278,7 +2313,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2278
2313
|
disabled: submitting,
|
|
2279
2314
|
fullWidth: true
|
|
2280
2315
|
}
|
|
2281
|
-
), /* @__PURE__ */
|
|
2316
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2282
2317
|
import_Autocomplete2.default,
|
|
2283
2318
|
{
|
|
2284
2319
|
multiple: true,
|
|
@@ -2286,7 +2321,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2286
2321
|
value: models,
|
|
2287
2322
|
onChange: (_, newValue) => setModels(newValue),
|
|
2288
2323
|
disabled: submitting,
|
|
2289
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2324
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2290
2325
|
import_TextField3.default,
|
|
2291
2326
|
{
|
|
2292
2327
|
...params,
|
|
@@ -2296,7 +2331,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2296
2331
|
}
|
|
2297
2332
|
)
|
|
2298
2333
|
}
|
|
2299
|
-
), /* @__PURE__ */
|
|
2334
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2300
2335
|
import_TextField3.default,
|
|
2301
2336
|
{
|
|
2302
2337
|
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
@@ -2308,13 +2343,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
2308
2343
|
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
2309
2344
|
fullWidth: true
|
|
2310
2345
|
}
|
|
2311
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
2346
|
+
), allowUnlimitedBudget && /* @__PURE__ */ import_react6.default.createElement(
|
|
2312
2347
|
import_FormControlLabel2.default,
|
|
2313
2348
|
{
|
|
2314
|
-
control: /* @__PURE__ */
|
|
2349
|
+
control: /* @__PURE__ */ import_react6.default.createElement(import_Checkbox2.default, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
2315
2350
|
label: "Unlimited Budget"
|
|
2316
2351
|
}
|
|
2317
|
-
), /* @__PURE__ */
|
|
2352
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2318
2353
|
import_TextField3.default,
|
|
2319
2354
|
{
|
|
2320
2355
|
select: true,
|
|
@@ -2325,8 +2360,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2325
2360
|
helperText: "Spend-reset period for the team budget",
|
|
2326
2361
|
fullWidth: true
|
|
2327
2362
|
},
|
|
2328
|
-
durationOptions.map((o) => /* @__PURE__ */
|
|
2329
|
-
), showMembers && /* @__PURE__ */
|
|
2363
|
+
durationOptions.map((o) => /* @__PURE__ */ import_react6.default.createElement(import_MenuItem2.default, { key: o.value, value: o.value }, o.label))
|
|
2364
|
+
), showMembers && /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "No members yet.") : /* @__PURE__ */ import_react6.default.createElement(import_TableContainer2.default, { component: import_Paper3.default, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 } }, /* @__PURE__ */ import_react6.default.createElement(import_Table2.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react6.default.createElement(import_TableHead2.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, null, "User"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right" }, "Role"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right", sx: { width: 40 } }))), /* @__PURE__ */ import_react6.default.createElement(import_TableBody2.default, null, members.map((m) => /* @__PURE__ */ import_react6.default.createElement(import_TableRow2.default, { key: m.user_id }, /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, null, m.user_email ? /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react6.default.createElement(
|
|
2330
2365
|
import_Typography5.default,
|
|
2331
2366
|
{
|
|
2332
2367
|
variant: "caption",
|
|
@@ -2334,14 +2369,14 @@ var init_ManageTeamDialog = __esm({
|
|
|
2334
2369
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
2335
2370
|
},
|
|
2336
2371
|
m.user_id
|
|
2337
|
-
)) : /* @__PURE__ */
|
|
2372
|
+
)) : /* @__PURE__ */ import_react6.default.createElement(
|
|
2338
2373
|
import_Typography5.default,
|
|
2339
2374
|
{
|
|
2340
2375
|
variant: "body2",
|
|
2341
2376
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
2342
2377
|
},
|
|
2343
2378
|
m.user_id
|
|
2344
|
-
)), /* @__PURE__ */
|
|
2379
|
+
)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react6.default.createElement(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })), /* @__PURE__ */ import_react6.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2345
2380
|
import_IconButton3.default,
|
|
2346
2381
|
{
|
|
2347
2382
|
edge: "end",
|
|
@@ -2350,8 +2385,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2350
2385
|
disabled: memberBusy,
|
|
2351
2386
|
onClick: () => handleRemoveMember(m.user_id)
|
|
2352
2387
|
},
|
|
2353
|
-
/* @__PURE__ */
|
|
2354
|
-
))))))), /* @__PURE__ */
|
|
2388
|
+
/* @__PURE__ */ import_react6.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
|
|
2389
|
+
))))))), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2355
2390
|
import_Autocomplete2.default,
|
|
2356
2391
|
{
|
|
2357
2392
|
freeSolo: true,
|
|
@@ -2375,7 +2410,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2375
2410
|
isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
|
|
2376
2411
|
disabled: memberBusy,
|
|
2377
2412
|
sx: { flex: 1, minWidth: 260 },
|
|
2378
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2413
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2379
2414
|
import_TextField3.default,
|
|
2380
2415
|
{
|
|
2381
2416
|
...params,
|
|
@@ -2385,12 +2420,12 @@ var init_ManageTeamDialog = __esm({
|
|
|
2385
2420
|
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
2386
2421
|
InputProps: {
|
|
2387
2422
|
...params.InputProps,
|
|
2388
|
-
endAdornment: /* @__PURE__ */
|
|
2423
|
+
endAdornment: /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, usersLoading ? /* @__PURE__ */ import_react6.default.createElement(import_CircularProgress3.default, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
|
|
2389
2424
|
}
|
|
2390
2425
|
}
|
|
2391
2426
|
)
|
|
2392
2427
|
}
|
|
2393
|
-
), /* @__PURE__ */
|
|
2428
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2394
2429
|
import_TextField3.default,
|
|
2395
2430
|
{
|
|
2396
2431
|
label: "Max budget in team ($)",
|
|
@@ -2401,7 +2436,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2401
2436
|
size: "small",
|
|
2402
2437
|
sx: { width: 160 }
|
|
2403
2438
|
}
|
|
2404
|
-
), /* @__PURE__ */
|
|
2439
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2405
2440
|
import_Button4.default,
|
|
2406
2441
|
{
|
|
2407
2442
|
onClick: handleAddMember,
|
|
@@ -2410,7 +2445,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2410
2445
|
sx: { mt: 0.5 }
|
|
2411
2446
|
},
|
|
2412
2447
|
"Add"
|
|
2413
|
-
))), showKnowledgeBases && /* @__PURE__ */
|
|
2448
|
+
))), showKnowledgeBases && /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Knowledge Bases"), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching a knowledge base exposes its documents to every key in this team."), kbError && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setKbError(null) }, kbError), /* @__PURE__ */ import_react6.default.createElement(
|
|
2414
2449
|
import_Autocomplete2.default,
|
|
2415
2450
|
{
|
|
2416
2451
|
multiple: true,
|
|
@@ -2419,9 +2454,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2419
2454
|
getOptionLabel: kbLabel,
|
|
2420
2455
|
onChange: (_, v) => setKbIds(v),
|
|
2421
2456
|
disabled: kbBusy,
|
|
2422
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2457
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(import_TextField3.default, { ...params, label: "Attached knowledge bases" })
|
|
2423
2458
|
}
|
|
2424
|
-
), /* @__PURE__ */
|
|
2459
|
+
), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2425
2460
|
import_Button4.default,
|
|
2426
2461
|
{
|
|
2427
2462
|
onClick: handleSaveKnowledgeBases,
|
|
@@ -2429,7 +2464,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2429
2464
|
variant: "outlined"
|
|
2430
2465
|
},
|
|
2431
2466
|
kbBusy ? "Saving\u2026" : "Save knowledge bases"
|
|
2432
|
-
))), showMcpServers && /* @__PURE__ */
|
|
2467
|
+
))), showMcpServers && /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "MCP Servers"), /* @__PURE__ */ import_react6.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching an MCP server lets every key in this team invoke that server's tools through the model \u2014 including any side effects those tools have."), mcpError && /* @__PURE__ */ import_react6.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMcpError(null) }, mcpError), /* @__PURE__ */ import_react6.default.createElement(
|
|
2433
2468
|
import_Autocomplete2.default,
|
|
2434
2469
|
{
|
|
2435
2470
|
multiple: true,
|
|
@@ -2438,9 +2473,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2438
2473
|
getOptionLabel: mcpLabel,
|
|
2439
2474
|
onChange: (_, v) => setMcpIds(v),
|
|
2440
2475
|
disabled: mcpBusy,
|
|
2441
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2476
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(import_TextField3.default, { ...params, label: "Attached MCP servers" })
|
|
2442
2477
|
}
|
|
2443
|
-
), /* @__PURE__ */
|
|
2478
|
+
), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2444
2479
|
import_Button4.default,
|
|
2445
2480
|
{
|
|
2446
2481
|
onClick: handleSaveMcpServers,
|
|
@@ -2448,7 +2483,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2448
2483
|
variant: "outlined"
|
|
2449
2484
|
},
|
|
2450
2485
|
mcpBusy ? "Saving\u2026" : "Save MCP servers"
|
|
2451
|
-
)))), /* @__PURE__ */
|
|
2486
|
+
)))), /* @__PURE__ */ import_react6.default.createElement(import_DialogActions3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Button4.default, { onClick: onClose, disabled: submitting }, "Cancel"), /* @__PURE__ */ import_react6.default.createElement(import_Button4.default, { onClick: handleSubmit, disabled: submitting, variant: "contained" }, submitting ? "Saving..." : "Save")));
|
|
2452
2487
|
};
|
|
2453
2488
|
}
|
|
2454
2489
|
});
|
|
@@ -2459,11 +2494,11 @@ function rateTone(rate) {
|
|
|
2459
2494
|
if (rate >= 0.9) return "warning";
|
|
2460
2495
|
return "danger";
|
|
2461
2496
|
}
|
|
2462
|
-
var
|
|
2497
|
+
var import_react7, import_Paper4, import_Box6, import_Typography6, import_TextField4, import_MenuItem3, import_Grid, import_Table3, import_TableBody3, import_TableCell3, import_TableContainer3, import_TableHead3, import_TableRow3, import_Skeleton3, import_recharts, TOP_N_MODELS, PERIOD_LS_KEY, PRESET_LABELS, fmtPct, ChartSkeleton, ChartOrFallback, SuccessRateCell, UsageStats;
|
|
2463
2498
|
var init_UsageStats = __esm({
|
|
2464
2499
|
"src/components/UsageStats.tsx"() {
|
|
2465
2500
|
"use strict";
|
|
2466
|
-
|
|
2501
|
+
import_react7 = __toESM(require("react"));
|
|
2467
2502
|
import_Paper4 = __toESM(require("@mui/material/Paper"));
|
|
2468
2503
|
import_Box6 = __toESM(require("@mui/material/Box"));
|
|
2469
2504
|
import_Typography6 = __toESM(require("@mui/material/Typography"));
|
|
@@ -2489,16 +2524,16 @@ var init_UsageStats = __esm({
|
|
|
2489
2524
|
"30d": "Last 30 days"
|
|
2490
2525
|
};
|
|
2491
2526
|
fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
|
|
2492
|
-
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */
|
|
2527
|
+
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ import_react7.default.createElement(import_Skeleton3.default, { variant: "rounded", height });
|
|
2493
2528
|
ChartOrFallback = ({ loading, empty, height = 240, children }) => {
|
|
2494
|
-
if (loading) return /* @__PURE__ */
|
|
2495
|
-
if (empty) return /* @__PURE__ */
|
|
2496
|
-
return /* @__PURE__ */
|
|
2529
|
+
if (loading) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { height });
|
|
2530
|
+
if (empty) return /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "No data for this period", height });
|
|
2531
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, children);
|
|
2497
2532
|
};
|
|
2498
2533
|
SuccessRateCell = ({ rate, requests }) => {
|
|
2499
|
-
if (requests === 0) return /* @__PURE__ */
|
|
2534
|
+
if (requests === 0) return /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "body2", color: "text.secondary" }, "\u2014");
|
|
2500
2535
|
const tone = rateTone(rate);
|
|
2501
|
-
return /* @__PURE__ */
|
|
2536
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { width: 72, flexShrink: 0 }, /* @__PURE__ */ import_react7.default.createElement(Meter, { value: rate * 100, tone, height: 4 })), /* @__PURE__ */ import_react7.default.createElement(
|
|
2502
2537
|
import_Typography6.default,
|
|
2503
2538
|
{
|
|
2504
2539
|
variant: "caption",
|
|
@@ -2517,8 +2552,8 @@ var init_UsageStats = __esm({
|
|
|
2517
2552
|
userInfo
|
|
2518
2553
|
}) => {
|
|
2519
2554
|
const chart = useChartTheme();
|
|
2520
|
-
const [selectedModel, setSelectedModel] = (0,
|
|
2521
|
-
const [tab, setTab] = (0,
|
|
2555
|
+
const [selectedModel, setSelectedModel] = (0, import_react7.useState)("all");
|
|
2556
|
+
const [tab, setTab] = (0, import_react7.useState)("costs");
|
|
2522
2557
|
const handlePresetChange = (preset) => {
|
|
2523
2558
|
const end = /* @__PURE__ */ new Date();
|
|
2524
2559
|
const start = /* @__PURE__ */ new Date();
|
|
@@ -2532,7 +2567,7 @@ var init_UsageStats = __esm({
|
|
|
2532
2567
|
}
|
|
2533
2568
|
onDateRangeChange({ start, end }, preset);
|
|
2534
2569
|
};
|
|
2535
|
-
const dailyData = (0,
|
|
2570
|
+
const dailyData = (0, import_react7.useMemo)(
|
|
2536
2571
|
() => (usage?.daily_usage ?? []).map((d) => ({
|
|
2537
2572
|
date: d.date,
|
|
2538
2573
|
spend: d.spend,
|
|
@@ -2545,21 +2580,21 @@ var init_UsageStats = __esm({
|
|
|
2545
2580
|
})),
|
|
2546
2581
|
[usage]
|
|
2547
2582
|
);
|
|
2548
|
-
const cumulativeData = (0,
|
|
2583
|
+
const cumulativeData = (0, import_react7.useMemo)(() => {
|
|
2549
2584
|
let cum = 0;
|
|
2550
2585
|
return dailyData.map((d) => {
|
|
2551
2586
|
cum += d.spend;
|
|
2552
2587
|
return { date: d.date, cumulative: cum };
|
|
2553
2588
|
});
|
|
2554
2589
|
}, [dailyData]);
|
|
2555
|
-
const successRateData = (0,
|
|
2590
|
+
const successRateData = (0, import_react7.useMemo)(
|
|
2556
2591
|
() => dailyData.filter((d) => d.apiRequests > 0).map((d) => ({
|
|
2557
2592
|
date: d.date,
|
|
2558
2593
|
successRate: parseFloat((d.successfulRequests / d.apiRequests * 100).toFixed(1))
|
|
2559
2594
|
})),
|
|
2560
2595
|
[dailyData]
|
|
2561
2596
|
);
|
|
2562
|
-
const { modelSpendByDate, topModels: topSpendModels } = (0,
|
|
2597
|
+
const { modelSpendByDate, topModels: topSpendModels } = (0, import_react7.useMemo)(() => {
|
|
2563
2598
|
const rows = usage?.daily_by_model ?? [];
|
|
2564
2599
|
const modelTotals = {};
|
|
2565
2600
|
for (const r of rows) modelTotals[r.model] = (modelTotals[r.model] ?? 0) + r.spend;
|
|
@@ -2574,7 +2609,7 @@ var init_UsageStats = __esm({
|
|
|
2574
2609
|
const hasOther = sorted.some((r) => r.Other > 0);
|
|
2575
2610
|
return { modelSpendByDate: sorted, topModels: hasOther ? [...top, "Other"] : top };
|
|
2576
2611
|
}, [usage]);
|
|
2577
|
-
const modelRows = (0,
|
|
2612
|
+
const modelRows = (0, import_react7.useMemo)(() => {
|
|
2578
2613
|
const entries = Object.entries(usage?.usage_by_model ?? {}).map(([model, d]) => ({
|
|
2579
2614
|
model,
|
|
2580
2615
|
spend: d.total_spend,
|
|
@@ -2588,11 +2623,11 @@ var init_UsageStats = __esm({
|
|
|
2588
2623
|
}));
|
|
2589
2624
|
return selectedModel === "all" ? entries : entries.filter((e) => e.model === selectedModel);
|
|
2590
2625
|
}, [usage, selectedModel]);
|
|
2591
|
-
const topModelSpendBars = (0,
|
|
2626
|
+
const topModelSpendBars = (0, import_react7.useMemo)(
|
|
2592
2627
|
() => [...modelRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
|
|
2593
2628
|
[modelRows]
|
|
2594
2629
|
);
|
|
2595
|
-
const keyRows = (0,
|
|
2630
|
+
const keyRows = (0, import_react7.useMemo)(
|
|
2596
2631
|
() => Object.entries(usage?.usage_by_key ?? {}).map(([keyHash, d]) => ({
|
|
2597
2632
|
keyHash,
|
|
2598
2633
|
keyAlias: d.key_alias ?? keyHash.slice(0, 8),
|
|
@@ -2609,7 +2644,7 @@ var init_UsageStats = __esm({
|
|
|
2609
2644
|
})),
|
|
2610
2645
|
[usage]
|
|
2611
2646
|
);
|
|
2612
|
-
const topKeySpendBars = (0,
|
|
2647
|
+
const topKeySpendBars = (0, import_react7.useMemo)(
|
|
2613
2648
|
() => [...keyRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
|
|
2614
2649
|
[keyRows]
|
|
2615
2650
|
);
|
|
@@ -2647,23 +2682,23 @@ var init_UsageStats = __esm({
|
|
|
2647
2682
|
];
|
|
2648
2683
|
const renderKeyRows = () => {
|
|
2649
2684
|
if (loading) {
|
|
2650
|
-
return /* @__PURE__ */
|
|
2685
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ import_react7.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
|
|
2651
2686
|
}
|
|
2652
2687
|
if (keyRows.length === 0) {
|
|
2653
|
-
return /* @__PURE__ */
|
|
2688
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 7 }, /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "No key activity in this period" })));
|
|
2654
2689
|
}
|
|
2655
|
-
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */
|
|
2690
|
+
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, { key: r.keyHash }, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ import_react7.default.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ import_react7.default.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
|
|
2656
2691
|
};
|
|
2657
2692
|
const renderModelRows = () => {
|
|
2658
2693
|
if (loading) {
|
|
2659
|
-
return /* @__PURE__ */
|
|
2694
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ import_react7.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
|
|
2660
2695
|
}
|
|
2661
2696
|
if (modelRows.length === 0) {
|
|
2662
|
-
return /* @__PURE__ */
|
|
2697
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { colSpan: 8 }, /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "No model activity in this period" })));
|
|
2663
2698
|
}
|
|
2664
|
-
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */
|
|
2699
|
+
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, { key: r.model }, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react7.default.createElement(import_Box6.default, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react7.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
|
|
2665
2700
|
};
|
|
2666
|
-
const periodSelect = /* @__PURE__ */
|
|
2701
|
+
const periodSelect = /* @__PURE__ */ import_react7.default.createElement(
|
|
2667
2702
|
import_TextField4.default,
|
|
2668
2703
|
{
|
|
2669
2704
|
select: true,
|
|
@@ -2673,17 +2708,17 @@ var init_UsageStats = __esm({
|
|
|
2673
2708
|
onChange: (e) => handlePresetChange(e.target.value),
|
|
2674
2709
|
sx: { minWidth: 160 }
|
|
2675
2710
|
},
|
|
2676
|
-
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */
|
|
2711
|
+
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { key: p, value: p }, PRESET_LABELS[p]))
|
|
2677
2712
|
);
|
|
2678
|
-
return /* @__PURE__ */
|
|
2713
|
+
return /* @__PURE__ */ import_react7.default.createElement(
|
|
2679
2714
|
SectionCard,
|
|
2680
2715
|
{
|
|
2681
2716
|
title: "Usage Analytics",
|
|
2682
2717
|
subtitle: `${PRESET_LABELS[currentPreset]} \xB7 ${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
2683
2718
|
actions: periodSelect
|
|
2684
2719
|
},
|
|
2685
|
-
/* @__PURE__ */
|
|
2686
|
-
/* @__PURE__ */
|
|
2720
|
+
/* @__PURE__ */ import_react7.default.createElement(MetricStrip, { metrics }),
|
|
2721
|
+
/* @__PURE__ */ import_react7.default.createElement(
|
|
2687
2722
|
import_Box6.default,
|
|
2688
2723
|
{
|
|
2689
2724
|
sx: {
|
|
@@ -2696,7 +2731,7 @@ var init_UsageStats = __esm({
|
|
|
2696
2731
|
mb: 2
|
|
2697
2732
|
}
|
|
2698
2733
|
},
|
|
2699
|
-
/* @__PURE__ */
|
|
2734
|
+
/* @__PURE__ */ import_react7.default.createElement(
|
|
2700
2735
|
SegmentedControl,
|
|
2701
2736
|
{
|
|
2702
2737
|
value: tab,
|
|
@@ -2708,7 +2743,7 @@ var init_UsageStats = __esm({
|
|
|
2708
2743
|
]
|
|
2709
2744
|
}
|
|
2710
2745
|
),
|
|
2711
|
-
tab === "models" && /* @__PURE__ */
|
|
2746
|
+
tab === "models" && /* @__PURE__ */ import_react7.default.createElement(
|
|
2712
2747
|
import_TextField4.default,
|
|
2713
2748
|
{
|
|
2714
2749
|
select: true,
|
|
@@ -2718,17 +2753,17 @@ var init_UsageStats = __esm({
|
|
|
2718
2753
|
onChange: (e) => setSelectedModel(e.target.value),
|
|
2719
2754
|
sx: { minWidth: 220 }
|
|
2720
2755
|
},
|
|
2721
|
-
/* @__PURE__ */
|
|
2722
|
-
models.map((m) => /* @__PURE__ */
|
|
2756
|
+
/* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "all" }, "All models"),
|
|
2757
|
+
models.map((m) => /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { key: m.model_name, value: m.model_name }, m.model_name))
|
|
2723
2758
|
)
|
|
2724
2759
|
),
|
|
2725
|
-
tab === "costs" && /* @__PURE__ */
|
|
2760
|
+
tab === "costs" && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { container: true, spacing: 2 }, /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(
|
|
2726
2761
|
import_recharts.Tooltip,
|
|
2727
2762
|
{
|
|
2728
2763
|
cursor: chart.cursor,
|
|
2729
|
-
content: /* @__PURE__ */
|
|
2764
|
+
content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
|
|
2730
2765
|
}
|
|
2731
|
-
), topSpendModels.map((m) => /* @__PURE__ */
|
|
2766
|
+
), topSpendModels.map((m) => /* @__PURE__ */ import_react7.default.createElement(
|
|
2732
2767
|
import_recharts.Area,
|
|
2733
2768
|
{
|
|
2734
2769
|
key: m,
|
|
@@ -2740,13 +2775,13 @@ var init_UsageStats = __esm({
|
|
|
2740
2775
|
fill: chartColor(m),
|
|
2741
2776
|
fillOpacity: 0.28
|
|
2742
2777
|
}
|
|
2743
|
-
))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */
|
|
2778
|
+
))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */ import_react7.default.createElement(SeriesLegend, { series: topSpendModels.map((m) => ({ name: m, color: chartColor(m) })) }))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily token usage" }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })))))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily requests" }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 }, barCategoryGap: "30%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })))))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Daily success rate" }, /* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.LineChart, { data: successRateData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(
|
|
2744
2779
|
import_recharts.Tooltip,
|
|
2745
2780
|
{
|
|
2746
2781
|
cursor: chart.cursor,
|
|
2747
|
-
content: /* @__PURE__ */
|
|
2782
|
+
content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
|
|
2748
2783
|
}
|
|
2749
|
-
), /* @__PURE__ */
|
|
2784
|
+
), /* @__PURE__ */ import_react7.default.createElement(import_recharts.ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ import_react7.default.createElement(
|
|
2750
2785
|
import_recharts.Line,
|
|
2751
2786
|
{
|
|
2752
2787
|
type: "monotone",
|
|
@@ -2757,13 +2792,13 @@ var init_UsageStats = __esm({
|
|
|
2757
2792
|
dot: { r: 2.5, strokeWidth: 0, fill: SERIES.input },
|
|
2758
2793
|
activeDot: { r: 4 }
|
|
2759
2794
|
}
|
|
2760
|
-
)))))), /* @__PURE__ */
|
|
2795
|
+
)))))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2761
2796
|
ChartCard,
|
|
2762
2797
|
{
|
|
2763
2798
|
title: maxBudget > 0 ? "Cumulative spend vs budget" : "Cumulative spend",
|
|
2764
2799
|
meta: maxBudget > 0 ? `${fmtUsd(totalCumSpend)} used \xB7 ${fmtUsd(Math.max(0, maxBudget - totalCumSpend))} left` : void 0
|
|
2765
2800
|
},
|
|
2766
|
-
/* @__PURE__ */
|
|
2801
|
+
/* @__PURE__ */ import_react7.default.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.AreaChart, { data: cumulativeData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(
|
|
2767
2802
|
import_recharts.YAxis,
|
|
2768
2803
|
{
|
|
2769
2804
|
tickFormatter: fmtUsdCompact,
|
|
@@ -2771,13 +2806,13 @@ var init_UsageStats = __esm({
|
|
|
2771
2806
|
domain: cumulativeDomain,
|
|
2772
2807
|
...chart.axis
|
|
2773
2808
|
}
|
|
2774
|
-
), /* @__PURE__ */
|
|
2809
|
+
), /* @__PURE__ */ import_react7.default.createElement(
|
|
2775
2810
|
import_recharts.Tooltip,
|
|
2776
2811
|
{
|
|
2777
2812
|
cursor: chart.cursor,
|
|
2778
|
-
content: /* @__PURE__ */
|
|
2813
|
+
content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd })
|
|
2779
2814
|
}
|
|
2780
|
-
), maxBudget > 0 && /* @__PURE__ */
|
|
2815
|
+
), maxBudget > 0 && /* @__PURE__ */ import_react7.default.createElement(
|
|
2781
2816
|
import_recharts.ReferenceLine,
|
|
2782
2817
|
{
|
|
2783
2818
|
y: maxBudget,
|
|
@@ -2785,7 +2820,7 @@ var init_UsageStats = __esm({
|
|
|
2785
2820
|
strokeDasharray: "5 4",
|
|
2786
2821
|
label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 10, fill: SERIES.budget }
|
|
2787
2822
|
}
|
|
2788
|
-
), /* @__PURE__ */
|
|
2823
|
+
), /* @__PURE__ */ import_react7.default.createElement(
|
|
2789
2824
|
import_recharts.Area,
|
|
2790
2825
|
{
|
|
2791
2826
|
type: "monotone",
|
|
@@ -2798,32 +2833,32 @@ var init_UsageStats = __esm({
|
|
|
2798
2833
|
}
|
|
2799
2834
|
))))
|
|
2800
2835
|
))),
|
|
2801
|
-
tab === "models" && /* @__PURE__ */
|
|
2836
|
+
tab === "models" && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { container: true, spacing: 2 }, /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Spend by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2802
2837
|
ChartOrFallback,
|
|
2803
2838
|
{
|
|
2804
2839
|
loading,
|
|
2805
2840
|
empty: topModelSpendBars.length === 0,
|
|
2806
2841
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2807
2842
|
},
|
|
2808
|
-
/* @__PURE__ */
|
|
2809
|
-
))), /* @__PURE__ */
|
|
2843
|
+
/* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", radius: [0, 3, 3, 0], barSize: 14 }, topModelSpendBars.map((r) => /* @__PURE__ */ import_react7.default.createElement(import_recharts.Cell, { key: r.model, fill: chartColor(r.model) })))))
|
|
2844
|
+
))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Tokens by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2810
2845
|
ChartOrFallback,
|
|
2811
2846
|
{
|
|
2812
2847
|
loading,
|
|
2813
2848
|
empty: modelRows.length === 0,
|
|
2814
2849
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2815
2850
|
},
|
|
2816
|
-
/* @__PURE__ */
|
|
2817
|
-
))), /* @__PURE__ */
|
|
2818
|
-
tab === "keys" && /* @__PURE__ */
|
|
2851
|
+
/* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
|
|
2852
|
+
))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react7.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, "Model"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Success"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Input"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Output"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react7.default.createElement(import_TableBody3.default, null, renderModelRows()))))),
|
|
2853
|
+
tab === "keys" && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2819
2854
|
ChartOrFallback,
|
|
2820
2855
|
{
|
|
2821
2856
|
loading,
|
|
2822
2857
|
empty: topKeySpendBars.length === 0,
|
|
2823
2858
|
height: Math.max(160, topKeySpendBars.length * 32)
|
|
2824
2859
|
},
|
|
2825
|
-
/* @__PURE__ */
|
|
2826
|
-
))), /* @__PURE__ */
|
|
2860
|
+
/* @__PURE__ */ import_react7.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
|
|
2861
|
+
))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react7.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react7.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react7.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, "Key"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, null, "Models"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Tokens"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react7.default.createElement(import_TableBody3.default, null, renderKeyRows())))))
|
|
2827
2862
|
);
|
|
2828
2863
|
};
|
|
2829
2864
|
}
|
|
@@ -2965,16 +3000,16 @@ function levelToneColor(tone, theme) {
|
|
|
2965
3000
|
return theme.palette.primary.main;
|
|
2966
3001
|
}
|
|
2967
3002
|
}
|
|
2968
|
-
var
|
|
3003
|
+
var import_react8, import_Box7, import_Paper5, import_Typography7, import_styles4, import_core_components2, LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
|
|
2969
3004
|
var init_BudgetLimitList = __esm({
|
|
2970
3005
|
"src/components/BudgetLimitList.tsx"() {
|
|
2971
3006
|
"use strict";
|
|
2972
|
-
|
|
3007
|
+
import_react8 = __toESM(require("react"));
|
|
2973
3008
|
import_Box7 = __toESM(require("@mui/material/Box"));
|
|
2974
3009
|
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
2975
3010
|
import_Typography7 = __toESM(require("@mui/material/Typography"));
|
|
2976
3011
|
import_styles4 = require("@mui/material/styles");
|
|
2977
|
-
|
|
3012
|
+
import_core_components2 = require("@backstage/core-components");
|
|
2978
3013
|
init_budget();
|
|
2979
3014
|
init_format();
|
|
2980
3015
|
init_ui();
|
|
@@ -2982,7 +3017,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2982
3017
|
const tone = budgetTone2(limit.pct);
|
|
2983
3018
|
const pct = Math.round(limit.pct);
|
|
2984
3019
|
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
2985
|
-
return /* @__PURE__ */
|
|
3020
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Paper5.default, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react8.default.createElement(
|
|
2986
3021
|
import_Typography7.default,
|
|
2987
3022
|
{
|
|
2988
3023
|
variant: "body2",
|
|
@@ -2990,7 +3025,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2990
3025
|
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
2991
3026
|
},
|
|
2992
3027
|
limit.label
|
|
2993
|
-
), limit.sublabel && /* @__PURE__ */
|
|
3028
|
+
), limit.sublabel && /* @__PURE__ */ import_react8.default.createElement(
|
|
2994
3029
|
import_Typography7.default,
|
|
2995
3030
|
{
|
|
2996
3031
|
variant: "caption",
|
|
@@ -3005,7 +3040,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3005
3040
|
}
|
|
3006
3041
|
},
|
|
3007
3042
|
limit.sublabel
|
|
3008
|
-
)), limit.hidden ? /* @__PURE__ */
|
|
3043
|
+
)), limit.hidden ? /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { mt: 1 } }, /* @__PURE__ */ import_react8.default.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ import_react8.default.createElement(
|
|
3009
3044
|
import_Box7.default,
|
|
3010
3045
|
{
|
|
3011
3046
|
sx: {
|
|
@@ -3016,14 +3051,14 @@ var init_BudgetLimitList = __esm({
|
|
|
3016
3051
|
mt: 0.75
|
|
3017
3052
|
}
|
|
3018
3053
|
},
|
|
3019
|
-
/* @__PURE__ */
|
|
3020
|
-
/* @__PURE__ */
|
|
3054
|
+
/* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, "Hidden by admin") : /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
|
|
3055
|
+
/* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3021
3056
|
));
|
|
3022
3057
|
};
|
|
3023
3058
|
TaggedLimit = ({ limit }) => {
|
|
3024
3059
|
const theme = (0, import_styles4.useTheme)();
|
|
3025
3060
|
const tone = budgetTone2(limit.pct);
|
|
3026
|
-
return /* @__PURE__ */
|
|
3061
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react8.default.createElement(
|
|
3027
3062
|
import_Typography7.default,
|
|
3028
3063
|
{
|
|
3029
3064
|
sx: {
|
|
@@ -3036,7 +3071,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3036
3071
|
}
|
|
3037
3072
|
},
|
|
3038
3073
|
limit.kind
|
|
3039
|
-
), /* @__PURE__ */
|
|
3074
|
+
), /* @__PURE__ */ import_react8.default.createElement(LimitCard, { limit }));
|
|
3040
3075
|
};
|
|
3041
3076
|
MoreKeysNote = ({
|
|
3042
3077
|
count,
|
|
@@ -3045,12 +3080,12 @@ var init_BudgetLimitList = __esm({
|
|
|
3045
3080
|
}) => {
|
|
3046
3081
|
const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
|
|
3047
3082
|
if (!href) {
|
|
3048
|
-
return /* @__PURE__ */
|
|
3083
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, text);
|
|
3049
3084
|
}
|
|
3050
|
-
return /* @__PURE__ */
|
|
3085
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_core_components2.Link, { to: href, variant: "caption" }, text, " \u2192");
|
|
3051
3086
|
};
|
|
3052
|
-
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */
|
|
3053
|
-
LimitListPanel = ({ children }) => /* @__PURE__ */
|
|
3087
|
+
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, limits.map((limit) => /* @__PURE__ */ import_react8.default.createElement(TaggedLimit, { key: `${limit.kind}:${limit.sublabel ?? limit.label}`, limit })), hiddenKeyCount > 0 && /* @__PURE__ */ import_react8.default.createElement(MoreKeysNote, { count: hiddenKeyCount, href: keysHref }));
|
|
3088
|
+
LimitListPanel = ({ children }) => /* @__PURE__ */ import_react8.default.createElement(
|
|
3054
3089
|
import_Box7.default,
|
|
3055
3090
|
{
|
|
3056
3091
|
sx: (theme) => ({
|
|
@@ -3066,11 +3101,11 @@ var init_BudgetLimitList = __esm({
|
|
|
3066
3101
|
});
|
|
3067
3102
|
|
|
3068
3103
|
// src/components/LiteLLMBudgetWidget.tsx
|
|
3069
|
-
var
|
|
3104
|
+
var import_react9, import_Paper6, import_Box8, import_Typography8, import_CircularProgress4, import_Alert3, import_Collapse, import_IconButton4, import_icons_material5, import_styles5, import_core_plugin_api3, LevelFrame, EmptyLimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, HierarchyNote, FullBody, CompactBody, LiteLLMBudgetWidget;
|
|
3070
3105
|
var init_LiteLLMBudgetWidget = __esm({
|
|
3071
3106
|
"src/components/LiteLLMBudgetWidget.tsx"() {
|
|
3072
3107
|
"use strict";
|
|
3073
|
-
|
|
3108
|
+
import_react9 = __toESM(require("react"));
|
|
3074
3109
|
import_Paper6 = __toESM(require("@mui/material/Paper"));
|
|
3075
3110
|
import_Box8 = __toESM(require("@mui/material/Box"));
|
|
3076
3111
|
import_Typography8 = __toESM(require("@mui/material/Typography"));
|
|
@@ -3093,7 +3128,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3093
3128
|
tone,
|
|
3094
3129
|
isLast,
|
|
3095
3130
|
children
|
|
3096
|
-
}) => /* @__PURE__ */
|
|
3131
|
+
}) => /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3097
3132
|
import_Box8.default,
|
|
3098
3133
|
{
|
|
3099
3134
|
sx: (theme) => ({
|
|
@@ -3111,7 +3146,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3111
3146
|
})
|
|
3112
3147
|
},
|
|
3113
3148
|
rank
|
|
3114
|
-
), /* @__PURE__ */
|
|
3149
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3115
3150
|
import_Box8.default,
|
|
3116
3151
|
{
|
|
3117
3152
|
sx: (theme) => ({
|
|
@@ -3121,7 +3156,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3121
3156
|
bgcolor: isLast ? "transparent" : (0, import_styles5.alpha)(theme.palette.text.primary, 0.12)
|
|
3122
3157
|
})
|
|
3123
3158
|
}
|
|
3124
|
-
)), /* @__PURE__ */
|
|
3159
|
+
)), /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3125
3160
|
import_Typography8.default,
|
|
3126
3161
|
{
|
|
3127
3162
|
sx: (theme) => ({
|
|
@@ -3133,8 +3168,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3133
3168
|
})
|
|
3134
3169
|
},
|
|
3135
3170
|
name
|
|
3136
|
-
), /* @__PURE__ */
|
|
3137
|
-
EmptyLimitCard = ({ children }) => /* @__PURE__ */
|
|
3171
|
+
), /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
|
|
3172
|
+
EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react9.default.createElement(
|
|
3138
3173
|
import_Paper6.default,
|
|
3139
3174
|
{
|
|
3140
3175
|
variant: "outlined",
|
|
@@ -3145,7 +3180,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3145
3180
|
bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
|
|
3146
3181
|
})
|
|
3147
3182
|
},
|
|
3148
|
-
/* @__PURE__ */
|
|
3183
|
+
/* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
|
|
3149
3184
|
);
|
|
3150
3185
|
CLOSEST_LEVEL_LABEL = {
|
|
3151
3186
|
key: "key",
|
|
@@ -3153,7 +3188,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3153
3188
|
team: "team"
|
|
3154
3189
|
};
|
|
3155
3190
|
KEYS_PATH = "/litellm?tab=keys";
|
|
3156
|
-
HierarchyNote = ({ withReset }) => /* @__PURE__ */
|
|
3191
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react9.default.createElement(
|
|
3157
3192
|
import_Box8.default,
|
|
3158
3193
|
{
|
|
3159
3194
|
sx: (theme) => ({
|
|
@@ -3164,11 +3199,11 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3164
3199
|
borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
|
|
3165
3200
|
})
|
|
3166
3201
|
},
|
|
3167
|
-
/* @__PURE__ */
|
|
3202
|
+
/* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react9.default.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
|
|
3168
3203
|
);
|
|
3169
3204
|
FullBody = ({ summary }) => {
|
|
3170
3205
|
const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
|
|
3171
|
-
return /* @__PURE__ */
|
|
3206
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement(
|
|
3172
3207
|
LevelFrame,
|
|
3173
3208
|
{
|
|
3174
3209
|
rank: 1,
|
|
@@ -3176,10 +3211,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3176
3211
|
tagline: "per-key cap \xB7 highest priority",
|
|
3177
3212
|
note: hasBudgetedKeys ? void 0 : "no key budgets set"
|
|
3178
3213
|
},
|
|
3179
|
-
!hasBudgetedKeys && /* @__PURE__ */
|
|
3180
|
-
summary.keys.map((k) => /* @__PURE__ */
|
|
3181
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */
|
|
3182
|
-
), /* @__PURE__ */
|
|
3214
|
+
!hasBudgetedKeys && /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3215
|
+
summary.keys.map((k) => /* @__PURE__ */ import_react9.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3216
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react9.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
|
|
3217
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3183
3218
|
LevelFrame,
|
|
3184
3219
|
{
|
|
3185
3220
|
rank: 2,
|
|
@@ -3187,15 +3222,15 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3187
3222
|
tagline: "cap on everything you spend with your own keys",
|
|
3188
3223
|
note: "skipped for team-bound keys"
|
|
3189
3224
|
},
|
|
3190
|
-
summary.user ? /* @__PURE__ */
|
|
3191
|
-
), /* @__PURE__ */
|
|
3225
|
+
summary.user ? /* @__PURE__ */ import_react9.default.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
|
|
3226
|
+
), /* @__PURE__ */ import_react9.default.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ import_react9.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react9.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react9.default.createElement(HierarchyNote, { withReset: true })));
|
|
3192
3227
|
};
|
|
3193
3228
|
CompactBody = ({ summary }) => {
|
|
3194
3229
|
const { count } = budgetHeadline(summary);
|
|
3195
3230
|
if (count === 0) {
|
|
3196
|
-
return /* @__PURE__ */
|
|
3231
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
|
|
3197
3232
|
}
|
|
3198
|
-
return /* @__PURE__ */
|
|
3233
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react9.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react9.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react9.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ import_react9.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react9.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
|
|
3199
3234
|
};
|
|
3200
3235
|
LiteLLMBudgetWidget = ({
|
|
3201
3236
|
title = "Budget Policy",
|
|
@@ -3206,13 +3241,13 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3206
3241
|
action
|
|
3207
3242
|
}) => {
|
|
3208
3243
|
const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
|
|
3209
|
-
const [loading, setLoading] = (0,
|
|
3210
|
-
const [error, setError] = (0,
|
|
3211
|
-
const [user, setUser] = (0,
|
|
3212
|
-
const [teams, setTeams] = (0,
|
|
3213
|
-
const [keys, setKeys] = (0,
|
|
3214
|
-
const [expanded, setExpanded] = (0,
|
|
3215
|
-
(0,
|
|
3244
|
+
const [loading, setLoading] = (0, import_react9.useState)(true);
|
|
3245
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
3246
|
+
const [user, setUser] = (0, import_react9.useState)(null);
|
|
3247
|
+
const [teams, setTeams] = (0, import_react9.useState)([]);
|
|
3248
|
+
const [keys, setKeys] = (0, import_react9.useState)([]);
|
|
3249
|
+
const [expanded, setExpanded] = (0, import_react9.useState)(defaultExpanded);
|
|
3250
|
+
(0, import_react9.useEffect)(() => {
|
|
3216
3251
|
let cancelled = false;
|
|
3217
3252
|
setLoading(true);
|
|
3218
3253
|
setError(null);
|
|
@@ -3236,18 +3271,18 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3236
3271
|
cancelled = true;
|
|
3237
3272
|
};
|
|
3238
3273
|
}, [api]);
|
|
3239
|
-
const summary = (0,
|
|
3274
|
+
const summary = (0, import_react9.useMemo)(
|
|
3240
3275
|
() => buildBudgetSummary(user, teams, keys, maxKeys),
|
|
3241
3276
|
[user, teams, keys, maxKeys]
|
|
3242
3277
|
);
|
|
3243
|
-
const headline = (0,
|
|
3278
|
+
const headline = (0, import_react9.useMemo)(() => budgetHeadline(summary), [summary]);
|
|
3244
3279
|
const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
3245
3280
|
const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
|
|
3246
3281
|
headline.closest.pct
|
|
3247
3282
|
)}%`;
|
|
3248
3283
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3249
|
-
const body = /* @__PURE__ */
|
|
3250
|
-
return /* @__PURE__ */
|
|
3284
|
+
const body = /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, loading && /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ import_react9.default.createElement(import_CircularProgress4.default, { size: 32 })), !loading && error && /* @__PURE__ */ import_react9.default.createElement(import_Alert3.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ import_react9.default.createElement(CompactBody, { summary }) : /* @__PURE__ */ import_react9.default.createElement(FullBody, { summary })));
|
|
3285
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Paper6.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react9.default.createElement(
|
|
3251
3286
|
import_Box8.default,
|
|
3252
3287
|
{
|
|
3253
3288
|
onClick: () => setExpanded((e) => !e),
|
|
@@ -3260,14 +3295,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3260
3295
|
mb: expanded ? 1.5 : 0
|
|
3261
3296
|
}
|
|
3262
3297
|
},
|
|
3263
|
-
/* @__PURE__ */
|
|
3298
|
+
/* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { mt: 0.5 } }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3264
3299
|
StatusPill,
|
|
3265
3300
|
{
|
|
3266
3301
|
label: summaryText,
|
|
3267
3302
|
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3268
3303
|
}
|
|
3269
3304
|
))),
|
|
3270
|
-
/* @__PURE__ */
|
|
3305
|
+
/* @__PURE__ */ import_react9.default.createElement(
|
|
3271
3306
|
import_IconButton4.default,
|
|
3272
3307
|
{
|
|
3273
3308
|
size: "small",
|
|
@@ -3283,9 +3318,9 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3283
3318
|
transition: theme.transitions.create("transform")
|
|
3284
3319
|
})
|
|
3285
3320
|
},
|
|
3286
|
-
/* @__PURE__ */
|
|
3321
|
+
/* @__PURE__ */ import_react9.default.createElement(import_icons_material5.ExpandMore, null)
|
|
3287
3322
|
)
|
|
3288
|
-
) : /* @__PURE__ */
|
|
3323
|
+
) : /* @__PURE__ */ import_react9.default.createElement(import_Box8.default, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ import_react9.default.createElement(import_Collapse.default, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ import_react9.default.createElement(
|
|
3289
3324
|
import_Box8.default,
|
|
3290
3325
|
{
|
|
3291
3326
|
sx: (theme) => ({
|
|
@@ -3315,11 +3350,11 @@ function budgetStatValue(hidden, budget) {
|
|
|
3315
3350
|
if (budget > 0) return fmtUsd2(budget);
|
|
3316
3351
|
return "Unlimited";
|
|
3317
3352
|
}
|
|
3318
|
-
var
|
|
3353
|
+
var import_react10, import_Paper7, import_Box9, import_Typography9, import_Skeleton4, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton5, import_Button5, import_CircularProgress5, import_styles6, import_icons_material6, import_recharts2, fmtUsd2, TeamCard, TeamUsage;
|
|
3319
3354
|
var init_TeamUsage = __esm({
|
|
3320
3355
|
"src/components/TeamUsage.tsx"() {
|
|
3321
3356
|
"use strict";
|
|
3322
|
-
|
|
3357
|
+
import_react10 = __toESM(require("react"));
|
|
3323
3358
|
import_Paper7 = __toESM(require("@mui/material/Paper"));
|
|
3324
3359
|
import_Box9 = __toESM(require("@mui/material/Box"));
|
|
3325
3360
|
import_Typography9 = __toESM(require("@mui/material/Typography"));
|
|
@@ -3341,7 +3376,7 @@ var init_TeamUsage = __esm({
|
|
|
3341
3376
|
init_ui();
|
|
3342
3377
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
3343
3378
|
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
|
|
3344
|
-
const [expanded, setExpanded] = (0,
|
|
3379
|
+
const [expanded, setExpanded] = (0, import_react10.useState)(false);
|
|
3345
3380
|
const chart = useChartTheme();
|
|
3346
3381
|
const hidden = team.budget_hidden === true;
|
|
3347
3382
|
const budget = hidden ? 0 : team.max_budget ?? 0;
|
|
@@ -3353,10 +3388,10 @@ var init_TeamUsage = __esm({
|
|
|
3353
3388
|
const renderDailySpendSection = () => {
|
|
3354
3389
|
if (hidden) return null;
|
|
3355
3390
|
if (usageLoading) {
|
|
3356
|
-
return /* @__PURE__ */
|
|
3391
|
+
return /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react10.default.createElement(import_CircularProgress5.default, { size: 22 }));
|
|
3357
3392
|
}
|
|
3358
3393
|
if (dailyData.length === 0) return null;
|
|
3359
|
-
return /* @__PURE__ */
|
|
3394
|
+
return /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3360
3395
|
import_Typography9.default,
|
|
3361
3396
|
{
|
|
3362
3397
|
variant: "caption",
|
|
@@ -3364,13 +3399,13 @@ var init_TeamUsage = __esm({
|
|
|
3364
3399
|
sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
|
|
3365
3400
|
},
|
|
3366
3401
|
"Daily spend"
|
|
3367
|
-
), /* @__PURE__ */
|
|
3402
|
+
), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { height: 150 }, /* @__PURE__ */ import_react10.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react10.default.createElement(import_recharts2.AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react10.default.createElement(import_recharts2.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react10.default.createElement(import_recharts2.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react10.default.createElement(import_recharts2.YAxis, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ import_react10.default.createElement(
|
|
3368
3403
|
import_recharts2.Tooltip,
|
|
3369
3404
|
{
|
|
3370
3405
|
cursor: chart.cursor,
|
|
3371
|
-
content: /* @__PURE__ */
|
|
3406
|
+
content: /* @__PURE__ */ import_react10.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
3372
3407
|
}
|
|
3373
|
-
), /* @__PURE__ */
|
|
3408
|
+
), /* @__PURE__ */ import_react10.default.createElement(
|
|
3374
3409
|
import_recharts2.Area,
|
|
3375
3410
|
{
|
|
3376
3411
|
type: "monotone",
|
|
@@ -3383,7 +3418,7 @@ var init_TeamUsage = __esm({
|
|
|
3383
3418
|
}
|
|
3384
3419
|
)))));
|
|
3385
3420
|
};
|
|
3386
|
-
const sectionLabel = (label) => /* @__PURE__ */
|
|
3421
|
+
const sectionLabel = (label) => /* @__PURE__ */ import_react10.default.createElement(
|
|
3387
3422
|
import_Typography9.default,
|
|
3388
3423
|
{
|
|
3389
3424
|
variant: "caption",
|
|
@@ -3392,7 +3427,7 @@ var init_TeamUsage = __esm({
|
|
|
3392
3427
|
},
|
|
3393
3428
|
label
|
|
3394
3429
|
);
|
|
3395
|
-
return /* @__PURE__ */
|
|
3430
|
+
return /* @__PURE__ */ import_react10.default.createElement(import_Paper7.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3396
3431
|
import_Box9.default,
|
|
3397
3432
|
{
|
|
3398
3433
|
sx: (theme) => ({
|
|
@@ -3407,8 +3442,8 @@ var init_TeamUsage = __esm({
|
|
|
3407
3442
|
color: theme.palette.primary.main
|
|
3408
3443
|
})
|
|
3409
3444
|
},
|
|
3410
|
-
/* @__PURE__ */
|
|
3411
|
-
), /* @__PURE__ */
|
|
3445
|
+
/* @__PURE__ */ import_react10.default.createElement(import_icons_material6.Group, { fontSize: "small" })
|
|
3446
|
+
), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ import_react10.default.createElement(
|
|
3412
3447
|
import_Typography9.default,
|
|
3413
3448
|
{
|
|
3414
3449
|
variant: "caption",
|
|
@@ -3416,7 +3451,7 @@ var init_TeamUsage = __esm({
|
|
|
3416
3451
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3417
3452
|
},
|
|
3418
3453
|
team.team_id
|
|
3419
|
-
)), isOver && /* @__PURE__ */
|
|
3454
|
+
)), isOver && /* @__PURE__ */ import_react10.default.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ import_react10.default.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ import_react10.default.createElement(
|
|
3420
3455
|
import_Button5.default,
|
|
3421
3456
|
{
|
|
3422
3457
|
size: "small",
|
|
@@ -3425,7 +3460,7 @@ var init_TeamUsage = __esm({
|
|
|
3425
3460
|
sx: { textTransform: "none" }
|
|
3426
3461
|
},
|
|
3427
3462
|
"Edit"
|
|
3428
|
-
), /* @__PURE__ */
|
|
3463
|
+
), /* @__PURE__ */ import_react10.default.createElement(
|
|
3429
3464
|
import_IconButton5.default,
|
|
3430
3465
|
{
|
|
3431
3466
|
size: "small",
|
|
@@ -3438,8 +3473,8 @@ var init_TeamUsage = __esm({
|
|
|
3438
3473
|
transition: theme.transitions.create("transform")
|
|
3439
3474
|
})
|
|
3440
3475
|
},
|
|
3441
|
-
/* @__PURE__ */
|
|
3442
|
-
)), /* @__PURE__ */
|
|
3476
|
+
/* @__PURE__ */ import_react10.default.createElement(import_icons_material6.ExpandMore, null)
|
|
3477
|
+
)), /* @__PURE__ */ import_react10.default.createElement(
|
|
3443
3478
|
import_Box9.default,
|
|
3444
3479
|
{
|
|
3445
3480
|
sx: {
|
|
@@ -3450,20 +3485,20 @@ var init_TeamUsage = __esm({
|
|
|
3450
3485
|
maxWidth: 640
|
|
3451
3486
|
}
|
|
3452
3487
|
},
|
|
3453
|
-
/* @__PURE__ */
|
|
3454
|
-
/* @__PURE__ */
|
|
3455
|
-
/* @__PURE__ */
|
|
3456
|
-
/* @__PURE__ */
|
|
3457
|
-
/* @__PURE__ */
|
|
3458
|
-
/* @__PURE__ */
|
|
3459
|
-
), hidden && /* @__PURE__ */
|
|
3488
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
|
|
3489
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
|
|
3490
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
|
|
3491
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
|
|
3492
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
3493
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
3494
|
+
), hidden && /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ import_react10.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.75 } }, "Team budget is hidden \u2014 showing usage level only", team.budget_duration ? ` \xB7 resets every ${team.budget_duration}` : "", ".")), !hidden && budget > 0 && /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react10.default.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react10.default.createElement(import_Collapse2.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react10.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react10.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react10.default.createElement(
|
|
3460
3495
|
import_TableContainer4.default,
|
|
3461
3496
|
{
|
|
3462
3497
|
component: import_Paper7.default,
|
|
3463
3498
|
variant: "outlined",
|
|
3464
3499
|
sx: { borderRadius: 1.5 }
|
|
3465
3500
|
},
|
|
3466
|
-
/* @__PURE__ */
|
|
3501
|
+
/* @__PURE__ */ import_react10.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react10.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, null, "User"), /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, { align: "right" }, "Role"))), /* @__PURE__ */ import_react10.default.createElement(import_TableBody4.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react10.default.createElement(import_TableRow4.default, { key: m.user_id }, /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, null, m.user_email ? /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react10.default.createElement(
|
|
3467
3502
|
import_Typography9.default,
|
|
3468
3503
|
{
|
|
3469
3504
|
variant: "caption",
|
|
@@ -3471,14 +3506,14 @@ var init_TeamUsage = __esm({
|
|
|
3471
3506
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3472
3507
|
},
|
|
3473
3508
|
m.user_id
|
|
3474
|
-
)) : /* @__PURE__ */
|
|
3509
|
+
)) : /* @__PURE__ */ import_react10.default.createElement(
|
|
3475
3510
|
import_Typography9.default,
|
|
3476
3511
|
{
|
|
3477
3512
|
variant: "body2",
|
|
3478
3513
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
3479
3514
|
},
|
|
3480
3515
|
m.user_id
|
|
3481
|
-
)), /* @__PURE__ */
|
|
3516
|
+
)), /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3482
3517
|
StatusPill,
|
|
3483
3518
|
{
|
|
3484
3519
|
label: m.role,
|
|
@@ -3486,7 +3521,7 @@ var init_TeamUsage = __esm({
|
|
|
3486
3521
|
dot: false
|
|
3487
3522
|
}
|
|
3488
3523
|
))))))
|
|
3489
|
-
) : /* @__PURE__ */
|
|
3524
|
+
) : /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
|
|
3490
3525
|
};
|
|
3491
3526
|
TeamUsage = ({
|
|
3492
3527
|
teams,
|
|
@@ -3498,22 +3533,22 @@ var init_TeamUsage = __esm({
|
|
|
3498
3533
|
canCreate,
|
|
3499
3534
|
onCreateTeam
|
|
3500
3535
|
}) => {
|
|
3501
|
-
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */
|
|
3536
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react10.default.createElement(
|
|
3502
3537
|
import_Button5.default,
|
|
3503
3538
|
{
|
|
3504
3539
|
variant: "contained",
|
|
3505
3540
|
color: "primary",
|
|
3506
3541
|
disableElevation: true,
|
|
3507
|
-
startIcon: /* @__PURE__ */
|
|
3542
|
+
startIcon: /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.Add, null),
|
|
3508
3543
|
onClick: onCreateTeam
|
|
3509
3544
|
},
|
|
3510
3545
|
"Create Team"
|
|
3511
3546
|
) : void 0;
|
|
3512
3547
|
if (loading) {
|
|
3513
|
-
return /* @__PURE__ */
|
|
3548
|
+
return /* @__PURE__ */ import_react10.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react10.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react10.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
|
|
3514
3549
|
}
|
|
3515
3550
|
if (!teams.length) {
|
|
3516
|
-
return /* @__PURE__ */
|
|
3551
|
+
return /* @__PURE__ */ import_react10.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3517
3552
|
EmptyState,
|
|
3518
3553
|
{
|
|
3519
3554
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -3521,14 +3556,14 @@ var init_TeamUsage = __esm({
|
|
|
3521
3556
|
}
|
|
3522
3557
|
));
|
|
3523
3558
|
}
|
|
3524
|
-
return /* @__PURE__ */
|
|
3559
|
+
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3525
3560
|
SectionCard,
|
|
3526
3561
|
{
|
|
3527
3562
|
title: "Teams",
|
|
3528
3563
|
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
3529
3564
|
actions: createAction
|
|
3530
3565
|
},
|
|
3531
|
-
/* @__PURE__ */
|
|
3566
|
+
/* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react10.default.createElement(
|
|
3532
3567
|
TeamCard,
|
|
3533
3568
|
{
|
|
3534
3569
|
key: team.team_id,
|
|
@@ -3562,11 +3597,11 @@ function fmtTokens(n) {
|
|
|
3562
3597
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
3563
3598
|
return String(n);
|
|
3564
3599
|
}
|
|
3565
|
-
var
|
|
3600
|
+
var import_react11, import_Box10, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography10, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles7, ALL_PROXY_MODELS2, ModelsTable;
|
|
3566
3601
|
var init_ModelsTable = __esm({
|
|
3567
3602
|
"src/components/ModelsTable.tsx"() {
|
|
3568
3603
|
"use strict";
|
|
3569
|
-
|
|
3604
|
+
import_react11 = __toESM(require("react"));
|
|
3570
3605
|
import_Box10 = __toESM(require("@mui/material/Box"));
|
|
3571
3606
|
import_Table5 = __toESM(require("@mui/material/Table"));
|
|
3572
3607
|
import_TableBody5 = __toESM(require("@mui/material/TableBody"));
|
|
@@ -3592,20 +3627,20 @@ var init_ModelsTable = __esm({
|
|
|
3592
3627
|
ALL_PROXY_MODELS2 = "all-proxy-models";
|
|
3593
3628
|
ModelsTable = ({ allModels, teams, loading }) => {
|
|
3594
3629
|
const theme = (0, import_styles7.useTheme)();
|
|
3595
|
-
const [selectedTeamId, setSelectedTeamId] = (0,
|
|
3596
|
-
const [filterText, setFilterText] = (0,
|
|
3597
|
-
const [copiedSnackbar, setCopiedSnackbar] = (0,
|
|
3598
|
-
const [sortKey, setSortKey] = (0,
|
|
3599
|
-
const [sortDirection, setSortDirection] = (0,
|
|
3600
|
-
const selectedTeam = (0,
|
|
3630
|
+
const [selectedTeamId, setSelectedTeamId] = (0, import_react11.useState)("");
|
|
3631
|
+
const [filterText, setFilterText] = (0, import_react11.useState)("");
|
|
3632
|
+
const [copiedSnackbar, setCopiedSnackbar] = (0, import_react11.useState)(false);
|
|
3633
|
+
const [sortKey, setSortKey] = (0, import_react11.useState)("model_name");
|
|
3634
|
+
const [sortDirection, setSortDirection] = (0, import_react11.useState)("asc");
|
|
3635
|
+
const selectedTeam = (0, import_react11.useMemo)(
|
|
3601
3636
|
() => teams.find((t) => t.team_id === selectedTeamId) ?? null,
|
|
3602
3637
|
[teams, selectedTeamId]
|
|
3603
3638
|
);
|
|
3604
|
-
const teamModels = (0,
|
|
3639
|
+
const teamModels = (0, import_react11.useMemo)(() => {
|
|
3605
3640
|
if (!selectedTeam) return [];
|
|
3606
3641
|
return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
|
|
3607
3642
|
}, [allModels, selectedTeam]);
|
|
3608
|
-
const filteredModels = (0,
|
|
3643
|
+
const filteredModels = (0, import_react11.useMemo)(() => {
|
|
3609
3644
|
const q = filterText.trim().toLowerCase();
|
|
3610
3645
|
let models = teamModels;
|
|
3611
3646
|
if (q) {
|
|
@@ -3652,10 +3687,10 @@ var init_ModelsTable = __esm({
|
|
|
3652
3687
|
return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
|
|
3653
3688
|
});
|
|
3654
3689
|
}, [teamModels, filterText, sortKey, sortDirection]);
|
|
3655
|
-
const handleCopyModelId = (0,
|
|
3690
|
+
const handleCopyModelId = (0, import_react11.useCallback)((modelId) => {
|
|
3656
3691
|
navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
|
|
3657
3692
|
}, []);
|
|
3658
|
-
const handleSort = (0,
|
|
3693
|
+
const handleSort = (0, import_react11.useCallback)((key) => {
|
|
3659
3694
|
if (sortKey === key) {
|
|
3660
3695
|
setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
|
|
3661
3696
|
} else {
|
|
@@ -3677,13 +3712,13 @@ var init_ModelsTable = __esm({
|
|
|
3677
3712
|
return theme.palette.info.main;
|
|
3678
3713
|
}
|
|
3679
3714
|
};
|
|
3680
|
-
return /* @__PURE__ */
|
|
3715
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
3681
3716
|
SectionCard,
|
|
3682
3717
|
{
|
|
3683
3718
|
title: "Available Models",
|
|
3684
3719
|
subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
|
|
3685
3720
|
},
|
|
3686
|
-
/* @__PURE__ */
|
|
3721
|
+
/* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ import_react11.default.createElement(import_FormControl.default, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ import_react11.default.createElement(import_InputLabel.default, { id: "team-select-label" }, "Team"), /* @__PURE__ */ import_react11.default.createElement(
|
|
3687
3722
|
import_Select.default,
|
|
3688
3723
|
{
|
|
3689
3724
|
labelId: "team-select-label",
|
|
@@ -3691,8 +3726,8 @@ var init_ModelsTable = __esm({
|
|
|
3691
3726
|
label: "Team",
|
|
3692
3727
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
3693
3728
|
},
|
|
3694
|
-
teams.map((t) => /* @__PURE__ */
|
|
3695
|
-
)), /* @__PURE__ */
|
|
3729
|
+
teams.map((t) => /* @__PURE__ */ import_react11.default.createElement(import_MenuItem4.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
|
|
3730
|
+
)), /* @__PURE__ */ import_react11.default.createElement(
|
|
3696
3731
|
import_TextField5.default,
|
|
3697
3732
|
{
|
|
3698
3733
|
label: "Filter Models",
|
|
@@ -3702,33 +3737,33 @@ var init_ModelsTable = __esm({
|
|
|
3702
3737
|
disabled: !selectedTeamId,
|
|
3703
3738
|
sx: { minWidth: 240, mb: 2 },
|
|
3704
3739
|
InputProps: {
|
|
3705
|
-
startAdornment: /* @__PURE__ */
|
|
3740
|
+
startAdornment: /* @__PURE__ */ import_react11.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react11.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
|
|
3706
3741
|
}
|
|
3707
3742
|
}
|
|
3708
3743
|
)),
|
|
3709
|
-
loading && /* @__PURE__ */
|
|
3710
|
-
!loading && !selectedTeamId && /* @__PURE__ */
|
|
3744
|
+
loading && /* @__PURE__ */ import_react11.default.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
3745
|
+
!loading && !selectedTeamId && /* @__PURE__ */ import_react11.default.createElement(
|
|
3711
3746
|
EmptyState,
|
|
3712
3747
|
{
|
|
3713
3748
|
message: "Select a team to see available models",
|
|
3714
3749
|
hint: "Each team may have a different set of models assigned"
|
|
3715
3750
|
}
|
|
3716
3751
|
),
|
|
3717
|
-
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */
|
|
3752
|
+
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react11.default.createElement(
|
|
3718
3753
|
EmptyState,
|
|
3719
3754
|
{
|
|
3720
3755
|
message: "No models available for this team",
|
|
3721
3756
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
3722
3757
|
}
|
|
3723
3758
|
),
|
|
3724
|
-
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */
|
|
3759
|
+
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react11.default.createElement(
|
|
3725
3760
|
EmptyState,
|
|
3726
3761
|
{
|
|
3727
3762
|
message: "No models match your search",
|
|
3728
3763
|
hint: "Try a different search term"
|
|
3729
3764
|
}
|
|
3730
3765
|
),
|
|
3731
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */
|
|
3766
|
+
!loading && filteredModels.length > 0 && /* @__PURE__ */ import_react11.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react11.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react11.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react11.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react11.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react11.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react11.default.createElement(
|
|
3732
3767
|
"span",
|
|
3733
3768
|
{
|
|
3734
3769
|
role: "button",
|
|
@@ -3738,8 +3773,8 @@ var init_ModelsTable = __esm({
|
|
|
3738
3773
|
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
3739
3774
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3740
3775
|
},
|
|
3741
|
-
/* @__PURE__ */
|
|
3742
|
-
)), /* @__PURE__ */
|
|
3776
|
+
/* @__PURE__ */ import_react11.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
3777
|
+
)), /* @__PURE__ */ import_react11.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react11.default.createElement(
|
|
3743
3778
|
import_Box10.default,
|
|
3744
3779
|
{
|
|
3745
3780
|
component: "span",
|
|
@@ -3750,8 +3785,8 @@ var init_ModelsTable = __esm({
|
|
|
3750
3785
|
sx: quietIconButtonSx("accent"),
|
|
3751
3786
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3752
3787
|
},
|
|
3753
|
-
/* @__PURE__ */
|
|
3754
|
-
)))), /* @__PURE__ */
|
|
3788
|
+
/* @__PURE__ */ import_react11.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
|
|
3789
|
+
)))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react11.default.createElement(
|
|
3755
3790
|
import_Box10.default,
|
|
3756
3791
|
{
|
|
3757
3792
|
component: "span",
|
|
@@ -3768,8 +3803,8 @@ var init_ModelsTable = __esm({
|
|
|
3768
3803
|
}
|
|
3769
3804
|
},
|
|
3770
3805
|
m.mode
|
|
3771
|
-
)), /* @__PURE__ */
|
|
3772
|
-
/* @__PURE__ */
|
|
3806
|
+
)), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react11.default.createElement(import_Typography10.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react11.default.createElement(import_Box10.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react11.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react11.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
|
|
3807
|
+
/* @__PURE__ */ import_react11.default.createElement(
|
|
3773
3808
|
import_Snackbar.default,
|
|
3774
3809
|
{
|
|
3775
3810
|
open: copiedSnackbar,
|
|
@@ -3777,7 +3812,7 @@ var init_ModelsTable = __esm({
|
|
|
3777
3812
|
onClose: () => setCopiedSnackbar(false),
|
|
3778
3813
|
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
3779
3814
|
},
|
|
3780
|
-
/* @__PURE__ */
|
|
3815
|
+
/* @__PURE__ */ import_react11.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
3781
3816
|
)
|
|
3782
3817
|
);
|
|
3783
3818
|
};
|
|
@@ -3805,10 +3840,10 @@ function formatDateTime(iso) {
|
|
|
3805
3840
|
}
|
|
3806
3841
|
function renderAuditLogBody(loading, entries) {
|
|
3807
3842
|
if (loading) {
|
|
3808
|
-
return /* @__PURE__ */
|
|
3843
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react12.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
|
|
3809
3844
|
}
|
|
3810
3845
|
if (entries.length === 0) {
|
|
3811
|
-
return /* @__PURE__ */
|
|
3846
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3812
3847
|
EmptyState,
|
|
3813
3848
|
{
|
|
3814
3849
|
message: "No audit events found",
|
|
@@ -3816,13 +3851,13 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3816
3851
|
}
|
|
3817
3852
|
)));
|
|
3818
3853
|
}
|
|
3819
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
3854
|
+
return entries.map((entry) => /* @__PURE__ */ import_react12.default.createElement(DetailRow, { key: entry.id, entry }));
|
|
3820
3855
|
}
|
|
3821
|
-
var
|
|
3856
|
+
var import_react12, import_Box11, import_Typography11, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_TablePagination, import_TextField6, import_MenuItem5, import_Skeleton5, import_Collapse3, import_IconButton6, import_Alert5, import_styles8, import_icons_material8, import_react_use2, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
3822
3857
|
var init_AuditLog = __esm({
|
|
3823
3858
|
"src/components/AuditLog.tsx"() {
|
|
3824
3859
|
"use strict";
|
|
3825
|
-
|
|
3860
|
+
import_react12 = __toESM(require("react"));
|
|
3826
3861
|
import_Box11 = __toESM(require("@mui/material/Box"));
|
|
3827
3862
|
import_Typography11 = __toESM(require("@mui/material/Typography"));
|
|
3828
3863
|
import_Table6 = __toESM(require("@mui/material/Table"));
|
|
@@ -3857,9 +3892,9 @@ var init_AuditLog = __esm({
|
|
|
3857
3892
|
LiteLLM_UserTable: "User"
|
|
3858
3893
|
};
|
|
3859
3894
|
DetailRow = ({ entry }) => {
|
|
3860
|
-
const [open, setOpen] = (0,
|
|
3895
|
+
const [open, setOpen] = (0, import_react12.useState)(false);
|
|
3861
3896
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3862
|
-
return /* @__PURE__ */
|
|
3897
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react12.default.createElement(
|
|
3863
3898
|
import_IconButton6.default,
|
|
3864
3899
|
{
|
|
3865
3900
|
size: "small",
|
|
@@ -3872,8 +3907,8 @@ var init_AuditLog = __esm({
|
|
|
3872
3907
|
transition: theme.transitions.create("transform")
|
|
3873
3908
|
})
|
|
3874
3909
|
},
|
|
3875
|
-
/* @__PURE__ */
|
|
3876
|
-
)), /* @__PURE__ */
|
|
3910
|
+
/* @__PURE__ */ import_react12.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
|
|
3911
|
+
)), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react12.default.createElement(import_Typography11.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, entry.action && /* @__PURE__ */ import_react12.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_Typography11.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react12.default.createElement(
|
|
3877
3912
|
import_Typography11.default,
|
|
3878
3913
|
{
|
|
3879
3914
|
variant: "body2",
|
|
@@ -3883,7 +3918,7 @@ var init_AuditLog = __esm({
|
|
|
3883
3918
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3884
3919
|
},
|
|
3885
3920
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3886
|
-
)), /* @__PURE__ */
|
|
3921
|
+
)), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react12.default.createElement(import_Collapse3.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3887
3922
|
import_Box11.default,
|
|
3888
3923
|
{
|
|
3889
3924
|
display: "flex",
|
|
@@ -3896,7 +3931,7 @@ var init_AuditLog = __esm({
|
|
|
3896
3931
|
bgcolor: (0, import_styles8.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
3897
3932
|
})
|
|
3898
3933
|
},
|
|
3899
|
-
entry.before_value && /* @__PURE__ */
|
|
3934
|
+
entry.before_value && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3900
3935
|
import_Typography11.default,
|
|
3901
3936
|
{
|
|
3902
3937
|
variant: "caption",
|
|
@@ -3906,8 +3941,8 @@ var init_AuditLog = __esm({
|
|
|
3906
3941
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3907
3942
|
},
|
|
3908
3943
|
"Before"
|
|
3909
|
-
), /* @__PURE__ */
|
|
3910
|
-
entry.updated_values && /* @__PURE__ */
|
|
3944
|
+
), /* @__PURE__ */ import_react12.default.createElement(import_Typography11.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
|
|
3945
|
+
entry.updated_values && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3911
3946
|
import_Typography11.default,
|
|
3912
3947
|
{
|
|
3913
3948
|
variant: "caption",
|
|
@@ -3917,14 +3952,14 @@ var init_AuditLog = __esm({
|
|
|
3917
3952
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3918
3953
|
},
|
|
3919
3954
|
"After"
|
|
3920
|
-
), /* @__PURE__ */
|
|
3955
|
+
), /* @__PURE__ */ import_react12.default.createElement(import_Typography11.default, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
|
|
3921
3956
|
)))));
|
|
3922
3957
|
};
|
|
3923
3958
|
AuditLog = ({ api }) => {
|
|
3924
|
-
const [page, setPage] = (0,
|
|
3925
|
-
const [pageSize, setPageSize] = (0,
|
|
3926
|
-
const [filters, setFilters] = (0,
|
|
3927
|
-
const fetchParams = (0,
|
|
3959
|
+
const [page, setPage] = (0, import_react12.useState)(0);
|
|
3960
|
+
const [pageSize, setPageSize] = (0, import_react12.useState)(25);
|
|
3961
|
+
const [filters, setFilters] = (0, import_react12.useState)({});
|
|
3962
|
+
const fetchParams = (0, import_react12.useMemo)(
|
|
3928
3963
|
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
3929
3964
|
[page, pageSize, filters]
|
|
3930
3965
|
);
|
|
@@ -3934,13 +3969,13 @@ var init_AuditLog = __esm({
|
|
|
3934
3969
|
);
|
|
3935
3970
|
const entries = value?.audit_logs ?? [];
|
|
3936
3971
|
const total = value?.total ?? 0;
|
|
3937
|
-
return /* @__PURE__ */
|
|
3972
|
+
return /* @__PURE__ */ import_react12.default.createElement(
|
|
3938
3973
|
SectionCard,
|
|
3939
3974
|
{
|
|
3940
3975
|
title: "Audit Log",
|
|
3941
3976
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
3942
3977
|
flush: true,
|
|
3943
|
-
actions: /* @__PURE__ */
|
|
3978
|
+
actions: /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(
|
|
3944
3979
|
import_TextField6.default,
|
|
3945
3980
|
{
|
|
3946
3981
|
size: "small",
|
|
@@ -3953,12 +3988,12 @@ var init_AuditLog = __esm({
|
|
|
3953
3988
|
},
|
|
3954
3989
|
sx: { minWidth: 150 }
|
|
3955
3990
|
},
|
|
3956
|
-
/* @__PURE__ */
|
|
3957
|
-
/* @__PURE__ */
|
|
3958
|
-
/* @__PURE__ */
|
|
3959
|
-
/* @__PURE__ */
|
|
3960
|
-
/* @__PURE__ */
|
|
3961
|
-
), /* @__PURE__ */
|
|
3991
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
|
|
3992
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
|
|
3993
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
|
|
3994
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
|
|
3995
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
|
|
3996
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
3962
3997
|
import_TextField6.default,
|
|
3963
3998
|
{
|
|
3964
3999
|
size: "small",
|
|
@@ -3971,13 +4006,13 @@ var init_AuditLog = __esm({
|
|
|
3971
4006
|
},
|
|
3972
4007
|
sx: { minWidth: 150 }
|
|
3973
4008
|
},
|
|
3974
|
-
/* @__PURE__ */
|
|
3975
|
-
/* @__PURE__ */
|
|
3976
|
-
/* @__PURE__ */
|
|
3977
|
-
/* @__PURE__ */
|
|
3978
|
-
/* @__PURE__ */
|
|
3979
|
-
/* @__PURE__ */
|
|
3980
|
-
), /* @__PURE__ */
|
|
4009
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
|
|
4010
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
4011
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
4012
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
|
|
4013
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
|
|
4014
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
|
|
4015
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
3981
4016
|
import_TextField6.default,
|
|
3982
4017
|
{
|
|
3983
4018
|
size: "small",
|
|
@@ -3991,9 +4026,9 @@ var init_AuditLog = __esm({
|
|
|
3991
4026
|
}
|
|
3992
4027
|
))
|
|
3993
4028
|
},
|
|
3994
|
-
error && /* @__PURE__ */
|
|
3995
|
-
/* @__PURE__ */
|
|
3996
|
-
/* @__PURE__ */
|
|
4029
|
+
error && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react12.default.createElement(import_Alert5.default, { severity: "error" }, error.message)),
|
|
4030
|
+
/* @__PURE__ */ import_react12.default.createElement(import_TableContainer6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_Table6.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react12.default.createElement(import_TableHead6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Time"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Action"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Table"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Object ID"), /* @__PURE__ */ import_react12.default.createElement(import_TableCell6.default, null, "Changed By"))), /* @__PURE__ */ import_react12.default.createElement(import_TableBody6.default, null, renderAuditLogBody(loading, entries)))),
|
|
4031
|
+
/* @__PURE__ */ import_react12.default.createElement(
|
|
3997
4032
|
import_TablePagination.default,
|
|
3998
4033
|
{
|
|
3999
4034
|
component: "div",
|
|
@@ -4062,11 +4097,11 @@ function initDateRange() {
|
|
|
4062
4097
|
else start.setDate(start.getDate() - 7);
|
|
4063
4098
|
return { start, end };
|
|
4064
4099
|
}
|
|
4065
|
-
var
|
|
4100
|
+
var import_react13, import_Box12, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography12, import_Paper8, import_Tabs2, import_Tab2, import_Grid2, import_react_router_dom, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
|
|
4066
4101
|
var init_LiteLLMPage = __esm({
|
|
4067
4102
|
"src/components/LiteLLMPage.tsx"() {
|
|
4068
4103
|
"use strict";
|
|
4069
|
-
|
|
4104
|
+
import_react13 = __toESM(require("react"));
|
|
4070
4105
|
import_Box12 = __toESM(require("@mui/material/Box"));
|
|
4071
4106
|
import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
|
|
4072
4107
|
import_Alert6 = __toESM(require("@mui/material/Alert"));
|
|
@@ -4096,8 +4131,8 @@ var init_LiteLLMPage = __esm({
|
|
|
4096
4131
|
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
4097
4132
|
LiteLLMPage = () => {
|
|
4098
4133
|
const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
|
|
4099
|
-
const [dateRange, setDateRange] = (0,
|
|
4100
|
-
const [currentPreset, setCurrentPreset] = (0,
|
|
4134
|
+
const [dateRange, setDateRange] = (0, import_react13.useState)(initDateRange);
|
|
4135
|
+
const [currentPreset, setCurrentPreset] = (0, import_react13.useState)(() => {
|
|
4101
4136
|
try {
|
|
4102
4137
|
return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
|
|
4103
4138
|
} catch {
|
|
@@ -4105,14 +4140,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4105
4140
|
}
|
|
4106
4141
|
});
|
|
4107
4142
|
const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
|
|
4108
|
-
const [activeTab, setActiveTab] = (0,
|
|
4143
|
+
const [activeTab, setActiveTab] = (0, import_react13.useState)(() => {
|
|
4109
4144
|
const t = searchParams.get("tab");
|
|
4110
4145
|
return isPageTab(t) ? t : "overview";
|
|
4111
4146
|
});
|
|
4112
|
-
const [generateDialogOpen, setGenerateDialogOpen] = (0,
|
|
4147
|
+
const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react13.useState)(
|
|
4113
4148
|
() => searchParams.get("generate") === "1"
|
|
4114
4149
|
);
|
|
4115
|
-
(0,
|
|
4150
|
+
(0, import_react13.useEffect)(() => {
|
|
4116
4151
|
if (searchParams.get("generate") !== "1") return;
|
|
4117
4152
|
setGenerateDialogOpen(true);
|
|
4118
4153
|
setSearchParams(
|
|
@@ -4124,7 +4159,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4124
4159
|
{ replace: true }
|
|
4125
4160
|
);
|
|
4126
4161
|
}, [searchParams, setSearchParams]);
|
|
4127
|
-
const selectTab = (0,
|
|
4162
|
+
const selectTab = (0, import_react13.useCallback)(
|
|
4128
4163
|
(tab) => {
|
|
4129
4164
|
setActiveTab(tab);
|
|
4130
4165
|
setSearchParams(
|
|
@@ -4138,12 +4173,12 @@ var init_LiteLLMPage = __esm({
|
|
|
4138
4173
|
},
|
|
4139
4174
|
[setSearchParams]
|
|
4140
4175
|
);
|
|
4141
|
-
const [snackbar, setSnackbar] = (0,
|
|
4142
|
-
const [manageTeam, setManageTeam] = (0,
|
|
4143
|
-
const [keyToEdit, setKeyToEdit] = (0,
|
|
4176
|
+
const [snackbar, setSnackbar] = (0, import_react13.useState)(null);
|
|
4177
|
+
const [manageTeam, setManageTeam] = (0, import_react13.useState)(null);
|
|
4178
|
+
const [keyToEdit, setKeyToEdit] = (0, import_react13.useState)(null);
|
|
4144
4179
|
const keyFormOpen = generateDialogOpen || !!keyToEdit;
|
|
4145
|
-
const [teamUsageCache, setTeamUsageCache] = (0,
|
|
4146
|
-
const [teamUsageLoading, setTeamUsageLoading] = (0,
|
|
4180
|
+
const [teamUsageCache, setTeamUsageCache] = (0, import_react13.useState)({});
|
|
4181
|
+
const [teamUsageLoading, setTeamUsageLoading] = (0, import_react13.useState)({});
|
|
4147
4182
|
const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
|
|
4148
4183
|
() => api.getUserInfo(),
|
|
4149
4184
|
[api]
|
|
@@ -4187,7 +4222,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4187
4222
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
4188
4223
|
[api, objectPermsEnabled]
|
|
4189
4224
|
);
|
|
4190
|
-
const teams = (0,
|
|
4225
|
+
const teams = (0, import_react13.useMemo)(() => {
|
|
4191
4226
|
if (!allTeams?.length) return [];
|
|
4192
4227
|
if (!userInfo) return allTeams;
|
|
4193
4228
|
const userId = userInfo.user_id;
|
|
@@ -4204,7 +4239,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4204
4239
|
const endDate = dateRange.end.toISOString().split("T")[0];
|
|
4205
4240
|
return api.getUsage(startDate, endDate);
|
|
4206
4241
|
}, [api, dateRange]);
|
|
4207
|
-
const handleDateRangeChange = (0,
|
|
4242
|
+
const handleDateRangeChange = (0, import_react13.useCallback)((range, preset) => {
|
|
4208
4243
|
setDateRange(range);
|
|
4209
4244
|
if (preset) {
|
|
4210
4245
|
setCurrentPreset(preset);
|
|
@@ -4212,7 +4247,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4212
4247
|
setTeamUsageCache({});
|
|
4213
4248
|
setTeamUsageLoading({});
|
|
4214
4249
|
}, [setDateRange, setCurrentPreset]);
|
|
4215
|
-
const loadTeamUsage = (0,
|
|
4250
|
+
const loadTeamUsage = (0, import_react13.useCallback)(async (teamId) => {
|
|
4216
4251
|
if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
|
|
4217
4252
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
|
|
4218
4253
|
try {
|
|
@@ -4226,7 +4261,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4226
4261
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
|
|
4227
4262
|
}
|
|
4228
4263
|
}, [api, dateRange, teamUsageCache, teamUsageLoading]);
|
|
4229
|
-
const allowedModels = (0,
|
|
4264
|
+
const allowedModels = (0, import_react13.useMemo)(() => {
|
|
4230
4265
|
if (!allModels?.length) return [];
|
|
4231
4266
|
const userModels = userInfo?.models;
|
|
4232
4267
|
const teamModels = teams?.flatMap((t) => t.models ?? []);
|
|
@@ -4239,7 +4274,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4239
4274
|
]);
|
|
4240
4275
|
return allModels.filter((m) => allowed.has(m.model_name));
|
|
4241
4276
|
}, [allModels, userInfo, teams]);
|
|
4242
|
-
const handleGenerateKey = (0,
|
|
4277
|
+
const handleGenerateKey = (0, import_react13.useCallback)(
|
|
4243
4278
|
async (request) => {
|
|
4244
4279
|
try {
|
|
4245
4280
|
const response = await api.generateKey(request);
|
|
@@ -4253,7 +4288,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4253
4288
|
},
|
|
4254
4289
|
[api, refreshKeys]
|
|
4255
4290
|
);
|
|
4256
|
-
const handleUpdateKey = (0,
|
|
4291
|
+
const handleUpdateKey = (0, import_react13.useCallback)(
|
|
4257
4292
|
async (keyId, request) => {
|
|
4258
4293
|
try {
|
|
4259
4294
|
await api.updateKey(keyId, request);
|
|
@@ -4266,7 +4301,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4266
4301
|
},
|
|
4267
4302
|
[api, refreshKeys]
|
|
4268
4303
|
);
|
|
4269
|
-
const handleBlockKey = (0,
|
|
4304
|
+
const handleBlockKey = (0, import_react13.useCallback)(
|
|
4270
4305
|
async (keyId) => {
|
|
4271
4306
|
try {
|
|
4272
4307
|
await api.blockKey(keyId);
|
|
@@ -4278,7 +4313,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4278
4313
|
},
|
|
4279
4314
|
[api, refreshKeys]
|
|
4280
4315
|
);
|
|
4281
|
-
const handleUnblockKey = (0,
|
|
4316
|
+
const handleUnblockKey = (0, import_react13.useCallback)(
|
|
4282
4317
|
async (keyId) => {
|
|
4283
4318
|
try {
|
|
4284
4319
|
await api.unblockKey(keyId);
|
|
@@ -4290,7 +4325,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4290
4325
|
},
|
|
4291
4326
|
[api, refreshKeys]
|
|
4292
4327
|
);
|
|
4293
|
-
const handleResetKeySpend = (0,
|
|
4328
|
+
const handleResetKeySpend = (0, import_react13.useCallback)(
|
|
4294
4329
|
async (keyId) => {
|
|
4295
4330
|
try {
|
|
4296
4331
|
await api.resetKeySpend(keyId);
|
|
@@ -4302,7 +4337,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4302
4337
|
},
|
|
4303
4338
|
[api, refreshKeys]
|
|
4304
4339
|
);
|
|
4305
|
-
const handleDeleteKey = (0,
|
|
4340
|
+
const handleDeleteKey = (0, import_react13.useCallback)(
|
|
4306
4341
|
async (keyId) => {
|
|
4307
4342
|
try {
|
|
4308
4343
|
await api.deleteKey(keyId);
|
|
@@ -4319,7 +4354,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4319
4354
|
},
|
|
4320
4355
|
[api, refreshKeys]
|
|
4321
4356
|
);
|
|
4322
|
-
const handlePruneExpiredKeys = (0,
|
|
4357
|
+
const handlePruneExpiredKeys = (0, import_react13.useCallback)(
|
|
4323
4358
|
async () => {
|
|
4324
4359
|
try {
|
|
4325
4360
|
const result = await api.pruneExpiredKeys();
|
|
@@ -4334,14 +4369,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4334
4369
|
);
|
|
4335
4370
|
const isInitialLoading = userLoading && !userInfo;
|
|
4336
4371
|
if (isInitialLoading) {
|
|
4337
|
-
return /* @__PURE__ */
|
|
4372
|
+
return /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react13.default.createElement(import_CircularProgress6.default, null));
|
|
4338
4373
|
}
|
|
4339
4374
|
if (userError || !userInfo) {
|
|
4340
4375
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
4341
4376
|
const hint = userError?.body?.hint;
|
|
4342
|
-
return /* @__PURE__ */
|
|
4377
|
+
return /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { p: 3 }, /* @__PURE__ */ import_react13.default.createElement(import_Paper8.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react13.default.createElement(import_Typography12.default, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
|
|
4343
4378
|
}
|
|
4344
|
-
const pageTabs = /* @__PURE__ */
|
|
4379
|
+
const pageTabs = /* @__PURE__ */ import_react13.default.createElement(
|
|
4345
4380
|
import_Tabs2.default,
|
|
4346
4381
|
{
|
|
4347
4382
|
value: activeTab,
|
|
@@ -4354,13 +4389,13 @@ var init_LiteLLMPage = __esm({
|
|
|
4354
4389
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
4355
4390
|
}
|
|
4356
4391
|
},
|
|
4357
|
-
/* @__PURE__ */
|
|
4358
|
-
/* @__PURE__ */
|
|
4359
|
-
/* @__PURE__ */
|
|
4360
|
-
/* @__PURE__ */
|
|
4361
|
-
userInfo.can_view_audit && /* @__PURE__ */
|
|
4392
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
|
|
4393
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
|
|
4394
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
|
|
4395
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
|
|
4396
|
+
userInfo.can_view_audit && /* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
|
|
4362
4397
|
);
|
|
4363
|
-
return /* @__PURE__ */
|
|
4398
|
+
return /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react13.default.createElement(
|
|
4364
4399
|
DashboardHeader,
|
|
4365
4400
|
{
|
|
4366
4401
|
userInfo,
|
|
@@ -4370,7 +4405,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4370
4405
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4371
4406
|
tabs: pageTabs
|
|
4372
4407
|
}
|
|
4373
|
-
), activeTab === "overview" && /* @__PURE__ */
|
|
4408
|
+
), activeTab === "overview" && /* @__PURE__ */ import_react13.default.createElement(import_Grid2.default, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ import_react13.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ import_react13.default.createElement(
|
|
4374
4409
|
UsageStats,
|
|
4375
4410
|
{
|
|
4376
4411
|
usage: usage ?? null,
|
|
@@ -4381,7 +4416,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4381
4416
|
loading: usageLoading,
|
|
4382
4417
|
userInfo
|
|
4383
4418
|
}
|
|
4384
|
-
)), /* @__PURE__ */
|
|
4419
|
+
)), /* @__PURE__ */ import_react13.default.createElement(import_Grid2.default, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ import_react13.default.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ import_react13.default.createElement(
|
|
4385
4420
|
KeysTable,
|
|
4386
4421
|
{
|
|
4387
4422
|
keys: keys ?? [],
|
|
@@ -4398,7 +4433,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4398
4433
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
4399
4434
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
4400
4435
|
const visibleTeams = Array.from(teamsById.values());
|
|
4401
|
-
return /* @__PURE__ */
|
|
4436
|
+
return /* @__PURE__ */ import_react13.default.createElement(
|
|
4402
4437
|
TeamUsage,
|
|
4403
4438
|
{
|
|
4404
4439
|
teams: visibleTeams,
|
|
@@ -4414,14 +4449,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4414
4449
|
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
4415
4450
|
}
|
|
4416
4451
|
);
|
|
4417
|
-
})(), activeTab === "models" && /* @__PURE__ */
|
|
4452
|
+
})(), activeTab === "models" && /* @__PURE__ */ import_react13.default.createElement(
|
|
4418
4453
|
ModelsTable,
|
|
4419
4454
|
{
|
|
4420
4455
|
allModels: allModels ?? [],
|
|
4421
4456
|
teams: teams ?? [],
|
|
4422
4457
|
loading: modelsLoading
|
|
4423
4458
|
}
|
|
4424
|
-
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */
|
|
4459
|
+
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react13.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react13.default.createElement(
|
|
4425
4460
|
KeyFormDialog,
|
|
4426
4461
|
{
|
|
4427
4462
|
open: keyFormOpen,
|
|
@@ -4441,7 +4476,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4441
4476
|
onResetKeySpend: handleResetKeySpend,
|
|
4442
4477
|
onGetConfig: () => api.getConfig()
|
|
4443
4478
|
}
|
|
4444
|
-
), /* @__PURE__ */
|
|
4479
|
+
), /* @__PURE__ */ import_react13.default.createElement(
|
|
4445
4480
|
ManageTeamDialog,
|
|
4446
4481
|
{
|
|
4447
4482
|
open: !!manageTeam,
|
|
@@ -4503,7 +4538,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4503
4538
|
refreshTeams();
|
|
4504
4539
|
}
|
|
4505
4540
|
}
|
|
4506
|
-
), /* @__PURE__ */
|
|
4541
|
+
), /* @__PURE__ */ import_react13.default.createElement(
|
|
4507
4542
|
import_Snackbar2.default,
|
|
4508
4543
|
{
|
|
4509
4544
|
open: !!snackbar,
|
|
@@ -4511,7 +4546,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4511
4546
|
onClose: () => setSnackbar(null),
|
|
4512
4547
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
4513
4548
|
},
|
|
4514
|
-
snackbar ? /* @__PURE__ */
|
|
4549
|
+
snackbar ? /* @__PURE__ */ import_react13.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
4515
4550
|
));
|
|
4516
4551
|
};
|
|
4517
4552
|
}
|
|
@@ -4521,6 +4556,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4521
4556
|
var index_exports = {};
|
|
4522
4557
|
__export(index_exports, {
|
|
4523
4558
|
DashboardHeader: () => DashboardHeader,
|
|
4559
|
+
GenerateKeyButton: () => GenerateKeyButton,
|
|
4524
4560
|
GenerateKeyDialog: () => GenerateKeyDialog,
|
|
4525
4561
|
KeyFormDialog: () => KeyFormDialog,
|
|
4526
4562
|
KeysTable: () => KeysTable,
|
|
@@ -4543,7 +4579,7 @@ __export(index_exports, {
|
|
|
4543
4579
|
module.exports = __toCommonJS(index_exports);
|
|
4544
4580
|
|
|
4545
4581
|
// src/plugin.tsx
|
|
4546
|
-
var
|
|
4582
|
+
var import_react14 = __toESM(require("react"));
|
|
4547
4583
|
var import_icons_material9 = require("@mui/icons-material");
|
|
4548
4584
|
var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
|
|
4549
4585
|
init_api();
|
|
@@ -4558,10 +4594,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
|
|
|
4558
4594
|
params: {
|
|
4559
4595
|
path: "/litellm",
|
|
4560
4596
|
title: "LiteLLM",
|
|
4561
|
-
icon: /* @__PURE__ */
|
|
4597
|
+
icon: /* @__PURE__ */ import_react14.default.createElement(import_icons_material9.TrendingUp, null),
|
|
4562
4598
|
loader: async () => {
|
|
4563
4599
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
4564
|
-
return /* @__PURE__ */
|
|
4600
|
+
return /* @__PURE__ */ import_react14.default.createElement(LiteLLMPage2, null);
|
|
4565
4601
|
}
|
|
4566
4602
|
}
|
|
4567
4603
|
});
|
|
@@ -4578,7 +4614,7 @@ init_UsageStats();
|
|
|
4578
4614
|
init_TeamUsage();
|
|
4579
4615
|
|
|
4580
4616
|
// src/components/LiteLLMHomeWidget.tsx
|
|
4581
|
-
var
|
|
4617
|
+
var import_react15 = __toESM(require("react"));
|
|
4582
4618
|
var import_Paper9 = __toESM(require("@mui/material/Paper"));
|
|
4583
4619
|
var import_Box13 = __toESM(require("@mui/material/Box"));
|
|
4584
4620
|
var import_Typography13 = __toESM(require("@mui/material/Typography"));
|
|
@@ -4604,19 +4640,19 @@ function presetToDateRange(preset) {
|
|
|
4604
4640
|
}
|
|
4605
4641
|
return { start, end };
|
|
4606
4642
|
}
|
|
4607
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
4643
|
+
var Kpi = ({ label, value }) => /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, null, /* @__PURE__ */ import_react15.default.createElement(import_Typography13.default, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ import_react15.default.createElement(import_Typography13.default, { variant: "subtitle1", fontWeight: 600 }, value));
|
|
4608
4644
|
var LiteLLMHomeWidget = ({
|
|
4609
4645
|
defaultPeriod = "7d",
|
|
4610
4646
|
title = "LiteLLM Usage"
|
|
4611
4647
|
}) => {
|
|
4612
4648
|
const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
|
|
4613
|
-
const [period, setPeriod] = (0,
|
|
4614
|
-
const [loading, setLoading] = (0,
|
|
4615
|
-
const [usageError, setUsageError] = (0,
|
|
4616
|
-
const [keysError, setKeysError] = (0,
|
|
4617
|
-
const [usage, setUsage] = (0,
|
|
4618
|
-
const [keys, setKeys] = (0,
|
|
4619
|
-
(0,
|
|
4649
|
+
const [period, setPeriod] = (0, import_react15.useState)(defaultPeriod);
|
|
4650
|
+
const [loading, setLoading] = (0, import_react15.useState)(true);
|
|
4651
|
+
const [usageError, setUsageError] = (0, import_react15.useState)(null);
|
|
4652
|
+
const [keysError, setKeysError] = (0, import_react15.useState)(null);
|
|
4653
|
+
const [usage, setUsage] = (0, import_react15.useState)(null);
|
|
4654
|
+
const [keys, setKeys] = (0, import_react15.useState)([]);
|
|
4655
|
+
(0, import_react15.useEffect)(() => {
|
|
4620
4656
|
let cancelled = false;
|
|
4621
4657
|
setLoading(true);
|
|
4622
4658
|
setUsageError(null);
|
|
@@ -4653,17 +4689,17 @@ var LiteLLMHomeWidget = ({
|
|
|
4653
4689
|
spend: d.spend
|
|
4654
4690
|
}));
|
|
4655
4691
|
const hasSparkline = dailyData.length > 0;
|
|
4656
|
-
return /* @__PURE__ */
|
|
4692
|
+
return /* @__PURE__ */ import_react15.default.createElement(import_Paper9.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react15.default.createElement(import_Typography13.default, { variant: "h6" }, title), /* @__PURE__ */ import_react15.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react15.default.createElement(
|
|
4657
4693
|
import_Select2.default,
|
|
4658
4694
|
{
|
|
4659
4695
|
value: period,
|
|
4660
4696
|
onChange: (e) => setPeriod(e.target.value),
|
|
4661
4697
|
displayEmpty: true
|
|
4662
4698
|
},
|
|
4663
|
-
/* @__PURE__ */
|
|
4664
|
-
/* @__PURE__ */
|
|
4665
|
-
/* @__PURE__ */
|
|
4666
|
-
))), loading && /* @__PURE__ */
|
|
4699
|
+
/* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
|
|
4700
|
+
/* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
|
|
4701
|
+
/* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
|
|
4702
|
+
))), loading && /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react15.default.createElement(import_CircularProgress7.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react15.default.createElement(import_Alert7.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react15.default.createElement(import_Alert7.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react15.default.createElement(import_Grid3.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react15.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react15.default.createElement(import_Box13.default, { height: 120 }, /* @__PURE__ */ import_react15.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react15.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react15.default.createElement(
|
|
4667
4703
|
import_recharts3.Area,
|
|
4668
4704
|
{
|
|
4669
4705
|
type: "monotone",
|
|
@@ -4681,7 +4717,7 @@ var LiteLLMHomeWidget = ({
|
|
|
4681
4717
|
init_LiteLLMBudgetWidget();
|
|
4682
4718
|
|
|
4683
4719
|
// src/components/LiteLLMBudgetGauges.tsx
|
|
4684
|
-
var
|
|
4720
|
+
var import_react16 = __toESM(require("react"));
|
|
4685
4721
|
var import_Paper10 = __toESM(require("@mui/material/Paper"));
|
|
4686
4722
|
var import_Box14 = __toESM(require("@mui/material/Box"));
|
|
4687
4723
|
var import_Button6 = __toESM(require("@mui/material/Button"));
|
|
@@ -4690,11 +4726,13 @@ var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress")
|
|
|
4690
4726
|
var import_Alert8 = __toESM(require("@mui/material/Alert"));
|
|
4691
4727
|
var import_Collapse4 = __toESM(require("@mui/material/Collapse"));
|
|
4692
4728
|
var import_icons_material10 = require("@mui/icons-material");
|
|
4729
|
+
var import_recharts4 = require("recharts");
|
|
4693
4730
|
var import_core_plugin_api6 = require("@backstage/core-plugin-api");
|
|
4694
|
-
var
|
|
4731
|
+
var import_core_components3 = require("@backstage/core-components");
|
|
4695
4732
|
init_api();
|
|
4696
4733
|
init_format();
|
|
4697
4734
|
init_ui();
|
|
4735
|
+
init_GenerateKeyButton();
|
|
4698
4736
|
init_BudgetLimitList();
|
|
4699
4737
|
init_budget();
|
|
4700
4738
|
var MODULE_PATH = "/litellm";
|
|
@@ -4705,7 +4743,7 @@ var LEVEL = {
|
|
|
4705
4743
|
};
|
|
4706
4744
|
var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
|
|
4707
4745
|
var CTA_LABELS = {
|
|
4708
|
-
"new-key": "New
|
|
4746
|
+
"new-key": "Generate New Key",
|
|
4709
4747
|
module: "Open module",
|
|
4710
4748
|
"all-limits": "All limits"
|
|
4711
4749
|
};
|
|
@@ -4731,7 +4769,7 @@ var LevelGauge = ({
|
|
|
4731
4769
|
const limit = gauge.limit;
|
|
4732
4770
|
const tone = limit ? budgetTone2(limit.pct) : "neutral";
|
|
4733
4771
|
const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
|
|
4734
|
-
return /* @__PURE__ */
|
|
4772
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4735
4773
|
import_Box14.default,
|
|
4736
4774
|
{
|
|
4737
4775
|
sx: {
|
|
@@ -4742,7 +4780,7 @@ var LevelGauge = ({
|
|
|
4742
4780
|
flex: 1
|
|
4743
4781
|
}
|
|
4744
4782
|
},
|
|
4745
|
-
/* @__PURE__ */
|
|
4783
|
+
/* @__PURE__ */ import_react16.default.createElement(
|
|
4746
4784
|
Gauge,
|
|
4747
4785
|
{
|
|
4748
4786
|
value: limit?.pct ?? 0,
|
|
@@ -4751,7 +4789,7 @@ var LevelGauge = ({
|
|
|
4751
4789
|
label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
|
|
4752
4790
|
}
|
|
4753
4791
|
),
|
|
4754
|
-
/* @__PURE__ */
|
|
4792
|
+
/* @__PURE__ */ import_react16.default.createElement(
|
|
4755
4793
|
import_Typography14.default,
|
|
4756
4794
|
{
|
|
4757
4795
|
sx: (theme) => ({
|
|
@@ -4765,7 +4803,7 @@ var LevelGauge = ({
|
|
|
4765
4803
|
},
|
|
4766
4804
|
name
|
|
4767
4805
|
),
|
|
4768
|
-
limit ? /* @__PURE__ */
|
|
4806
|
+
limit ? /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(
|
|
4769
4807
|
import_Typography14.default,
|
|
4770
4808
|
{
|
|
4771
4809
|
variant: "caption",
|
|
@@ -4781,7 +4819,7 @@ var LevelGauge = ({
|
|
|
4781
4819
|
}
|
|
4782
4820
|
},
|
|
4783
4821
|
limit.label
|
|
4784
|
-
), /* @__PURE__ */
|
|
4822
|
+
), /* @__PURE__ */ import_react16.default.createElement(
|
|
4785
4823
|
import_Typography14.default,
|
|
4786
4824
|
{
|
|
4787
4825
|
variant: "caption",
|
|
@@ -4793,10 +4831,10 @@ var LevelGauge = ({
|
|
|
4793
4831
|
}
|
|
4794
4832
|
},
|
|
4795
4833
|
spendCaption(limit)
|
|
4796
|
-
), extra > 0 ? /* @__PURE__ */
|
|
4834
|
+
), extra > 0 ? /* @__PURE__ */ import_react16.default.createElement(
|
|
4797
4835
|
import_Typography14.default,
|
|
4798
4836
|
{
|
|
4799
|
-
component:
|
|
4837
|
+
component: import_core_components3.Link,
|
|
4800
4838
|
to: keysHref,
|
|
4801
4839
|
variant: "caption",
|
|
4802
4840
|
sx: {
|
|
@@ -4809,7 +4847,7 @@ var LevelGauge = ({
|
|
|
4809
4847
|
"+",
|
|
4810
4848
|
extra,
|
|
4811
4849
|
" more"
|
|
4812
|
-
) : /* @__PURE__ */
|
|
4850
|
+
) : /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ import_react16.default.createElement(
|
|
4813
4851
|
import_Typography14.default,
|
|
4814
4852
|
{
|
|
4815
4853
|
variant: "caption",
|
|
@@ -4820,6 +4858,125 @@ var LevelGauge = ({
|
|
|
4820
4858
|
)
|
|
4821
4859
|
);
|
|
4822
4860
|
};
|
|
4861
|
+
function monthToDateRange(now = /* @__PURE__ */ new Date()) {
|
|
4862
|
+
const toDay = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
|
|
4863
|
+
d.getDate()
|
|
4864
|
+
).padStart(2, "0")}`;
|
|
4865
|
+
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
4866
|
+
return { startDate: toDay(start), endDate: toDay(now) };
|
|
4867
|
+
}
|
|
4868
|
+
var UsageMiniChart = ({ data, totalTokens, loading, height }) => {
|
|
4869
|
+
const renderPlot = () => {
|
|
4870
|
+
if (loading) {
|
|
4871
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%" }, /* @__PURE__ */ import_react16.default.createElement(import_CircularProgress8.default, { size: 20 }));
|
|
4872
|
+
}
|
|
4873
|
+
if (data.length === 0) {
|
|
4874
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4875
|
+
import_Box14.default,
|
|
4876
|
+
{
|
|
4877
|
+
display: "flex",
|
|
4878
|
+
justifyContent: "center",
|
|
4879
|
+
alignItems: "center",
|
|
4880
|
+
height: "100%",
|
|
4881
|
+
sx: (theme) => ({ color: theme.palette.text.secondary, fontSize: 18 })
|
|
4882
|
+
},
|
|
4883
|
+
"\u2014"
|
|
4884
|
+
);
|
|
4885
|
+
}
|
|
4886
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_recharts4.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react16.default.createElement(import_recharts4.AreaChart, { data, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react16.default.createElement(import_recharts4.Tooltip, { content: /* @__PURE__ */ import_react16.default.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ import_react16.default.createElement(
|
|
4887
|
+
import_recharts4.Area,
|
|
4888
|
+
{
|
|
4889
|
+
type: "monotone",
|
|
4890
|
+
dataKey: "input",
|
|
4891
|
+
name: "Input",
|
|
4892
|
+
stackId: "tok",
|
|
4893
|
+
stroke: SERIES.input,
|
|
4894
|
+
strokeWidth: 1.5,
|
|
4895
|
+
fill: SERIES.input,
|
|
4896
|
+
fillOpacity: 0.25,
|
|
4897
|
+
dot: false,
|
|
4898
|
+
isAnimationActive: false
|
|
4899
|
+
}
|
|
4900
|
+
), /* @__PURE__ */ import_react16.default.createElement(
|
|
4901
|
+
import_recharts4.Area,
|
|
4902
|
+
{
|
|
4903
|
+
type: "monotone",
|
|
4904
|
+
dataKey: "output",
|
|
4905
|
+
name: "Output",
|
|
4906
|
+
stackId: "tok",
|
|
4907
|
+
stroke: SERIES.output,
|
|
4908
|
+
strokeWidth: 1.5,
|
|
4909
|
+
fill: SERIES.output,
|
|
4910
|
+
fillOpacity: 0.25,
|
|
4911
|
+
dot: false,
|
|
4912
|
+
isAnimationActive: false
|
|
4913
|
+
}
|
|
4914
|
+
)));
|
|
4915
|
+
};
|
|
4916
|
+
const renderCaption = () => {
|
|
4917
|
+
if (loading) {
|
|
4918
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { mt: 0.25, fontSize: 11 } }, "\u2026");
|
|
4919
|
+
}
|
|
4920
|
+
if (data.length === 0) {
|
|
4921
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4922
|
+
import_Typography14.default,
|
|
4923
|
+
{
|
|
4924
|
+
variant: "caption",
|
|
4925
|
+
color: "text.secondary",
|
|
4926
|
+
sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
|
|
4927
|
+
},
|
|
4928
|
+
"No usage this month"
|
|
4929
|
+
);
|
|
4930
|
+
}
|
|
4931
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(
|
|
4932
|
+
import_Typography14.default,
|
|
4933
|
+
{
|
|
4934
|
+
variant: "caption",
|
|
4935
|
+
title: `${fmtInt(totalTokens)} tokens month to date`,
|
|
4936
|
+
sx: {
|
|
4937
|
+
mt: 0.25,
|
|
4938
|
+
fontSize: 11,
|
|
4939
|
+
fontWeight: 600,
|
|
4940
|
+
maxWidth: "100%",
|
|
4941
|
+
overflow: "hidden",
|
|
4942
|
+
textOverflow: "ellipsis",
|
|
4943
|
+
whiteSpace: "nowrap",
|
|
4944
|
+
fontVariantNumeric: "tabular-nums"
|
|
4945
|
+
}
|
|
4946
|
+
},
|
|
4947
|
+
fmtCompact(totalTokens),
|
|
4948
|
+
" tokens"
|
|
4949
|
+
), /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, "month to date"));
|
|
4950
|
+
};
|
|
4951
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4952
|
+
import_Box14.default,
|
|
4953
|
+
{
|
|
4954
|
+
sx: {
|
|
4955
|
+
display: "flex",
|
|
4956
|
+
flexDirection: "column",
|
|
4957
|
+
alignItems: "center",
|
|
4958
|
+
minWidth: 0,
|
|
4959
|
+
flex: 1
|
|
4960
|
+
}
|
|
4961
|
+
},
|
|
4962
|
+
/* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { sx: { height, width: "100%" } }, renderPlot()),
|
|
4963
|
+
/* @__PURE__ */ import_react16.default.createElement(
|
|
4964
|
+
import_Typography14.default,
|
|
4965
|
+
{
|
|
4966
|
+
sx: (theme) => ({
|
|
4967
|
+
mt: 0.75,
|
|
4968
|
+
fontSize: 10.5,
|
|
4969
|
+
fontWeight: 700,
|
|
4970
|
+
letterSpacing: "0.08em",
|
|
4971
|
+
textTransform: "uppercase",
|
|
4972
|
+
color: theme.palette.text.secondary
|
|
4973
|
+
})
|
|
4974
|
+
},
|
|
4975
|
+
"Usage"
|
|
4976
|
+
),
|
|
4977
|
+
renderCaption()
|
|
4978
|
+
);
|
|
4979
|
+
};
|
|
4823
4980
|
var LiteLLMBudgetGauges = ({
|
|
4824
4981
|
title = "Budget",
|
|
4825
4982
|
size = 72,
|
|
@@ -4834,60 +4991,83 @@ var LiteLLMBudgetGauges = ({
|
|
|
4834
4991
|
action
|
|
4835
4992
|
}) => {
|
|
4836
4993
|
const api = (0, import_core_plugin_api6.useApi)(liteLlmApiRef);
|
|
4837
|
-
const [loading, setLoading] = (0,
|
|
4838
|
-
const [error, setError] = (0,
|
|
4839
|
-
const [user, setUser] = (0,
|
|
4840
|
-
const [teams, setTeams] = (0,
|
|
4841
|
-
const [keys, setKeys] = (0,
|
|
4994
|
+
const [loading, setLoading] = (0, import_react16.useState)(true);
|
|
4995
|
+
const [error, setError] = (0, import_react16.useState)(null);
|
|
4996
|
+
const [user, setUser] = (0, import_react16.useState)(null);
|
|
4997
|
+
const [teams, setTeams] = (0, import_react16.useState)([]);
|
|
4998
|
+
const [keys, setKeys] = (0, import_react16.useState)([]);
|
|
4999
|
+
const [usageLoading, setUsageLoading] = (0, import_react16.useState)(true);
|
|
5000
|
+
const [dailyTokens, setDailyTokens] = (0, import_react16.useState)([]);
|
|
5001
|
+
const [mtdTokens, setMtdTokens] = (0, import_react16.useState)(0);
|
|
4842
5002
|
const isControlled = expanded !== void 0;
|
|
4843
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
5003
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react16.useState)(defaultExpanded);
|
|
4844
5004
|
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
4845
|
-
const toggleExpanded = (0,
|
|
5005
|
+
const toggleExpanded = (0, import_react16.useCallback)(() => {
|
|
4846
5006
|
const next = !isExpanded;
|
|
4847
5007
|
if (!isControlled) setUncontrolledExpanded(next);
|
|
4848
5008
|
onExpandedChange?.(next);
|
|
4849
5009
|
}, [isControlled, isExpanded, onExpandedChange]);
|
|
4850
5010
|
const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
|
|
4851
|
-
(0,
|
|
5011
|
+
(0, import_react16.useEffect)(() => {
|
|
4852
5012
|
let cancelled = false;
|
|
4853
5013
|
setLoading(true);
|
|
5014
|
+
setUsageLoading(true);
|
|
4854
5015
|
setError(null);
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
5016
|
+
const { startDate, endDate } = monthToDateRange();
|
|
5017
|
+
Promise.allSettled([
|
|
5018
|
+
api.getUserInfo(),
|
|
5019
|
+
api.getTeams(),
|
|
5020
|
+
api.listKeys(),
|
|
5021
|
+
api.getUsage(startDate, endDate)
|
|
5022
|
+
]).then(([userResult, teamsResult, keysResult, usageResult]) => {
|
|
5023
|
+
if (cancelled) return;
|
|
5024
|
+
if (userResult.status === "fulfilled") {
|
|
5025
|
+
setUser(userResult.value);
|
|
5026
|
+
} else {
|
|
5027
|
+
setUser(null);
|
|
5028
|
+
setError(
|
|
5029
|
+
userResult.reason?.message ?? "Failed to load your LiteLLM profile"
|
|
5030
|
+
);
|
|
4869
5031
|
}
|
|
4870
|
-
|
|
5032
|
+
setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
|
|
5033
|
+
setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
|
|
5034
|
+
setLoading(false);
|
|
5035
|
+
if (usageResult.status === "fulfilled") {
|
|
5036
|
+
const daily = usageResult.value.daily_usage ?? [];
|
|
5037
|
+
setDailyTokens(
|
|
5038
|
+
daily.map((d) => ({
|
|
5039
|
+
date: d.date,
|
|
5040
|
+
input: d.prompt_tokens,
|
|
5041
|
+
output: d.completion_tokens
|
|
5042
|
+
}))
|
|
5043
|
+
);
|
|
5044
|
+
setMtdTokens(usageResult.value.total_tokens ?? 0);
|
|
5045
|
+
} else {
|
|
5046
|
+
setDailyTokens([]);
|
|
5047
|
+
setMtdTokens(0);
|
|
5048
|
+
}
|
|
5049
|
+
setUsageLoading(false);
|
|
5050
|
+
});
|
|
4871
5051
|
return () => {
|
|
4872
5052
|
cancelled = true;
|
|
4873
5053
|
};
|
|
4874
5054
|
}, [api]);
|
|
4875
|
-
const summary = (0,
|
|
5055
|
+
const summary = (0, import_react16.useMemo)(
|
|
4876
5056
|
() => buildBudgetSummary(user, teams, keys, keys.length),
|
|
4877
5057
|
[user, teams, keys]
|
|
4878
5058
|
);
|
|
4879
|
-
const gauges = (0,
|
|
4880
|
-
const headline = (0,
|
|
4881
|
-
const expandedSummary = (0,
|
|
5059
|
+
const gauges = (0, import_react16.useMemo)(() => buildBudgetGauges(summary), [summary]);
|
|
5060
|
+
const headline = (0, import_react16.useMemo)(() => budgetHeadline(summary), [summary]);
|
|
5061
|
+
const expandedSummary = (0, import_react16.useMemo)(
|
|
4882
5062
|
() => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
|
|
4883
5063
|
[user, teams, keys, maxExpandedKeys]
|
|
4884
5064
|
);
|
|
4885
|
-
const expandedLimits = (0,
|
|
5065
|
+
const expandedLimits = (0, import_react16.useMemo)(
|
|
4886
5066
|
() => allBudgetLimits(expandedSummary),
|
|
4887
5067
|
[expandedSummary]
|
|
4888
5068
|
);
|
|
4889
5069
|
const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
4890
|
-
const resolvedCtas = (0,
|
|
5070
|
+
const resolvedCtas = (0, import_react16.useMemo)(() => {
|
|
4891
5071
|
const list = ctas ?? DEFAULT_CTAS;
|
|
4892
5072
|
return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
|
|
4893
5073
|
}, [ctas, headline.count]);
|
|
@@ -4896,28 +5076,18 @@ var LiteLLMBudgetGauges = ({
|
|
|
4896
5076
|
const key = `${cta.kind}-${index}`;
|
|
4897
5077
|
switch (cta.kind) {
|
|
4898
5078
|
case "new-key":
|
|
4899
|
-
return onCreateKey ? /* @__PURE__ */
|
|
4900
|
-
import_Button6.default,
|
|
4901
|
-
{
|
|
4902
|
-
key,
|
|
4903
|
-
size: "small",
|
|
4904
|
-
variant: "contained",
|
|
4905
|
-
component: import_core_components2.Link,
|
|
4906
|
-
to: `${moduleHref}?generate=1`
|
|
4907
|
-
},
|
|
4908
|
-
label
|
|
4909
|
-
);
|
|
5079
|
+
return onCreateKey ? /* @__PURE__ */ import_react16.default.createElement(GenerateKeyButton, { key, size: "small", label, onClick: onCreateKey }) : /* @__PURE__ */ import_react16.default.createElement(GenerateKeyButton, { key, size: "small", label, to: `${moduleHref}?generate=1` });
|
|
4910
5080
|
case "module":
|
|
4911
|
-
return /* @__PURE__ */
|
|
5081
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_Button6.default, { key, size: "small", variant: "outlined", component: import_core_components3.Link, to: moduleHref }, label);
|
|
4912
5082
|
case "all-limits":
|
|
4913
|
-
return /* @__PURE__ */
|
|
5083
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4914
5084
|
import_Button6.default,
|
|
4915
5085
|
{
|
|
4916
5086
|
key,
|
|
4917
5087
|
size: "small",
|
|
4918
5088
|
variant: "text",
|
|
4919
5089
|
onClick: toggleExpanded,
|
|
4920
|
-
endIcon: /* @__PURE__ */
|
|
5090
|
+
endIcon: /* @__PURE__ */ import_react16.default.createElement(
|
|
4921
5091
|
import_icons_material10.ExpandMore,
|
|
4922
5092
|
{
|
|
4923
5093
|
sx: {
|
|
@@ -4935,14 +5105,22 @@ var LiteLLMBudgetGauges = ({
|
|
|
4935
5105
|
}
|
|
4936
5106
|
};
|
|
4937
5107
|
const hasFooter = resolvedCtas.length > 0 || !!action;
|
|
4938
|
-
return /* @__PURE__ */
|
|
5108
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_Paper10.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ import_react16.default.createElement(
|
|
4939
5109
|
StatusPill,
|
|
4940
5110
|
{
|
|
4941
5111
|
label: summaryText,
|
|
4942
5112
|
tone: budgetTone2(headline.closest?.pct ?? 0),
|
|
4943
5113
|
dot: headline.count > 0
|
|
4944
5114
|
}
|
|
4945
|
-
)), loading && /* @__PURE__ */
|
|
5115
|
+
)), loading && /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ import_react16.default.createElement(import_CircularProgress8.default, { size: 28 })), !loading && error && /* @__PURE__ */ import_react16.default.createElement(import_Alert8.default, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ import_react16.default.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref: keysTabHref })), /* @__PURE__ */ import_react16.default.createElement(
|
|
5116
|
+
UsageMiniChart,
|
|
5117
|
+
{
|
|
5118
|
+
data: dailyTokens,
|
|
5119
|
+
totalTokens: mtdTokens,
|
|
5120
|
+
loading: usageLoading,
|
|
5121
|
+
height: size
|
|
5122
|
+
}
|
|
5123
|
+
)), /* @__PURE__ */ import_react16.default.createElement(import_Collapse4.default, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ import_react16.default.createElement(
|
|
4946
5124
|
import_Box14.default,
|
|
4947
5125
|
{
|
|
4948
5126
|
sx: {
|
|
@@ -4953,7 +5131,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4953
5131
|
overflowY: "auto"
|
|
4954
5132
|
}
|
|
4955
5133
|
},
|
|
4956
|
-
/* @__PURE__ */
|
|
5134
|
+
/* @__PURE__ */ import_react16.default.createElement(LimitListPanel, null, /* @__PURE__ */ import_react16.default.createElement(
|
|
4957
5135
|
BudgetLimitList,
|
|
4958
5136
|
{
|
|
4959
5137
|
limits: expandedLimits,
|
|
@@ -4961,7 +5139,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4961
5139
|
keysHref: keysTabHref
|
|
4962
5140
|
}
|
|
4963
5141
|
))
|
|
4964
|
-
))), hasFooter && /* @__PURE__ */
|
|
5142
|
+
))), hasFooter && /* @__PURE__ */ import_react16.default.createElement(
|
|
4965
5143
|
import_Box14.default,
|
|
4966
5144
|
{
|
|
4967
5145
|
sx: (theme) => ({
|
|
@@ -4981,11 +5159,12 @@ var LiteLLMBudgetGauges = ({
|
|
|
4981
5159
|
|
|
4982
5160
|
// src/index.ts
|
|
4983
5161
|
init_KeyFormDialog();
|
|
5162
|
+
init_GenerateKeyButton();
|
|
4984
5163
|
|
|
4985
5164
|
// src/components/GenerateKeyDialog.tsx
|
|
4986
|
-
var
|
|
5165
|
+
var import_react17 = __toESM(require("react"));
|
|
4987
5166
|
init_KeyFormDialog();
|
|
4988
|
-
var GenerateKeyDialog = (props) => /* @__PURE__ */
|
|
5167
|
+
var GenerateKeyDialog = (props) => /* @__PURE__ */ import_react17.default.createElement(
|
|
4989
5168
|
KeyFormDialog,
|
|
4990
5169
|
{
|
|
4991
5170
|
open: props.open,
|