@acarmisc/backstage-plugin-litellm 0.26.0 → 0.28.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 +22 -0
- package/dist/components/LiteLLMBudgetGauges.d.ts +23 -10
- package/dist/index.cjs.js +700 -516
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +566 -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,71 @@ 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
|
+
fullWidth = false,
|
|
876
|
+
disabled = false
|
|
877
|
+
}) => {
|
|
878
|
+
if (onClick && !disabled) {
|
|
879
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
880
|
+
import_Button.default,
|
|
881
|
+
{
|
|
882
|
+
variant: "contained",
|
|
883
|
+
color: "primary",
|
|
884
|
+
disableElevation: true,
|
|
885
|
+
size,
|
|
886
|
+
fullWidth,
|
|
887
|
+
startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
|
|
888
|
+
onClick
|
|
889
|
+
},
|
|
890
|
+
label
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
if (to && !disabled) {
|
|
894
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
895
|
+
import_Button.default,
|
|
896
|
+
{
|
|
897
|
+
variant: "contained",
|
|
898
|
+
color: "primary",
|
|
899
|
+
disableElevation: true,
|
|
900
|
+
size,
|
|
901
|
+
fullWidth,
|
|
902
|
+
startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
|
|
903
|
+
component: import_core_components.Link,
|
|
904
|
+
to
|
|
905
|
+
},
|
|
906
|
+
label
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
910
|
+
import_Button.default,
|
|
911
|
+
{
|
|
912
|
+
variant: "contained",
|
|
913
|
+
color: "primary",
|
|
914
|
+
disableElevation: true,
|
|
915
|
+
size,
|
|
916
|
+
fullWidth,
|
|
917
|
+
startIcon: /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Add, null),
|
|
918
|
+
disabled: true
|
|
919
|
+
},
|
|
920
|
+
label
|
|
921
|
+
);
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
|
|
861
926
|
// src/components/DashboardHeader.tsx
|
|
862
927
|
function initials(name) {
|
|
863
928
|
const local = name.split("@")[0] ?? name;
|
|
@@ -865,24 +930,23 @@ function initials(name) {
|
|
|
865
930
|
if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
866
931
|
return local.slice(0, 2).toUpperCase();
|
|
867
932
|
}
|
|
868
|
-
var
|
|
933
|
+
var import_react3, import_Box2, import_Typography2, import_Skeleton, import_Paper2, import_Divider, import_styles2, CountStat, DashboardHeader;
|
|
869
934
|
var init_DashboardHeader = __esm({
|
|
870
935
|
"src/components/DashboardHeader.tsx"() {
|
|
871
936
|
"use strict";
|
|
872
|
-
|
|
937
|
+
import_react3 = __toESM(require("react"));
|
|
873
938
|
import_Box2 = __toESM(require("@mui/material/Box"));
|
|
874
939
|
import_Typography2 = __toESM(require("@mui/material/Typography"));
|
|
875
940
|
import_Skeleton = __toESM(require("@mui/material/Skeleton"));
|
|
876
941
|
import_Paper2 = __toESM(require("@mui/material/Paper"));
|
|
877
|
-
import_Button = __toESM(require("@mui/material/Button"));
|
|
878
942
|
import_Divider = __toESM(require("@mui/material/Divider"));
|
|
879
943
|
import_styles2 = require("@mui/material/styles");
|
|
880
|
-
import_icons_material = require("@mui/icons-material");
|
|
881
944
|
init_api();
|
|
882
945
|
init_ui();
|
|
946
|
+
init_GenerateKeyButton();
|
|
883
947
|
CountStat = ({ value, label, tone }) => {
|
|
884
948
|
const active = value > 0;
|
|
885
|
-
return /* @__PURE__ */
|
|
949
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_Box2.default, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ import_react3.default.createElement(
|
|
886
950
|
import_Box2.default,
|
|
887
951
|
{
|
|
888
952
|
sx: (theme) => ({
|
|
@@ -893,14 +957,14 @@ var init_DashboardHeader = __esm({
|
|
|
893
957
|
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
958
|
})
|
|
895
959
|
}
|
|
896
|
-
), /* @__PURE__ */
|
|
960
|
+
), /* @__PURE__ */ import_react3.default.createElement(
|
|
897
961
|
import_Typography2.default,
|
|
898
962
|
{
|
|
899
963
|
component: "span",
|
|
900
964
|
sx: { fontSize: 15, fontWeight: 700, lineHeight: 1, fontVariantNumeric: "tabular-nums" }
|
|
901
965
|
},
|
|
902
966
|
value
|
|
903
|
-
), /* @__PURE__ */
|
|
967
|
+
), /* @__PURE__ */ import_react3.default.createElement(
|
|
904
968
|
import_Typography2.default,
|
|
905
969
|
{
|
|
906
970
|
component: "span",
|
|
@@ -918,13 +982,13 @@ var init_DashboardHeader = __esm({
|
|
|
918
982
|
onGenerateKeyClick,
|
|
919
983
|
tabs
|
|
920
984
|
}) => {
|
|
921
|
-
const counts = (0,
|
|
985
|
+
const counts = (0, import_react3.useMemo)(() => {
|
|
922
986
|
const expired = keys.filter((k) => expiryStatus(k.expires_at) === "expired").length;
|
|
923
987
|
const expiringSoon = keys.filter((k) => expiryStatus(k.expires_at) === "soon").length;
|
|
924
988
|
return { total: keys.length, expired, expiringSoon };
|
|
925
989
|
}, [keys]);
|
|
926
990
|
const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;
|
|
927
|
-
return /* @__PURE__ */
|
|
991
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_Paper2.default, { sx: { borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ import_react3.default.createElement(
|
|
928
992
|
import_Box2.default,
|
|
929
993
|
{
|
|
930
994
|
sx: {
|
|
@@ -937,7 +1001,7 @@ var init_DashboardHeader = __esm({
|
|
|
937
1001
|
pb: 2
|
|
938
1002
|
}
|
|
939
1003
|
},
|
|
940
|
-
/* @__PURE__ */
|
|
1004
|
+
/* @__PURE__ */ import_react3.default.createElement(
|
|
941
1005
|
import_Box2.default,
|
|
942
1006
|
{
|
|
943
1007
|
sx: (theme) => ({
|
|
@@ -957,7 +1021,7 @@ var init_DashboardHeader = __esm({
|
|
|
957
1021
|
},
|
|
958
1022
|
initials(displayName)
|
|
959
1023
|
),
|
|
960
|
-
/* @__PURE__ */
|
|
1024
|
+
/* @__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
1025
|
import_Typography2.default,
|
|
962
1026
|
{
|
|
963
1027
|
variant: "caption",
|
|
@@ -965,7 +1029,7 @@ var init_DashboardHeader = __esm({
|
|
|
965
1029
|
sx: { display: "block", fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
966
1030
|
},
|
|
967
1031
|
userInfo.user_id
|
|
968
|
-
), /* @__PURE__ */
|
|
1032
|
+
), /* @__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
1033
|
TagChip,
|
|
970
1034
|
{
|
|
971
1035
|
key: team.team_id,
|
|
@@ -973,18 +1037,8 @@ var init_DashboardHeader = __esm({
|
|
|
973
1037
|
title: team.team_id
|
|
974
1038
|
}
|
|
975
1039
|
)))),
|
|
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(
|
|
1040
|
+
/* @__PURE__ */ import_react3.default.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick })
|
|
1041
|
+
), /* @__PURE__ */ import_react3.default.createElement(import_Divider.default, null), /* @__PURE__ */ import_react3.default.createElement(
|
|
988
1042
|
import_Box2.default,
|
|
989
1043
|
{
|
|
990
1044
|
sx: {
|
|
@@ -996,12 +1050,12 @@ var init_DashboardHeader = __esm({
|
|
|
996
1050
|
py: 1.5
|
|
997
1051
|
}
|
|
998
1052
|
},
|
|
999
|
-
/* @__PURE__ */
|
|
1000
|
-
/* @__PURE__ */
|
|
1001
|
-
/* @__PURE__ */
|
|
1002
|
-
/* @__PURE__ */
|
|
1003
|
-
/* @__PURE__ */
|
|
1004
|
-
), tabs && /* @__PURE__ */
|
|
1053
|
+
/* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.total, label: "keys", tone: "accent" }),
|
|
1054
|
+
/* @__PURE__ */ import_react3.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1055
|
+
/* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.expired, label: "expired", tone: "danger" }),
|
|
1056
|
+
/* @__PURE__ */ import_react3.default.createElement(import_Divider.default, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1057
|
+
/* @__PURE__ */ import_react3.default.createElement(CountStat, { value: counts.expiringSoon, label: "expiring soon", tone: "warning" })
|
|
1058
|
+
), 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
1059
|
};
|
|
1006
1060
|
}
|
|
1007
1061
|
});
|
|
@@ -1020,12 +1074,12 @@ function expiryTone(status) {
|
|
|
1020
1074
|
function ExpiryCell({ expiresAt }) {
|
|
1021
1075
|
const status = expiryStatus(expiresAt);
|
|
1022
1076
|
if (!status) {
|
|
1023
|
-
return /* @__PURE__ */
|
|
1077
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, "Never");
|
|
1024
1078
|
}
|
|
1025
1079
|
if (status === "ok") {
|
|
1026
|
-
return /* @__PURE__ */
|
|
1080
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_Typography3.default, { variant: "body2", color: "text.secondary" }, formatDate(expiresAt));
|
|
1027
1081
|
}
|
|
1028
|
-
return /* @__PURE__ */
|
|
1082
|
+
return /* @__PURE__ */ import_react4.default.createElement(
|
|
1029
1083
|
StatusPill,
|
|
1030
1084
|
{
|
|
1031
1085
|
label: expiryChipLabel(status, expiresAt),
|
|
@@ -1040,16 +1094,16 @@ function budgetTone(pct) {
|
|
|
1040
1094
|
return "accent";
|
|
1041
1095
|
}
|
|
1042
1096
|
function keyBlockIcon(isBlocking, blocked) {
|
|
1043
|
-
if (isBlocking) return /* @__PURE__ */
|
|
1044
|
-
if (blocked) return /* @__PURE__ */
|
|
1045
|
-
return /* @__PURE__ */
|
|
1097
|
+
if (isBlocking) return /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 18 });
|
|
1098
|
+
if (blocked) return /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.LockOpen, { fontSize: "small" });
|
|
1099
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Lock, { fontSize: "small" });
|
|
1046
1100
|
}
|
|
1047
1101
|
function BudgetCell({ spend, maxBudget }) {
|
|
1048
1102
|
if (!maxBudget) {
|
|
1049
|
-
return /* @__PURE__ */
|
|
1103
|
+
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
1104
|
}
|
|
1051
1105
|
const pct = Math.min(100, spend / maxBudget * 100);
|
|
1052
|
-
return /* @__PURE__ */
|
|
1106
|
+
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
1107
|
}
|
|
1054
1108
|
function compareKeys(a, b, sortKey) {
|
|
1055
1109
|
switch (sortKey) {
|
|
@@ -1069,11 +1123,11 @@ function compareKeys(a, b, sortKey) {
|
|
|
1069
1123
|
return 0;
|
|
1070
1124
|
}
|
|
1071
1125
|
}
|
|
1072
|
-
var
|
|
1126
|
+
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
1127
|
var init_KeysTable = __esm({
|
|
1074
1128
|
"src/components/KeysTable.tsx"() {
|
|
1075
1129
|
"use strict";
|
|
1076
|
-
|
|
1130
|
+
import_react4 = __toESM(require("react"));
|
|
1077
1131
|
import_Table = __toESM(require("@mui/material/Table"));
|
|
1078
1132
|
import_TableBody = __toESM(require("@mui/material/TableBody"));
|
|
1079
1133
|
import_TableCell = __toESM(require("@mui/material/TableCell"));
|
|
@@ -1096,6 +1150,7 @@ var init_KeysTable = __esm({
|
|
|
1096
1150
|
import_styles3 = require("@mui/material/styles");
|
|
1097
1151
|
import_icons_material2 = require("@mui/icons-material");
|
|
1098
1152
|
init_api();
|
|
1153
|
+
init_GenerateKeyButton();
|
|
1099
1154
|
init_ui();
|
|
1100
1155
|
shortKeyId = (token) => {
|
|
1101
1156
|
if (!token) return "-";
|
|
@@ -1119,12 +1174,12 @@ var init_KeysTable = __esm({
|
|
|
1119
1174
|
onDeleteKey,
|
|
1120
1175
|
onPruneExpiredKeys
|
|
1121
1176
|
}) => {
|
|
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,
|
|
1177
|
+
const [blockingKeyId, setBlockingKeyId] = (0, import_react4.useState)(null);
|
|
1178
|
+
const [deleteConfirmId, setDeleteConfirmId] = (0, import_react4.useState)(null);
|
|
1179
|
+
const [deleteSubmitting, setDeleteSubmitting] = (0, import_react4.useState)(false);
|
|
1180
|
+
const [filterText, setFilterText] = (0, import_react4.useState)("");
|
|
1181
|
+
const [sortKey, setSortKey] = (0, import_react4.useState)("created");
|
|
1182
|
+
const [sortDirection, setSortDirection] = (0, import_react4.useState)("desc");
|
|
1128
1183
|
const handleSort = (nextSortKey) => {
|
|
1129
1184
|
if (sortKey === nextSortKey) {
|
|
1130
1185
|
setSortDirection((current) => current === "asc" ? "desc" : "asc");
|
|
@@ -1133,7 +1188,7 @@ var init_KeysTable = __esm({
|
|
|
1133
1188
|
setSortDirection("asc");
|
|
1134
1189
|
}
|
|
1135
1190
|
};
|
|
1136
|
-
const filteredKeys = (0,
|
|
1191
|
+
const filteredKeys = (0, import_react4.useMemo)(() => {
|
|
1137
1192
|
let result = keys;
|
|
1138
1193
|
if (filterText.trim()) {
|
|
1139
1194
|
const q = filterText.toLowerCase();
|
|
@@ -1175,9 +1230,9 @@ var init_KeysTable = __esm({
|
|
|
1175
1230
|
const copyToClipboard = (text) => {
|
|
1176
1231
|
navigator.clipboard.writeText(text);
|
|
1177
1232
|
};
|
|
1178
|
-
const [pruneConfirmCount, setPruneConfirmCount] = (0,
|
|
1179
|
-
const [pruneSubmitting, setPruneSubmitting] = (0,
|
|
1180
|
-
const expiredKeys = (0,
|
|
1233
|
+
const [pruneConfirmCount, setPruneConfirmCount] = (0, import_react4.useState)(null);
|
|
1234
|
+
const [pruneSubmitting, setPruneSubmitting] = (0, import_react4.useState)(false);
|
|
1235
|
+
const expiredKeys = (0, import_react4.useMemo)(() => {
|
|
1181
1236
|
return keys.filter((k) => expiryStatus(k.expires_at) === "expired");
|
|
1182
1237
|
}, [keys]);
|
|
1183
1238
|
const handlePruneExpired = async () => {
|
|
@@ -1194,32 +1249,27 @@ var init_KeysTable = __esm({
|
|
|
1194
1249
|
};
|
|
1195
1250
|
const renderTableBody = () => {
|
|
1196
1251
|
if (loading) {
|
|
1197
|
-
return /* @__PURE__ */
|
|
1252
|
+
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
1253
|
}
|
|
1199
1254
|
if (filteredKeys.length === 0) {
|
|
1200
|
-
return /* @__PURE__ */
|
|
1255
|
+
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
1256
|
EmptyState,
|
|
1202
1257
|
{
|
|
1203
1258
|
message: "No keys match that filter",
|
|
1204
1259
|
hint: "Try a different alias or model name."
|
|
1205
1260
|
}
|
|
1206
|
-
) : /* @__PURE__ */
|
|
1261
|
+
) : /* @__PURE__ */ import_react4.default.createElement(
|
|
1207
1262
|
EmptyState,
|
|
1208
1263
|
{
|
|
1209
1264
|
message: "No keys yet",
|
|
1210
1265
|
hint: "Generate your first key to start calling models.",
|
|
1211
|
-
action: /* @__PURE__ */
|
|
1212
|
-
|
|
1266
|
+
action: /* @__PURE__ */ import_react4.default.createElement(
|
|
1267
|
+
GenerateKeyButton,
|
|
1213
1268
|
{
|
|
1214
|
-
variant: "contained",
|
|
1215
|
-
color: "primary",
|
|
1216
1269
|
size: "small",
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
sx: { mt: 1 }
|
|
1221
|
-
},
|
|
1222
|
-
"Generate Your First Key"
|
|
1270
|
+
label: "Generate Your First Key",
|
|
1271
|
+
onClick: onGenerateKeyClick
|
|
1272
|
+
}
|
|
1223
1273
|
)
|
|
1224
1274
|
}
|
|
1225
1275
|
)));
|
|
@@ -1228,7 +1278,7 @@ var init_KeysTable = __esm({
|
|
|
1228
1278
|
const keyId = key.token ?? key.key;
|
|
1229
1279
|
const isBlocking = blockingKeyId === keyId;
|
|
1230
1280
|
const keyModels = key.models ?? [];
|
|
1231
|
-
return /* @__PURE__ */
|
|
1281
|
+
return /* @__PURE__ */ import_react4.default.createElement(
|
|
1232
1282
|
import_TableRow.default,
|
|
1233
1283
|
{
|
|
1234
1284
|
key: keyId,
|
|
@@ -1239,8 +1289,8 @@ var init_KeysTable = __esm({
|
|
|
1239
1289
|
"& td": { color: theme.palette.text.secondary }
|
|
1240
1290
|
}) : void 0
|
|
1241
1291
|
},
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */
|
|
1292
|
+
/* @__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" }))),
|
|
1293
|
+
/* @__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
1294
|
import_Typography3.default,
|
|
1245
1295
|
{
|
|
1246
1296
|
variant: "body2",
|
|
@@ -1257,7 +1307,7 @@ var init_KeysTable = __esm({
|
|
|
1257
1307
|
})
|
|
1258
1308
|
},
|
|
1259
1309
|
shortKeyId(keyId)
|
|
1260
|
-
), /* @__PURE__ */
|
|
1310
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
1261
1311
|
import_IconButton.default,
|
|
1262
1312
|
{
|
|
1263
1313
|
size: "small",
|
|
@@ -1265,13 +1315,13 @@ var init_KeysTable = __esm({
|
|
|
1265
1315
|
title: "Copy Key ID",
|
|
1266
1316
|
sx: quietIconButtonSx("accent")
|
|
1267
1317
|
},
|
|
1268
|
-
/* @__PURE__ */
|
|
1318
|
+
/* @__PURE__ */ import_react4.default.createElement(import_icons_material2.ContentCopy, { sx: { fontSize: 15 } })
|
|
1269
1319
|
))),
|
|
1270
|
-
/* @__PURE__ */
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
/* @__PURE__ */
|
|
1273
|
-
/* @__PURE__ */
|
|
1274
|
-
/* @__PURE__ */
|
|
1320
|
+
/* @__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))),
|
|
1321
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(ExpiryCell, { expiresAt: key.expires_at })),
|
|
1322
|
+
/* @__PURE__ */ import_react4.default.createElement(import_TableCell.default, null, /* @__PURE__ */ import_react4.default.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })),
|
|
1323
|
+
/* @__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")),
|
|
1324
|
+
/* @__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
1325
|
TagChip,
|
|
1276
1326
|
{
|
|
1277
1327
|
label: `+${keyModels.length - 2}`,
|
|
@@ -1279,7 +1329,7 @@ var init_KeysTable = __esm({
|
|
|
1279
1329
|
title: keyModels.slice(2).join(", ")
|
|
1280
1330
|
}
|
|
1281
1331
|
))),
|
|
1282
|
-
/* @__PURE__ */
|
|
1332
|
+
/* @__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
1333
|
import_IconButton.default,
|
|
1284
1334
|
{
|
|
1285
1335
|
size: "small",
|
|
@@ -1287,8 +1337,8 @@ var init_KeysTable = __esm({
|
|
|
1287
1337
|
title: "Edit key",
|
|
1288
1338
|
sx: quietIconButtonSx("accent")
|
|
1289
1339
|
},
|
|
1290
|
-
/* @__PURE__ */
|
|
1291
|
-
), /* @__PURE__ */
|
|
1340
|
+
/* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Edit, { fontSize: "small" })
|
|
1341
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
1292
1342
|
import_IconButton.default,
|
|
1293
1343
|
{
|
|
1294
1344
|
size: "small",
|
|
@@ -1298,7 +1348,7 @@ var init_KeysTable = __esm({
|
|
|
1298
1348
|
sx: quietIconButtonSx("warning")
|
|
1299
1349
|
},
|
|
1300
1350
|
keyBlockIcon(isBlocking, key.blocked)
|
|
1301
|
-
), /* @__PURE__ */
|
|
1351
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
1302
1352
|
import_IconButton.default,
|
|
1303
1353
|
{
|
|
1304
1354
|
size: "small",
|
|
@@ -1306,18 +1356,18 @@ var init_KeysTable = __esm({
|
|
|
1306
1356
|
title: "Revoke key",
|
|
1307
1357
|
sx: quietIconButtonSx("danger")
|
|
1308
1358
|
},
|
|
1309
|
-
/* @__PURE__ */
|
|
1359
|
+
/* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Delete, { fontSize: "small" })
|
|
1310
1360
|
)))
|
|
1311
1361
|
);
|
|
1312
1362
|
});
|
|
1313
1363
|
};
|
|
1314
|
-
return /* @__PURE__ */
|
|
1364
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
|
|
1315
1365
|
SectionCard,
|
|
1316
1366
|
{
|
|
1317
1367
|
title: "Virtual Keys",
|
|
1318
1368
|
subtitle: loading ? "Loading\u2026" : `${filteredKeys.length}${filterText ? ` of ${keys.length}` : ""} key${filteredKeys.length === 1 ? "" : "s"}`,
|
|
1319
1369
|
flush: true,
|
|
1320
|
-
actions: /* @__PURE__ */
|
|
1370
|
+
actions: /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
|
|
1321
1371
|
import_TextField.default,
|
|
1322
1372
|
{
|
|
1323
1373
|
size: "small",
|
|
@@ -1327,15 +1377,15 @@ var init_KeysTable = __esm({
|
|
|
1327
1377
|
onChange: (e) => setFilterText(e.target.value),
|
|
1328
1378
|
sx: { minWidth: 240 },
|
|
1329
1379
|
InputProps: {
|
|
1330
|
-
startAdornment: /* @__PURE__ */
|
|
1380
|
+
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
1381
|
}
|
|
1332
1382
|
}
|
|
1333
|
-
), expiredKeys.length > 0 && /* @__PURE__ */
|
|
1383
|
+
), expiredKeys.length > 0 && /* @__PURE__ */ import_react4.default.createElement(
|
|
1334
1384
|
import_Button2.default,
|
|
1335
1385
|
{
|
|
1336
1386
|
variant: "outlined",
|
|
1337
1387
|
color: "inherit",
|
|
1338
|
-
startIcon: /* @__PURE__ */
|
|
1388
|
+
startIcon: /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Autorenew, null),
|
|
1339
1389
|
onClick: () => setPruneConfirmCount(expiredKeys.length),
|
|
1340
1390
|
sx: (theme) => ({
|
|
1341
1391
|
color: theme.palette.text.secondary,
|
|
@@ -1350,20 +1400,10 @@ var init_KeysTable = __esm({
|
|
|
1350
1400
|
"Prune expired (",
|
|
1351
1401
|
expiredKeys.length,
|
|
1352
1402
|
")"
|
|
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
|
-
))
|
|
1403
|
+
), /* @__PURE__ */ import_react4.default.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick }))
|
|
1364
1404
|
},
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
), /* @__PURE__ */
|
|
1405
|
+
/* @__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())))
|
|
1406
|
+
), /* @__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
1407
|
import_Button2.default,
|
|
1368
1408
|
{
|
|
1369
1409
|
onClick: handleConfirmDelete,
|
|
@@ -1371,8 +1411,8 @@ var init_KeysTable = __esm({
|
|
|
1371
1411
|
color: "error",
|
|
1372
1412
|
disabled: deleteSubmitting
|
|
1373
1413
|
},
|
|
1374
|
-
deleteSubmitting ? /* @__PURE__ */
|
|
1375
|
-
))), /* @__PURE__ */
|
|
1414
|
+
deleteSubmitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 20 }) : "Revoke"
|
|
1415
|
+
))), /* @__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
1416
|
import_Button2.default,
|
|
1377
1417
|
{
|
|
1378
1418
|
onClick: handlePruneExpired,
|
|
@@ -1380,7 +1420,7 @@ var init_KeysTable = __esm({
|
|
|
1380
1420
|
color: "error",
|
|
1381
1421
|
disabled: pruneSubmitting
|
|
1382
1422
|
},
|
|
1383
|
-
pruneSubmitting ? /* @__PURE__ */
|
|
1423
|
+
pruneSubmitting ? /* @__PURE__ */ import_react4.default.createElement(import_CircularProgress.default, { size: 20 }) : "Prune"
|
|
1384
1424
|
))));
|
|
1385
1425
|
};
|
|
1386
1426
|
}
|
|
@@ -1518,11 +1558,11 @@ function formatContextWindow(maxInput, maxOutput) {
|
|
|
1518
1558
|
if (outPart) return `ctx ${outPart} out`;
|
|
1519
1559
|
return null;
|
|
1520
1560
|
}
|
|
1521
|
-
var
|
|
1561
|
+
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
1562
|
var init_KeyFormDialog = __esm({
|
|
1523
1563
|
"src/components/KeyFormDialog.tsx"() {
|
|
1524
1564
|
"use strict";
|
|
1525
|
-
|
|
1565
|
+
import_react5 = __toESM(require("react"));
|
|
1526
1566
|
import_Box4 = __toESM(require("@mui/material/Box"));
|
|
1527
1567
|
import_Typography4 = __toESM(require("@mui/material/Typography"));
|
|
1528
1568
|
import_Dialog2 = __toESM(require("@mui/material/Dialog"));
|
|
@@ -1574,18 +1614,18 @@ var init_KeyFormDialog = __esm({
|
|
|
1574
1614
|
"claude-code": "Requires Claude Code CLI installed"
|
|
1575
1615
|
};
|
|
1576
1616
|
SnippetTabs = ({ snippets, model, onCopy }) => {
|
|
1577
|
-
const [tab, setTab] = (0,
|
|
1617
|
+
const [tab, setTab] = (0, import_react5.useState)("curl");
|
|
1578
1618
|
const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
|
|
1579
1619
|
const code = snippets[snippetKey];
|
|
1580
1620
|
const fileHint = SNIPPET_FILE_HINTS[tab];
|
|
1581
|
-
return /* @__PURE__ */
|
|
1621
|
+
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
1622
|
import_Box4.default,
|
|
1583
1623
|
{
|
|
1584
1624
|
position: "relative",
|
|
1585
1625
|
p: 1.5,
|
|
1586
1626
|
sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
|
|
1587
1627
|
},
|
|
1588
|
-
/* @__PURE__ */
|
|
1628
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1589
1629
|
import_IconButton2.default,
|
|
1590
1630
|
{
|
|
1591
1631
|
size: "small",
|
|
@@ -1593,9 +1633,9 @@ var init_KeyFormDialog = __esm({
|
|
|
1593
1633
|
title: "Copy snippet",
|
|
1594
1634
|
sx: { position: "absolute", top: 4, right: 4 }
|
|
1595
1635
|
},
|
|
1596
|
-
/* @__PURE__ */
|
|
1636
|
+
/* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, { fontSize: "small" })
|
|
1597
1637
|
),
|
|
1598
|
-
/* @__PURE__ */
|
|
1638
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1599
1639
|
import_Typography4.default,
|
|
1600
1640
|
{
|
|
1601
1641
|
component: "pre",
|
|
@@ -1610,7 +1650,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1610
1650
|
},
|
|
1611
1651
|
code
|
|
1612
1652
|
),
|
|
1613
|
-
model && /* @__PURE__ */
|
|
1653
|
+
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
1654
|
));
|
|
1615
1655
|
};
|
|
1616
1656
|
KeyFormDialog = ({
|
|
@@ -1629,18 +1669,18 @@ var init_KeyFormDialog = __esm({
|
|
|
1629
1669
|
onGetConfig
|
|
1630
1670
|
}) => {
|
|
1631
1671
|
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,
|
|
1672
|
+
const [generateForm, setGenerateForm] = (0, import_react5.useState)(createForm(username));
|
|
1673
|
+
const [editFormState, setEditFormState] = (0, import_react5.useState)({});
|
|
1674
|
+
const [unlimitedBudget, setUnlimitedBudget] = (0, import_react5.useState)(false);
|
|
1675
|
+
const [submitting, setSubmitting] = (0, import_react5.useState)(false);
|
|
1676
|
+
const [newKeyValue, setNewKeyValue] = (0, import_react5.useState)(null);
|
|
1677
|
+
const [newKeySnippets, setNewKeySnippets] = (0, import_react5.useState)(null);
|
|
1678
|
+
const [newKeyModel, setNewKeyModel] = (0, import_react5.useState)("");
|
|
1679
|
+
const [generateError, setGenerateError] = (0, import_react5.useState)(null);
|
|
1680
|
+
const [editError, setEditError] = (0, import_react5.useState)(null);
|
|
1681
|
+
const [resetSpendConfirm, setResetSpendConfirm] = (0, import_react5.useState)(false);
|
|
1682
|
+
const [resetSpendSubmitting, setResetSpendSubmitting] = (0, import_react5.useState)(false);
|
|
1683
|
+
(0, import_react5.useEffect)(() => {
|
|
1644
1684
|
if (open) {
|
|
1645
1685
|
if (isCreate) {
|
|
1646
1686
|
setGenerateForm(createForm(username));
|
|
@@ -1677,7 +1717,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1677
1717
|
else setFormData({ key_alias: value });
|
|
1678
1718
|
};
|
|
1679
1719
|
const selectedTeam = teams.find((t) => t.team_id === (isCreate ? generateForm.team_id : void 0)) ?? null;
|
|
1680
|
-
const availableModels = (0,
|
|
1720
|
+
const availableModels = (0, import_react5.useMemo)(() => {
|
|
1681
1721
|
return models.filter((m) => isModelAllowedByTeam(m, selectedTeam?.models));
|
|
1682
1722
|
}, [models, selectedTeam]);
|
|
1683
1723
|
const selectedModels = availableModels.filter((m) => (formData.models || []).includes(m.model_name));
|
|
@@ -1686,7 +1726,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1686
1726
|
const budgetInvalid = !unlimitedBudget && (formData.max_budget === void 0 || formData.max_budget === null || formData.max_budget <= 0);
|
|
1687
1727
|
const canSubmit = !aliasError && !teamError && !budgetInvalid && !submitting;
|
|
1688
1728
|
const aliasDuplicate = !!currentAlias && keys.some((k) => k.key_alias === currentAlias && k !== keyToEdit);
|
|
1689
|
-
const budgetEstimate = (0,
|
|
1729
|
+
const budgetEstimate = (0, import_react5.useMemo)(() => {
|
|
1690
1730
|
if (unlimitedBudget || budgetInvalid) return null;
|
|
1691
1731
|
const inputCosts = selectedModels.map((m) => m.input_cost_per_token).filter((c) => typeof c === "number" && c > 0);
|
|
1692
1732
|
if (inputCosts.length === 0) return null;
|
|
@@ -1765,11 +1805,11 @@ var init_KeyFormDialog = __esm({
|
|
|
1765
1805
|
const inCost = fmtCost(m.input_cost_per_token);
|
|
1766
1806
|
const outCost = fmtCost(m.output_cost_per_token);
|
|
1767
1807
|
const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
|
|
1768
|
-
return /* @__PURE__ */
|
|
1808
|
+
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
1809
|
};
|
|
1770
1810
|
const renderTeamField = () => {
|
|
1771
1811
|
if (teams.length > 0) {
|
|
1772
|
-
return /* @__PURE__ */
|
|
1812
|
+
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1773
1813
|
import_Autocomplete.default,
|
|
1774
1814
|
{
|
|
1775
1815
|
options: teams,
|
|
@@ -1783,7 +1823,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1783
1823
|
setFormData({ team_id: team?.team_id, models: restrictedModels });
|
|
1784
1824
|
},
|
|
1785
1825
|
disabled: !isCreate,
|
|
1786
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1826
|
+
renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
|
|
1787
1827
|
import_TextField2.default,
|
|
1788
1828
|
{
|
|
1789
1829
|
...params,
|
|
@@ -1798,14 +1838,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1798
1838
|
);
|
|
1799
1839
|
}
|
|
1800
1840
|
if (teamRequired && isCreate) {
|
|
1801
|
-
return /* @__PURE__ */
|
|
1841
|
+
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
1842
|
}
|
|
1803
1843
|
return null;
|
|
1804
1844
|
};
|
|
1805
1845
|
const editKeyId = keyToEdit?.token ?? keyToEdit?.key ?? "";
|
|
1806
1846
|
const renderDangerZone = () => {
|
|
1807
1847
|
if (isCreate || !onResetKeySpend) return null;
|
|
1808
|
-
return /* @__PURE__ */
|
|
1848
|
+
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
1849
|
import_Button3.default,
|
|
1810
1850
|
{
|
|
1811
1851
|
size: "small",
|
|
@@ -1814,7 +1854,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1814
1854
|
onClick: () => setResetSpendConfirm(true)
|
|
1815
1855
|
},
|
|
1816
1856
|
"Reset Spend to $0"
|
|
1817
|
-
) : /* @__PURE__ */
|
|
1857
|
+
) : /* @__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
1858
|
import_Button3.default,
|
|
1819
1859
|
{
|
|
1820
1860
|
size: "small",
|
|
@@ -1823,8 +1863,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1823
1863
|
disabled: resetSpendSubmitting,
|
|
1824
1864
|
onClick: handleResetSpend
|
|
1825
1865
|
},
|
|
1826
|
-
resetSpendSubmitting ? /* @__PURE__ */
|
|
1827
|
-
), /* @__PURE__ */
|
|
1866
|
+
resetSpendSubmitting ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress2.default, { size: 16 }) : "Confirm"
|
|
1867
|
+
), /* @__PURE__ */ import_react5.default.createElement(import_Button3.default, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel")));
|
|
1828
1868
|
};
|
|
1829
1869
|
const maskKey = (key) => {
|
|
1830
1870
|
if (key.length <= 8) return "***";
|
|
@@ -1834,7 +1874,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1834
1874
|
if (newKeyValue) dialogTitle = "Key Generated";
|
|
1835
1875
|
else if (isCreate) dialogTitle = "Generate New Key";
|
|
1836
1876
|
const submitLabel = isCreate ? "Generate" : "Save";
|
|
1837
|
-
return /* @__PURE__ */
|
|
1877
|
+
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
1878
|
import_Box4.default,
|
|
1839
1879
|
{
|
|
1840
1880
|
display: "flex",
|
|
@@ -1849,7 +1889,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1849
1889
|
borderRadius: 1
|
|
1850
1890
|
}
|
|
1851
1891
|
},
|
|
1852
|
-
/* @__PURE__ */
|
|
1892
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1853
1893
|
import_Typography4.default,
|
|
1854
1894
|
{
|
|
1855
1895
|
component: "code",
|
|
@@ -1858,8 +1898,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1858
1898
|
},
|
|
1859
1899
|
newKeyValue
|
|
1860
1900
|
),
|
|
1861
|
-
/* @__PURE__ */
|
|
1862
|
-
), newKeySnippets && /* @__PURE__ */
|
|
1901
|
+
/* @__PURE__ */ import_react5.default.createElement(import_IconButton2.default, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ import_react5.default.createElement(import_icons_material3.ContentCopy, null))
|
|
1902
|
+
), 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
1903
|
import_Box4.default,
|
|
1864
1904
|
{
|
|
1865
1905
|
display: "flex",
|
|
@@ -1873,7 +1913,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1873
1913
|
borderRadius: 1
|
|
1874
1914
|
}
|
|
1875
1915
|
},
|
|
1876
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ import_react5.default.createElement(
|
|
1877
1917
|
import_Typography4.default,
|
|
1878
1918
|
{
|
|
1879
1919
|
component: "code",
|
|
@@ -1882,8 +1922,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1882
1922
|
},
|
|
1883
1923
|
newKeySnippets.publicEndpoint
|
|
1884
1924
|
),
|
|
1885
|
-
/* @__PURE__ */
|
|
1886
|
-
)), newKeySnippets && /* @__PURE__ */
|
|
1925
|
+
/* @__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" }))
|
|
1926
|
+
)), 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
1927
|
import_TextField2.default,
|
|
1888
1928
|
{
|
|
1889
1929
|
label: "Alias",
|
|
@@ -1899,7 +1939,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1899
1939
|
required: true,
|
|
1900
1940
|
fullWidth: true
|
|
1901
1941
|
}
|
|
1902
|
-
), !isCreate ? null : /* @__PURE__ */
|
|
1942
|
+
), !isCreate ? null : /* @__PURE__ */ import_react5.default.createElement(
|
|
1903
1943
|
import_TextField2.default,
|
|
1904
1944
|
{
|
|
1905
1945
|
select: true,
|
|
@@ -1908,12 +1948,12 @@ var init_KeyFormDialog = __esm({
|
|
|
1908
1948
|
onChange: (e) => setFormData({ duration: e.target.value }),
|
|
1909
1949
|
fullWidth: true
|
|
1910
1950
|
},
|
|
1911
|
-
/* @__PURE__ */
|
|
1912
|
-
/* @__PURE__ */
|
|
1913
|
-
/* @__PURE__ */
|
|
1914
|
-
/* @__PURE__ */
|
|
1915
|
-
/* @__PURE__ */
|
|
1916
|
-
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */
|
|
1951
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "1d" }, "1 Day"),
|
|
1952
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "7d" }, "7 Days"),
|
|
1953
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "30d" }, "30 Days"),
|
|
1954
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "90d" }, "90 Days"),
|
|
1955
|
+
/* @__PURE__ */ import_react5.default.createElement(import_MenuItem.default, { value: "1y" }, "1 Year")
|
|
1956
|
+
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ import_react5.default.createElement(
|
|
1917
1957
|
import_Autocomplete.default,
|
|
1918
1958
|
{
|
|
1919
1959
|
multiple: true,
|
|
@@ -1922,8 +1962,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1922
1962
|
getOptionLabel: (m) => m.model_name,
|
|
1923
1963
|
value: selectedModels,
|
|
1924
1964
|
onChange: (_e, selected) => setFormData({ models: selected.map((m) => m.model_name) }),
|
|
1925
|
-
renderOption: (props, m) => /* @__PURE__ */
|
|
1926
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1965
|
+
renderOption: (props, m) => /* @__PURE__ */ import_react5.default.createElement("li", { ...props }, modelOption(m)),
|
|
1966
|
+
renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
|
|
1927
1967
|
import_TextField2.default,
|
|
1928
1968
|
{
|
|
1929
1969
|
...params,
|
|
@@ -1933,10 +1973,10 @@ var init_KeyFormDialog = __esm({
|
|
|
1933
1973
|
}
|
|
1934
1974
|
)
|
|
1935
1975
|
}
|
|
1936
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
1976
|
+
), allowUnlimitedBudget && /* @__PURE__ */ import_react5.default.createElement(
|
|
1937
1977
|
import_FormControlLabel.default,
|
|
1938
1978
|
{
|
|
1939
|
-
control: /* @__PURE__ */
|
|
1979
|
+
control: /* @__PURE__ */ import_react5.default.createElement(
|
|
1940
1980
|
import_Checkbox.default,
|
|
1941
1981
|
{
|
|
1942
1982
|
checked: unlimitedBudget,
|
|
@@ -1952,7 +1992,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1952
1992
|
),
|
|
1953
1993
|
label: "Unlimited budget"
|
|
1954
1994
|
}
|
|
1955
|
-
), /* @__PURE__ */
|
|
1995
|
+
), /* @__PURE__ */ import_react5.default.createElement(
|
|
1956
1996
|
import_TextField2.default,
|
|
1957
1997
|
{
|
|
1958
1998
|
label: "Max Budget (USD)",
|
|
@@ -1965,7 +2005,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1965
2005
|
required: true,
|
|
1966
2006
|
fullWidth: true
|
|
1967
2007
|
}
|
|
1968
|
-
), /* @__PURE__ */
|
|
2008
|
+
), /* @__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
2009
|
import_TextField2.default,
|
|
1970
2010
|
{
|
|
1971
2011
|
label: "TPM Limit",
|
|
@@ -1975,7 +2015,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1975
2015
|
helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
|
|
1976
2016
|
fullWidth: true
|
|
1977
2017
|
}
|
|
1978
|
-
), /* @__PURE__ */
|
|
2018
|
+
), /* @__PURE__ */ import_react5.default.createElement(
|
|
1979
2019
|
import_TextField2.default,
|
|
1980
2020
|
{
|
|
1981
2021
|
label: "RPM Limit",
|
|
@@ -1985,7 +2025,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1985
2025
|
helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
|
|
1986
2026
|
fullWidth: true
|
|
1987
2027
|
}
|
|
1988
|
-
))), renderDangerZone())), /* @__PURE__ */
|
|
2028
|
+
))), 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
2029
|
import_Button3.default,
|
|
1990
2030
|
{
|
|
1991
2031
|
onClick: isCreate ? handleGenerate : handleUpdate,
|
|
@@ -1993,18 +2033,18 @@ var init_KeyFormDialog = __esm({
|
|
|
1993
2033
|
color: "primary",
|
|
1994
2034
|
disabled: !canSubmit
|
|
1995
2035
|
},
|
|
1996
|
-
submitting ? /* @__PURE__ */
|
|
2036
|
+
submitting ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress2.default, { size: 24 }) : submitLabel
|
|
1997
2037
|
))));
|
|
1998
2038
|
};
|
|
1999
2039
|
}
|
|
2000
2040
|
});
|
|
2001
2041
|
|
|
2002
2042
|
// src/components/ManageTeamDialog.tsx
|
|
2003
|
-
var
|
|
2043
|
+
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
2044
|
var init_ManageTeamDialog = __esm({
|
|
2005
2045
|
"src/components/ManageTeamDialog.tsx"() {
|
|
2006
2046
|
"use strict";
|
|
2007
|
-
|
|
2047
|
+
import_react6 = __toESM(require("react"));
|
|
2008
2048
|
import_Dialog3 = __toESM(require("@mui/material/Dialog"));
|
|
2009
2049
|
import_DialogTitle3 = __toESM(require("@mui/material/DialogTitle"));
|
|
2010
2050
|
import_DialogContent3 = __toESM(require("@mui/material/DialogContent"));
|
|
@@ -2061,29 +2101,29 @@ var init_ManageTeamDialog = __esm({
|
|
|
2061
2101
|
mcpServers,
|
|
2062
2102
|
onSaveMcpServers
|
|
2063
2103
|
}) => {
|
|
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,
|
|
2104
|
+
const [alias, setAlias] = (0, import_react6.useState)("");
|
|
2105
|
+
const [models, setModels] = (0, import_react6.useState)([]);
|
|
2106
|
+
const [maxBudget, setMaxBudget] = (0, import_react6.useState)("");
|
|
2107
|
+
const [unlimited, setUnlimited] = (0, import_react6.useState)(false);
|
|
2108
|
+
const [budgetDuration, setBudgetDuration] = (0, import_react6.useState)("");
|
|
2109
|
+
const [submitting, setSubmitting] = (0, import_react6.useState)(false);
|
|
2110
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
2111
|
+
const [memberQuery, setMemberQuery] = (0, import_react6.useState)("");
|
|
2112
|
+
const [memberRef, setMemberRef] = (0, import_react6.useState)("");
|
|
2113
|
+
const [memberBudget, setMemberBudget] = (0, import_react6.useState)("");
|
|
2114
|
+
const [memberBusy, setMemberBusy] = (0, import_react6.useState)(false);
|
|
2115
|
+
const [memberError, setMemberError] = (0, import_react6.useState)(null);
|
|
2116
|
+
const [kbIds, setKbIds] = (0, import_react6.useState)([]);
|
|
2117
|
+
const [kbBusy, setKbBusy] = (0, import_react6.useState)(false);
|
|
2118
|
+
const [kbError, setKbError] = (0, import_react6.useState)(null);
|
|
2119
|
+
const [mcpIds, setMcpIds] = (0, import_react6.useState)([]);
|
|
2120
|
+
const [mcpBusy, setMcpBusy] = (0, import_react6.useState)(false);
|
|
2121
|
+
const [mcpError, setMcpError] = (0, import_react6.useState)(null);
|
|
2082
2122
|
const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
|
|
2083
2123
|
const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
|
|
2084
2124
|
const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
|
|
2085
2125
|
const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
|
|
2086
|
-
const members = (0,
|
|
2126
|
+
const members = (0, import_react6.useMemo)(() => team?.members_with_roles ?? [], [team]);
|
|
2087
2127
|
const showMembers = mode === "edit" && !!canManageMembers;
|
|
2088
2128
|
const catalogApi = (0, import_core_plugin_api2.useApi)(import_plugin_catalog_react.catalogApiRef);
|
|
2089
2129
|
const { value: catalogUsers, loading: usersLoading } = (0, import_react_use.useAsync)(async () => {
|
|
@@ -2098,7 +2138,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2098
2138
|
return [];
|
|
2099
2139
|
}
|
|
2100
2140
|
}, [open, showMembers, catalogApi]);
|
|
2101
|
-
const userOptions = (0,
|
|
2141
|
+
const userOptions = (0, import_react6.useMemo)(() => {
|
|
2102
2142
|
const existingEmails = new Set(
|
|
2103
2143
|
members.map((m) => m.user_email?.toLowerCase()).filter((e) => !!e)
|
|
2104
2144
|
);
|
|
@@ -2113,7 +2153,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2113
2153
|
};
|
|
2114
2154
|
}).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
|
|
2115
2155
|
}, [catalogUsers, members]);
|
|
2116
|
-
(0,
|
|
2156
|
+
(0, import_react6.useEffect)(() => {
|
|
2117
2157
|
if (open) {
|
|
2118
2158
|
if (mode === "edit" && team) {
|
|
2119
2159
|
setAlias(team.team_alias ?? "");
|
|
@@ -2269,7 +2309,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2269
2309
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
2270
2310
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
2271
2311
|
const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
|
|
2272
|
-
return /* @__PURE__ */
|
|
2312
|
+
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
2313
|
import_TextField3.default,
|
|
2274
2314
|
{
|
|
2275
2315
|
label: "Team Alias",
|
|
@@ -2278,7 +2318,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2278
2318
|
disabled: submitting,
|
|
2279
2319
|
fullWidth: true
|
|
2280
2320
|
}
|
|
2281
|
-
), /* @__PURE__ */
|
|
2321
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2282
2322
|
import_Autocomplete2.default,
|
|
2283
2323
|
{
|
|
2284
2324
|
multiple: true,
|
|
@@ -2286,7 +2326,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2286
2326
|
value: models,
|
|
2287
2327
|
onChange: (_, newValue) => setModels(newValue),
|
|
2288
2328
|
disabled: submitting,
|
|
2289
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2329
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2290
2330
|
import_TextField3.default,
|
|
2291
2331
|
{
|
|
2292
2332
|
...params,
|
|
@@ -2296,7 +2336,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2296
2336
|
}
|
|
2297
2337
|
)
|
|
2298
2338
|
}
|
|
2299
|
-
), /* @__PURE__ */
|
|
2339
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2300
2340
|
import_TextField3.default,
|
|
2301
2341
|
{
|
|
2302
2342
|
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
@@ -2308,13 +2348,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
2308
2348
|
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
2309
2349
|
fullWidth: true
|
|
2310
2350
|
}
|
|
2311
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
2351
|
+
), allowUnlimitedBudget && /* @__PURE__ */ import_react6.default.createElement(
|
|
2312
2352
|
import_FormControlLabel2.default,
|
|
2313
2353
|
{
|
|
2314
|
-
control: /* @__PURE__ */
|
|
2354
|
+
control: /* @__PURE__ */ import_react6.default.createElement(import_Checkbox2.default, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
2315
2355
|
label: "Unlimited Budget"
|
|
2316
2356
|
}
|
|
2317
|
-
), /* @__PURE__ */
|
|
2357
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2318
2358
|
import_TextField3.default,
|
|
2319
2359
|
{
|
|
2320
2360
|
select: true,
|
|
@@ -2325,8 +2365,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2325
2365
|
helperText: "Spend-reset period for the team budget",
|
|
2326
2366
|
fullWidth: true
|
|
2327
2367
|
},
|
|
2328
|
-
durationOptions.map((o) => /* @__PURE__ */
|
|
2329
|
-
), showMembers && /* @__PURE__ */
|
|
2368
|
+
durationOptions.map((o) => /* @__PURE__ */ import_react6.default.createElement(import_MenuItem2.default, { key: o.value, value: o.value }, o.label))
|
|
2369
|
+
), 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
2370
|
import_Typography5.default,
|
|
2331
2371
|
{
|
|
2332
2372
|
variant: "caption",
|
|
@@ -2334,14 +2374,14 @@ var init_ManageTeamDialog = __esm({
|
|
|
2334
2374
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
2335
2375
|
},
|
|
2336
2376
|
m.user_id
|
|
2337
|
-
)) : /* @__PURE__ */
|
|
2377
|
+
)) : /* @__PURE__ */ import_react6.default.createElement(
|
|
2338
2378
|
import_Typography5.default,
|
|
2339
2379
|
{
|
|
2340
2380
|
variant: "body2",
|
|
2341
2381
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
2342
2382
|
},
|
|
2343
2383
|
m.user_id
|
|
2344
|
-
)), /* @__PURE__ */
|
|
2384
|
+
)), /* @__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
2385
|
import_IconButton3.default,
|
|
2346
2386
|
{
|
|
2347
2387
|
edge: "end",
|
|
@@ -2350,8 +2390,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2350
2390
|
disabled: memberBusy,
|
|
2351
2391
|
onClick: () => handleRemoveMember(m.user_id)
|
|
2352
2392
|
},
|
|
2353
|
-
/* @__PURE__ */
|
|
2354
|
-
))))))), /* @__PURE__ */
|
|
2393
|
+
/* @__PURE__ */ import_react6.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
|
|
2394
|
+
))))))), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2355
2395
|
import_Autocomplete2.default,
|
|
2356
2396
|
{
|
|
2357
2397
|
freeSolo: true,
|
|
@@ -2375,7 +2415,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2375
2415
|
isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
|
|
2376
2416
|
disabled: memberBusy,
|
|
2377
2417
|
sx: { flex: 1, minWidth: 260 },
|
|
2378
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2418
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2379
2419
|
import_TextField3.default,
|
|
2380
2420
|
{
|
|
2381
2421
|
...params,
|
|
@@ -2385,12 +2425,12 @@ var init_ManageTeamDialog = __esm({
|
|
|
2385
2425
|
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
2386
2426
|
InputProps: {
|
|
2387
2427
|
...params.InputProps,
|
|
2388
|
-
endAdornment: /* @__PURE__ */
|
|
2428
|
+
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
2429
|
}
|
|
2390
2430
|
}
|
|
2391
2431
|
)
|
|
2392
2432
|
}
|
|
2393
|
-
), /* @__PURE__ */
|
|
2433
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2394
2434
|
import_TextField3.default,
|
|
2395
2435
|
{
|
|
2396
2436
|
label: "Max budget in team ($)",
|
|
@@ -2401,7 +2441,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2401
2441
|
size: "small",
|
|
2402
2442
|
sx: { width: 160 }
|
|
2403
2443
|
}
|
|
2404
|
-
), /* @__PURE__ */
|
|
2444
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
2405
2445
|
import_Button4.default,
|
|
2406
2446
|
{
|
|
2407
2447
|
onClick: handleAddMember,
|
|
@@ -2410,7 +2450,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2410
2450
|
sx: { mt: 0.5 }
|
|
2411
2451
|
},
|
|
2412
2452
|
"Add"
|
|
2413
|
-
))), showKnowledgeBases && /* @__PURE__ */
|
|
2453
|
+
))), 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
2454
|
import_Autocomplete2.default,
|
|
2415
2455
|
{
|
|
2416
2456
|
multiple: true,
|
|
@@ -2419,9 +2459,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2419
2459
|
getOptionLabel: kbLabel,
|
|
2420
2460
|
onChange: (_, v) => setKbIds(v),
|
|
2421
2461
|
disabled: kbBusy,
|
|
2422
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2462
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(import_TextField3.default, { ...params, label: "Attached knowledge bases" })
|
|
2423
2463
|
}
|
|
2424
|
-
), /* @__PURE__ */
|
|
2464
|
+
), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2425
2465
|
import_Button4.default,
|
|
2426
2466
|
{
|
|
2427
2467
|
onClick: handleSaveKnowledgeBases,
|
|
@@ -2429,7 +2469,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2429
2469
|
variant: "outlined"
|
|
2430
2470
|
},
|
|
2431
2471
|
kbBusy ? "Saving\u2026" : "Save knowledge bases"
|
|
2432
|
-
))), showMcpServers && /* @__PURE__ */
|
|
2472
|
+
))), 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
2473
|
import_Autocomplete2.default,
|
|
2434
2474
|
{
|
|
2435
2475
|
multiple: true,
|
|
@@ -2438,9 +2478,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2438
2478
|
getOptionLabel: mcpLabel,
|
|
2439
2479
|
onChange: (_, v) => setMcpIds(v),
|
|
2440
2480
|
disabled: mcpBusy,
|
|
2441
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2481
|
+
renderInput: (params) => /* @__PURE__ */ import_react6.default.createElement(import_TextField3.default, { ...params, label: "Attached MCP servers" })
|
|
2442
2482
|
}
|
|
2443
|
-
), /* @__PURE__ */
|
|
2483
|
+
), /* @__PURE__ */ import_react6.default.createElement(import_Box5.default, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2444
2484
|
import_Button4.default,
|
|
2445
2485
|
{
|
|
2446
2486
|
onClick: handleSaveMcpServers,
|
|
@@ -2448,7 +2488,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2448
2488
|
variant: "outlined"
|
|
2449
2489
|
},
|
|
2450
2490
|
mcpBusy ? "Saving\u2026" : "Save MCP servers"
|
|
2451
|
-
)))), /* @__PURE__ */
|
|
2491
|
+
)))), /* @__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
2492
|
};
|
|
2453
2493
|
}
|
|
2454
2494
|
});
|
|
@@ -2459,11 +2499,11 @@ function rateTone(rate) {
|
|
|
2459
2499
|
if (rate >= 0.9) return "warning";
|
|
2460
2500
|
return "danger";
|
|
2461
2501
|
}
|
|
2462
|
-
var
|
|
2502
|
+
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
2503
|
var init_UsageStats = __esm({
|
|
2464
2504
|
"src/components/UsageStats.tsx"() {
|
|
2465
2505
|
"use strict";
|
|
2466
|
-
|
|
2506
|
+
import_react7 = __toESM(require("react"));
|
|
2467
2507
|
import_Paper4 = __toESM(require("@mui/material/Paper"));
|
|
2468
2508
|
import_Box6 = __toESM(require("@mui/material/Box"));
|
|
2469
2509
|
import_Typography6 = __toESM(require("@mui/material/Typography"));
|
|
@@ -2489,16 +2529,16 @@ var init_UsageStats = __esm({
|
|
|
2489
2529
|
"30d": "Last 30 days"
|
|
2490
2530
|
};
|
|
2491
2531
|
fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
|
|
2492
|
-
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */
|
|
2532
|
+
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ import_react7.default.createElement(import_Skeleton3.default, { variant: "rounded", height });
|
|
2493
2533
|
ChartOrFallback = ({ loading, empty, height = 240, children }) => {
|
|
2494
|
-
if (loading) return /* @__PURE__ */
|
|
2495
|
-
if (empty) return /* @__PURE__ */
|
|
2496
|
-
return /* @__PURE__ */
|
|
2534
|
+
if (loading) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { height });
|
|
2535
|
+
if (empty) return /* @__PURE__ */ import_react7.default.createElement(EmptyState, { message: "No data for this period", height });
|
|
2536
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, children);
|
|
2497
2537
|
};
|
|
2498
2538
|
SuccessRateCell = ({ rate, requests }) => {
|
|
2499
|
-
if (requests === 0) return /* @__PURE__ */
|
|
2539
|
+
if (requests === 0) return /* @__PURE__ */ import_react7.default.createElement(import_Typography6.default, { variant: "body2", color: "text.secondary" }, "\u2014");
|
|
2500
2540
|
const tone = rateTone(rate);
|
|
2501
|
-
return /* @__PURE__ */
|
|
2541
|
+
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
2542
|
import_Typography6.default,
|
|
2503
2543
|
{
|
|
2504
2544
|
variant: "caption",
|
|
@@ -2517,8 +2557,8 @@ var init_UsageStats = __esm({
|
|
|
2517
2557
|
userInfo
|
|
2518
2558
|
}) => {
|
|
2519
2559
|
const chart = useChartTheme();
|
|
2520
|
-
const [selectedModel, setSelectedModel] = (0,
|
|
2521
|
-
const [tab, setTab] = (0,
|
|
2560
|
+
const [selectedModel, setSelectedModel] = (0, import_react7.useState)("all");
|
|
2561
|
+
const [tab, setTab] = (0, import_react7.useState)("costs");
|
|
2522
2562
|
const handlePresetChange = (preset) => {
|
|
2523
2563
|
const end = /* @__PURE__ */ new Date();
|
|
2524
2564
|
const start = /* @__PURE__ */ new Date();
|
|
@@ -2532,7 +2572,7 @@ var init_UsageStats = __esm({
|
|
|
2532
2572
|
}
|
|
2533
2573
|
onDateRangeChange({ start, end }, preset);
|
|
2534
2574
|
};
|
|
2535
|
-
const dailyData = (0,
|
|
2575
|
+
const dailyData = (0, import_react7.useMemo)(
|
|
2536
2576
|
() => (usage?.daily_usage ?? []).map((d) => ({
|
|
2537
2577
|
date: d.date,
|
|
2538
2578
|
spend: d.spend,
|
|
@@ -2545,21 +2585,21 @@ var init_UsageStats = __esm({
|
|
|
2545
2585
|
})),
|
|
2546
2586
|
[usage]
|
|
2547
2587
|
);
|
|
2548
|
-
const cumulativeData = (0,
|
|
2588
|
+
const cumulativeData = (0, import_react7.useMemo)(() => {
|
|
2549
2589
|
let cum = 0;
|
|
2550
2590
|
return dailyData.map((d) => {
|
|
2551
2591
|
cum += d.spend;
|
|
2552
2592
|
return { date: d.date, cumulative: cum };
|
|
2553
2593
|
});
|
|
2554
2594
|
}, [dailyData]);
|
|
2555
|
-
const successRateData = (0,
|
|
2595
|
+
const successRateData = (0, import_react7.useMemo)(
|
|
2556
2596
|
() => dailyData.filter((d) => d.apiRequests > 0).map((d) => ({
|
|
2557
2597
|
date: d.date,
|
|
2558
2598
|
successRate: parseFloat((d.successfulRequests / d.apiRequests * 100).toFixed(1))
|
|
2559
2599
|
})),
|
|
2560
2600
|
[dailyData]
|
|
2561
2601
|
);
|
|
2562
|
-
const { modelSpendByDate, topModels: topSpendModels } = (0,
|
|
2602
|
+
const { modelSpendByDate, topModels: topSpendModels } = (0, import_react7.useMemo)(() => {
|
|
2563
2603
|
const rows = usage?.daily_by_model ?? [];
|
|
2564
2604
|
const modelTotals = {};
|
|
2565
2605
|
for (const r of rows) modelTotals[r.model] = (modelTotals[r.model] ?? 0) + r.spend;
|
|
@@ -2574,7 +2614,7 @@ var init_UsageStats = __esm({
|
|
|
2574
2614
|
const hasOther = sorted.some((r) => r.Other > 0);
|
|
2575
2615
|
return { modelSpendByDate: sorted, topModels: hasOther ? [...top, "Other"] : top };
|
|
2576
2616
|
}, [usage]);
|
|
2577
|
-
const modelRows = (0,
|
|
2617
|
+
const modelRows = (0, import_react7.useMemo)(() => {
|
|
2578
2618
|
const entries = Object.entries(usage?.usage_by_model ?? {}).map(([model, d]) => ({
|
|
2579
2619
|
model,
|
|
2580
2620
|
spend: d.total_spend,
|
|
@@ -2588,11 +2628,11 @@ var init_UsageStats = __esm({
|
|
|
2588
2628
|
}));
|
|
2589
2629
|
return selectedModel === "all" ? entries : entries.filter((e) => e.model === selectedModel);
|
|
2590
2630
|
}, [usage, selectedModel]);
|
|
2591
|
-
const topModelSpendBars = (0,
|
|
2631
|
+
const topModelSpendBars = (0, import_react7.useMemo)(
|
|
2592
2632
|
() => [...modelRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
|
|
2593
2633
|
[modelRows]
|
|
2594
2634
|
);
|
|
2595
|
-
const keyRows = (0,
|
|
2635
|
+
const keyRows = (0, import_react7.useMemo)(
|
|
2596
2636
|
() => Object.entries(usage?.usage_by_key ?? {}).map(([keyHash, d]) => ({
|
|
2597
2637
|
keyHash,
|
|
2598
2638
|
keyAlias: d.key_alias ?? keyHash.slice(0, 8),
|
|
@@ -2609,7 +2649,7 @@ var init_UsageStats = __esm({
|
|
|
2609
2649
|
})),
|
|
2610
2650
|
[usage]
|
|
2611
2651
|
);
|
|
2612
|
-
const topKeySpendBars = (0,
|
|
2652
|
+
const topKeySpendBars = (0, import_react7.useMemo)(
|
|
2613
2653
|
() => [...keyRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
|
|
2614
2654
|
[keyRows]
|
|
2615
2655
|
);
|
|
@@ -2647,23 +2687,23 @@ var init_UsageStats = __esm({
|
|
|
2647
2687
|
];
|
|
2648
2688
|
const renderKeyRows = () => {
|
|
2649
2689
|
if (loading) {
|
|
2650
|
-
return /* @__PURE__ */
|
|
2690
|
+
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
2691
|
}
|
|
2652
2692
|
if (keyRows.length === 0) {
|
|
2653
|
-
return /* @__PURE__ */
|
|
2693
|
+
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
2694
|
}
|
|
2655
|
-
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */
|
|
2695
|
+
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
2696
|
};
|
|
2657
2697
|
const renderModelRows = () => {
|
|
2658
2698
|
if (loading) {
|
|
2659
|
-
return /* @__PURE__ */
|
|
2699
|
+
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
2700
|
}
|
|
2661
2701
|
if (modelRows.length === 0) {
|
|
2662
|
-
return /* @__PURE__ */
|
|
2702
|
+
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
2703
|
}
|
|
2664
|
-
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */
|
|
2704
|
+
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
2705
|
};
|
|
2666
|
-
const periodSelect = /* @__PURE__ */
|
|
2706
|
+
const periodSelect = /* @__PURE__ */ import_react7.default.createElement(
|
|
2667
2707
|
import_TextField4.default,
|
|
2668
2708
|
{
|
|
2669
2709
|
select: true,
|
|
@@ -2673,17 +2713,17 @@ var init_UsageStats = __esm({
|
|
|
2673
2713
|
onChange: (e) => handlePresetChange(e.target.value),
|
|
2674
2714
|
sx: { minWidth: 160 }
|
|
2675
2715
|
},
|
|
2676
|
-
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */
|
|
2716
|
+
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { key: p, value: p }, PRESET_LABELS[p]))
|
|
2677
2717
|
);
|
|
2678
|
-
return /* @__PURE__ */
|
|
2718
|
+
return /* @__PURE__ */ import_react7.default.createElement(
|
|
2679
2719
|
SectionCard,
|
|
2680
2720
|
{
|
|
2681
2721
|
title: "Usage Analytics",
|
|
2682
2722
|
subtitle: `${PRESET_LABELS[currentPreset]} \xB7 ${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
2683
2723
|
actions: periodSelect
|
|
2684
2724
|
},
|
|
2685
|
-
/* @__PURE__ */
|
|
2686
|
-
/* @__PURE__ */
|
|
2725
|
+
/* @__PURE__ */ import_react7.default.createElement(MetricStrip, { metrics }),
|
|
2726
|
+
/* @__PURE__ */ import_react7.default.createElement(
|
|
2687
2727
|
import_Box6.default,
|
|
2688
2728
|
{
|
|
2689
2729
|
sx: {
|
|
@@ -2696,7 +2736,7 @@ var init_UsageStats = __esm({
|
|
|
2696
2736
|
mb: 2
|
|
2697
2737
|
}
|
|
2698
2738
|
},
|
|
2699
|
-
/* @__PURE__ */
|
|
2739
|
+
/* @__PURE__ */ import_react7.default.createElement(
|
|
2700
2740
|
SegmentedControl,
|
|
2701
2741
|
{
|
|
2702
2742
|
value: tab,
|
|
@@ -2708,7 +2748,7 @@ var init_UsageStats = __esm({
|
|
|
2708
2748
|
]
|
|
2709
2749
|
}
|
|
2710
2750
|
),
|
|
2711
|
-
tab === "models" && /* @__PURE__ */
|
|
2751
|
+
tab === "models" && /* @__PURE__ */ import_react7.default.createElement(
|
|
2712
2752
|
import_TextField4.default,
|
|
2713
2753
|
{
|
|
2714
2754
|
select: true,
|
|
@@ -2718,17 +2758,17 @@ var init_UsageStats = __esm({
|
|
|
2718
2758
|
onChange: (e) => setSelectedModel(e.target.value),
|
|
2719
2759
|
sx: { minWidth: 220 }
|
|
2720
2760
|
},
|
|
2721
|
-
/* @__PURE__ */
|
|
2722
|
-
models.map((m) => /* @__PURE__ */
|
|
2761
|
+
/* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { value: "all" }, "All models"),
|
|
2762
|
+
models.map((m) => /* @__PURE__ */ import_react7.default.createElement(import_MenuItem3.default, { key: m.model_name, value: m.model_name }, m.model_name))
|
|
2723
2763
|
)
|
|
2724
2764
|
),
|
|
2725
|
-
tab === "costs" && /* @__PURE__ */
|
|
2765
|
+
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
2766
|
import_recharts.Tooltip,
|
|
2727
2767
|
{
|
|
2728
2768
|
cursor: chart.cursor,
|
|
2729
|
-
content: /* @__PURE__ */
|
|
2769
|
+
content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
|
|
2730
2770
|
}
|
|
2731
|
-
), topSpendModels.map((m) => /* @__PURE__ */
|
|
2771
|
+
), topSpendModels.map((m) => /* @__PURE__ */ import_react7.default.createElement(
|
|
2732
2772
|
import_recharts.Area,
|
|
2733
2773
|
{
|
|
2734
2774
|
key: m,
|
|
@@ -2740,13 +2780,13 @@ var init_UsageStats = __esm({
|
|
|
2740
2780
|
fill: chartColor(m),
|
|
2741
2781
|
fillOpacity: 0.28
|
|
2742
2782
|
}
|
|
2743
|
-
))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */
|
|
2783
|
+
))))), !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
2784
|
import_recharts.Tooltip,
|
|
2745
2785
|
{
|
|
2746
2786
|
cursor: chart.cursor,
|
|
2747
|
-
content: /* @__PURE__ */
|
|
2787
|
+
content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
|
|
2748
2788
|
}
|
|
2749
|
-
), /* @__PURE__ */
|
|
2789
|
+
), /* @__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
2790
|
import_recharts.Line,
|
|
2751
2791
|
{
|
|
2752
2792
|
type: "monotone",
|
|
@@ -2757,13 +2797,13 @@ var init_UsageStats = __esm({
|
|
|
2757
2797
|
dot: { r: 2.5, strokeWidth: 0, fill: SERIES.input },
|
|
2758
2798
|
activeDot: { r: 4 }
|
|
2759
2799
|
}
|
|
2760
|
-
)))))), /* @__PURE__ */
|
|
2800
|
+
)))))), /* @__PURE__ */ import_react7.default.createElement(import_Grid.default, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2761
2801
|
ChartCard,
|
|
2762
2802
|
{
|
|
2763
2803
|
title: maxBudget > 0 ? "Cumulative spend vs budget" : "Cumulative spend",
|
|
2764
2804
|
meta: maxBudget > 0 ? `${fmtUsd(totalCumSpend)} used \xB7 ${fmtUsd(Math.max(0, maxBudget - totalCumSpend))} left` : void 0
|
|
2765
2805
|
},
|
|
2766
|
-
/* @__PURE__ */
|
|
2806
|
+
/* @__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
2807
|
import_recharts.YAxis,
|
|
2768
2808
|
{
|
|
2769
2809
|
tickFormatter: fmtUsdCompact,
|
|
@@ -2771,13 +2811,13 @@ var init_UsageStats = __esm({
|
|
|
2771
2811
|
domain: cumulativeDomain,
|
|
2772
2812
|
...chart.axis
|
|
2773
2813
|
}
|
|
2774
|
-
), /* @__PURE__ */
|
|
2814
|
+
), /* @__PURE__ */ import_react7.default.createElement(
|
|
2775
2815
|
import_recharts.Tooltip,
|
|
2776
2816
|
{
|
|
2777
2817
|
cursor: chart.cursor,
|
|
2778
|
-
content: /* @__PURE__ */
|
|
2818
|
+
content: /* @__PURE__ */ import_react7.default.createElement(ChartTooltip, { valueFormatter: fmtUsd })
|
|
2779
2819
|
}
|
|
2780
|
-
), maxBudget > 0 && /* @__PURE__ */
|
|
2820
|
+
), maxBudget > 0 && /* @__PURE__ */ import_react7.default.createElement(
|
|
2781
2821
|
import_recharts.ReferenceLine,
|
|
2782
2822
|
{
|
|
2783
2823
|
y: maxBudget,
|
|
@@ -2785,7 +2825,7 @@ var init_UsageStats = __esm({
|
|
|
2785
2825
|
strokeDasharray: "5 4",
|
|
2786
2826
|
label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 10, fill: SERIES.budget }
|
|
2787
2827
|
}
|
|
2788
|
-
), /* @__PURE__ */
|
|
2828
|
+
), /* @__PURE__ */ import_react7.default.createElement(
|
|
2789
2829
|
import_recharts.Area,
|
|
2790
2830
|
{
|
|
2791
2831
|
type: "monotone",
|
|
@@ -2798,32 +2838,32 @@ var init_UsageStats = __esm({
|
|
|
2798
2838
|
}
|
|
2799
2839
|
))))
|
|
2800
2840
|
))),
|
|
2801
|
-
tab === "models" && /* @__PURE__ */
|
|
2841
|
+
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
2842
|
ChartOrFallback,
|
|
2803
2843
|
{
|
|
2804
2844
|
loading,
|
|
2805
2845
|
empty: topModelSpendBars.length === 0,
|
|
2806
2846
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2807
2847
|
},
|
|
2808
|
-
/* @__PURE__ */
|
|
2809
|
-
))), /* @__PURE__ */
|
|
2848
|
+
/* @__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) })))))
|
|
2849
|
+
))), /* @__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
2850
|
ChartOrFallback,
|
|
2811
2851
|
{
|
|
2812
2852
|
loading,
|
|
2813
2853
|
empty: modelRows.length === 0,
|
|
2814
2854
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2815
2855
|
},
|
|
2816
|
-
/* @__PURE__ */
|
|
2817
|
-
))), /* @__PURE__ */
|
|
2818
|
-
tab === "keys" && /* @__PURE__ */
|
|
2856
|
+
/* @__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] })))
|
|
2857
|
+
))), /* @__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()))))),
|
|
2858
|
+
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
2859
|
ChartOrFallback,
|
|
2820
2860
|
{
|
|
2821
2861
|
loading,
|
|
2822
2862
|
empty: topKeySpendBars.length === 0,
|
|
2823
2863
|
height: Math.max(160, topKeySpendBars.length * 32)
|
|
2824
2864
|
},
|
|
2825
|
-
/* @__PURE__ */
|
|
2826
|
-
))), /* @__PURE__ */
|
|
2865
|
+
/* @__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 })))
|
|
2866
|
+
))), /* @__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
2867
|
);
|
|
2828
2868
|
};
|
|
2829
2869
|
}
|
|
@@ -2965,16 +3005,16 @@ function levelToneColor(tone, theme) {
|
|
|
2965
3005
|
return theme.palette.primary.main;
|
|
2966
3006
|
}
|
|
2967
3007
|
}
|
|
2968
|
-
var
|
|
3008
|
+
var import_react8, import_Box7, import_Paper5, import_Typography7, import_styles4, import_core_components2, LimitCard, TaggedLimit, MoreKeysNote, BudgetLimitList, LimitListPanel;
|
|
2969
3009
|
var init_BudgetLimitList = __esm({
|
|
2970
3010
|
"src/components/BudgetLimitList.tsx"() {
|
|
2971
3011
|
"use strict";
|
|
2972
|
-
|
|
3012
|
+
import_react8 = __toESM(require("react"));
|
|
2973
3013
|
import_Box7 = __toESM(require("@mui/material/Box"));
|
|
2974
3014
|
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
2975
3015
|
import_Typography7 = __toESM(require("@mui/material/Typography"));
|
|
2976
3016
|
import_styles4 = require("@mui/material/styles");
|
|
2977
|
-
|
|
3017
|
+
import_core_components2 = require("@backstage/core-components");
|
|
2978
3018
|
init_budget();
|
|
2979
3019
|
init_format();
|
|
2980
3020
|
init_ui();
|
|
@@ -2982,7 +3022,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2982
3022
|
const tone = budgetTone2(limit.pct);
|
|
2983
3023
|
const pct = Math.round(limit.pct);
|
|
2984
3024
|
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
2985
|
-
return /* @__PURE__ */
|
|
3025
|
+
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
3026
|
import_Typography7.default,
|
|
2987
3027
|
{
|
|
2988
3028
|
variant: "body2",
|
|
@@ -2990,7 +3030,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2990
3030
|
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
2991
3031
|
},
|
|
2992
3032
|
limit.label
|
|
2993
|
-
), limit.sublabel && /* @__PURE__ */
|
|
3033
|
+
), limit.sublabel && /* @__PURE__ */ import_react8.default.createElement(
|
|
2994
3034
|
import_Typography7.default,
|
|
2995
3035
|
{
|
|
2996
3036
|
variant: "caption",
|
|
@@ -3005,7 +3045,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3005
3045
|
}
|
|
3006
3046
|
},
|
|
3007
3047
|
limit.sublabel
|
|
3008
|
-
)), limit.hidden ? /* @__PURE__ */
|
|
3048
|
+
)), 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
3049
|
import_Box7.default,
|
|
3010
3050
|
{
|
|
3011
3051
|
sx: {
|
|
@@ -3016,14 +3056,14 @@ var init_BudgetLimitList = __esm({
|
|
|
3016
3056
|
mt: 0.75
|
|
3017
3057
|
}
|
|
3018
3058
|
},
|
|
3019
|
-
/* @__PURE__ */
|
|
3020
|
-
/* @__PURE__ */
|
|
3059
|
+
/* @__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`)),
|
|
3060
|
+
/* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3021
3061
|
));
|
|
3022
3062
|
};
|
|
3023
3063
|
TaggedLimit = ({ limit }) => {
|
|
3024
3064
|
const theme = (0, import_styles4.useTheme)();
|
|
3025
3065
|
const tone = budgetTone2(limit.pct);
|
|
3026
|
-
return /* @__PURE__ */
|
|
3066
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react8.default.createElement(
|
|
3027
3067
|
import_Typography7.default,
|
|
3028
3068
|
{
|
|
3029
3069
|
sx: {
|
|
@@ -3036,7 +3076,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3036
3076
|
}
|
|
3037
3077
|
},
|
|
3038
3078
|
limit.kind
|
|
3039
|
-
), /* @__PURE__ */
|
|
3079
|
+
), /* @__PURE__ */ import_react8.default.createElement(LimitCard, { limit }));
|
|
3040
3080
|
};
|
|
3041
3081
|
MoreKeysNote = ({
|
|
3042
3082
|
count,
|
|
@@ -3045,12 +3085,12 @@ var init_BudgetLimitList = __esm({
|
|
|
3045
3085
|
}) => {
|
|
3046
3086
|
const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
|
|
3047
3087
|
if (!href) {
|
|
3048
|
-
return /* @__PURE__ */
|
|
3088
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary" }, text);
|
|
3049
3089
|
}
|
|
3050
|
-
return /* @__PURE__ */
|
|
3090
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_core_components2.Link, { to: href, variant: "caption" }, text, " \u2192");
|
|
3051
3091
|
};
|
|
3052
|
-
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */
|
|
3053
|
-
LimitListPanel = ({ children }) => /* @__PURE__ */
|
|
3092
|
+
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 }));
|
|
3093
|
+
LimitListPanel = ({ children }) => /* @__PURE__ */ import_react8.default.createElement(
|
|
3054
3094
|
import_Box7.default,
|
|
3055
3095
|
{
|
|
3056
3096
|
sx: (theme) => ({
|
|
@@ -3066,11 +3106,11 @@ var init_BudgetLimitList = __esm({
|
|
|
3066
3106
|
});
|
|
3067
3107
|
|
|
3068
3108
|
// src/components/LiteLLMBudgetWidget.tsx
|
|
3069
|
-
var
|
|
3109
|
+
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
3110
|
var init_LiteLLMBudgetWidget = __esm({
|
|
3071
3111
|
"src/components/LiteLLMBudgetWidget.tsx"() {
|
|
3072
3112
|
"use strict";
|
|
3073
|
-
|
|
3113
|
+
import_react9 = __toESM(require("react"));
|
|
3074
3114
|
import_Paper6 = __toESM(require("@mui/material/Paper"));
|
|
3075
3115
|
import_Box8 = __toESM(require("@mui/material/Box"));
|
|
3076
3116
|
import_Typography8 = __toESM(require("@mui/material/Typography"));
|
|
@@ -3093,7 +3133,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3093
3133
|
tone,
|
|
3094
3134
|
isLast,
|
|
3095
3135
|
children
|
|
3096
|
-
}) => /* @__PURE__ */
|
|
3136
|
+
}) => /* @__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
3137
|
import_Box8.default,
|
|
3098
3138
|
{
|
|
3099
3139
|
sx: (theme) => ({
|
|
@@ -3111,7 +3151,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3111
3151
|
})
|
|
3112
3152
|
},
|
|
3113
3153
|
rank
|
|
3114
|
-
), /* @__PURE__ */
|
|
3154
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3115
3155
|
import_Box8.default,
|
|
3116
3156
|
{
|
|
3117
3157
|
sx: (theme) => ({
|
|
@@ -3121,7 +3161,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3121
3161
|
bgcolor: isLast ? "transparent" : (0, import_styles5.alpha)(theme.palette.text.primary, 0.12)
|
|
3122
3162
|
})
|
|
3123
3163
|
}
|
|
3124
|
-
)), /* @__PURE__ */
|
|
3164
|
+
)), /* @__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
3165
|
import_Typography8.default,
|
|
3126
3166
|
{
|
|
3127
3167
|
sx: (theme) => ({
|
|
@@ -3133,8 +3173,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3133
3173
|
})
|
|
3134
3174
|
},
|
|
3135
3175
|
name
|
|
3136
|
-
), /* @__PURE__ */
|
|
3137
|
-
EmptyLimitCard = ({ children }) => /* @__PURE__ */
|
|
3176
|
+
), /* @__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)));
|
|
3177
|
+
EmptyLimitCard = ({ children }) => /* @__PURE__ */ import_react9.default.createElement(
|
|
3138
3178
|
import_Paper6.default,
|
|
3139
3179
|
{
|
|
3140
3180
|
variant: "outlined",
|
|
@@ -3145,7 +3185,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3145
3185
|
bgcolor: (0, import_styles5.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
|
|
3146
3186
|
})
|
|
3147
3187
|
},
|
|
3148
|
-
/* @__PURE__ */
|
|
3188
|
+
/* @__PURE__ */ import_react9.default.createElement(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
|
|
3149
3189
|
);
|
|
3150
3190
|
CLOSEST_LEVEL_LABEL = {
|
|
3151
3191
|
key: "key",
|
|
@@ -3153,7 +3193,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3153
3193
|
team: "team"
|
|
3154
3194
|
};
|
|
3155
3195
|
KEYS_PATH = "/litellm?tab=keys";
|
|
3156
|
-
HierarchyNote = ({ withReset }) => /* @__PURE__ */
|
|
3196
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react9.default.createElement(
|
|
3157
3197
|
import_Box8.default,
|
|
3158
3198
|
{
|
|
3159
3199
|
sx: (theme) => ({
|
|
@@ -3164,11 +3204,11 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3164
3204
|
borderColor: (0, import_styles5.alpha)(theme.palette.text.primary, 0.18)
|
|
3165
3205
|
})
|
|
3166
3206
|
},
|
|
3167
|
-
/* @__PURE__ */
|
|
3207
|
+
/* @__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
3208
|
);
|
|
3169
3209
|
FullBody = ({ summary }) => {
|
|
3170
3210
|
const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
|
|
3171
|
-
return /* @__PURE__ */
|
|
3211
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement(
|
|
3172
3212
|
LevelFrame,
|
|
3173
3213
|
{
|
|
3174
3214
|
rank: 1,
|
|
@@ -3176,10 +3216,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3176
3216
|
tagline: "per-key cap \xB7 highest priority",
|
|
3177
3217
|
note: hasBudgetedKeys ? void 0 : "no key budgets set"
|
|
3178
3218
|
},
|
|
3179
|
-
!hasBudgetedKeys && /* @__PURE__ */
|
|
3180
|
-
summary.keys.map((k) => /* @__PURE__ */
|
|
3181
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */
|
|
3182
|
-
), /* @__PURE__ */
|
|
3219
|
+
!hasBudgetedKeys && /* @__PURE__ */ import_react9.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3220
|
+
summary.keys.map((k) => /* @__PURE__ */ import_react9.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3221
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react9.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
|
|
3222
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3183
3223
|
LevelFrame,
|
|
3184
3224
|
{
|
|
3185
3225
|
rank: 2,
|
|
@@ -3187,15 +3227,15 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3187
3227
|
tagline: "cap on everything you spend with your own keys",
|
|
3188
3228
|
note: "skipped for team-bound keys"
|
|
3189
3229
|
},
|
|
3190
|
-
summary.user ? /* @__PURE__ */
|
|
3191
|
-
), /* @__PURE__ */
|
|
3230
|
+
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.")
|
|
3231
|
+
), /* @__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
3232
|
};
|
|
3193
3233
|
CompactBody = ({ summary }) => {
|
|
3194
3234
|
const { count } = budgetHeadline(summary);
|
|
3195
3235
|
if (count === 0) {
|
|
3196
|
-
return /* @__PURE__ */
|
|
3236
|
+
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
3237
|
}
|
|
3198
|
-
return /* @__PURE__ */
|
|
3238
|
+
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
3239
|
};
|
|
3200
3240
|
LiteLLMBudgetWidget = ({
|
|
3201
3241
|
title = "Budget Policy",
|
|
@@ -3206,13 +3246,13 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3206
3246
|
action
|
|
3207
3247
|
}) => {
|
|
3208
3248
|
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,
|
|
3249
|
+
const [loading, setLoading] = (0, import_react9.useState)(true);
|
|
3250
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
3251
|
+
const [user, setUser] = (0, import_react9.useState)(null);
|
|
3252
|
+
const [teams, setTeams] = (0, import_react9.useState)([]);
|
|
3253
|
+
const [keys, setKeys] = (0, import_react9.useState)([]);
|
|
3254
|
+
const [expanded, setExpanded] = (0, import_react9.useState)(defaultExpanded);
|
|
3255
|
+
(0, import_react9.useEffect)(() => {
|
|
3216
3256
|
let cancelled = false;
|
|
3217
3257
|
setLoading(true);
|
|
3218
3258
|
setError(null);
|
|
@@ -3236,18 +3276,18 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3236
3276
|
cancelled = true;
|
|
3237
3277
|
};
|
|
3238
3278
|
}, [api]);
|
|
3239
|
-
const summary = (0,
|
|
3279
|
+
const summary = (0, import_react9.useMemo)(
|
|
3240
3280
|
() => buildBudgetSummary(user, teams, keys, maxKeys),
|
|
3241
3281
|
[user, teams, keys, maxKeys]
|
|
3242
3282
|
);
|
|
3243
|
-
const headline = (0,
|
|
3283
|
+
const headline = (0, import_react9.useMemo)(() => budgetHeadline(summary), [summary]);
|
|
3244
3284
|
const limitsWord = `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
3245
3285
|
const closestWord = headline.closest === null ? "" : ` \xB7 closest: ${CLOSEST_LEVEL_LABEL[headline.closest.kind]} ${Math.round(
|
|
3246
3286
|
headline.closest.pct
|
|
3247
3287
|
)}%`;
|
|
3248
3288
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3249
|
-
const body = /* @__PURE__ */
|
|
3250
|
-
return /* @__PURE__ */
|
|
3289
|
+
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 })));
|
|
3290
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_Paper6.default, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ import_react9.default.createElement(
|
|
3251
3291
|
import_Box8.default,
|
|
3252
3292
|
{
|
|
3253
3293
|
onClick: () => setExpanded((e) => !e),
|
|
@@ -3260,14 +3300,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3260
3300
|
mb: expanded ? 1.5 : 0
|
|
3261
3301
|
}
|
|
3262
3302
|
},
|
|
3263
|
-
/* @__PURE__ */
|
|
3303
|
+
/* @__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
3304
|
StatusPill,
|
|
3265
3305
|
{
|
|
3266
3306
|
label: summaryText,
|
|
3267
3307
|
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3268
3308
|
}
|
|
3269
3309
|
))),
|
|
3270
|
-
/* @__PURE__ */
|
|
3310
|
+
/* @__PURE__ */ import_react9.default.createElement(
|
|
3271
3311
|
import_IconButton4.default,
|
|
3272
3312
|
{
|
|
3273
3313
|
size: "small",
|
|
@@ -3283,9 +3323,9 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3283
3323
|
transition: theme.transitions.create("transform")
|
|
3284
3324
|
})
|
|
3285
3325
|
},
|
|
3286
|
-
/* @__PURE__ */
|
|
3326
|
+
/* @__PURE__ */ import_react9.default.createElement(import_icons_material5.ExpandMore, null)
|
|
3287
3327
|
)
|
|
3288
|
-
) : /* @__PURE__ */
|
|
3328
|
+
) : /* @__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
3329
|
import_Box8.default,
|
|
3290
3330
|
{
|
|
3291
3331
|
sx: (theme) => ({
|
|
@@ -3315,11 +3355,11 @@ function budgetStatValue(hidden, budget) {
|
|
|
3315
3355
|
if (budget > 0) return fmtUsd2(budget);
|
|
3316
3356
|
return "Unlimited";
|
|
3317
3357
|
}
|
|
3318
|
-
var
|
|
3358
|
+
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
3359
|
var init_TeamUsage = __esm({
|
|
3320
3360
|
"src/components/TeamUsage.tsx"() {
|
|
3321
3361
|
"use strict";
|
|
3322
|
-
|
|
3362
|
+
import_react10 = __toESM(require("react"));
|
|
3323
3363
|
import_Paper7 = __toESM(require("@mui/material/Paper"));
|
|
3324
3364
|
import_Box9 = __toESM(require("@mui/material/Box"));
|
|
3325
3365
|
import_Typography9 = __toESM(require("@mui/material/Typography"));
|
|
@@ -3341,7 +3381,7 @@ var init_TeamUsage = __esm({
|
|
|
3341
3381
|
init_ui();
|
|
3342
3382
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
3343
3383
|
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
|
|
3344
|
-
const [expanded, setExpanded] = (0,
|
|
3384
|
+
const [expanded, setExpanded] = (0, import_react10.useState)(false);
|
|
3345
3385
|
const chart = useChartTheme();
|
|
3346
3386
|
const hidden = team.budget_hidden === true;
|
|
3347
3387
|
const budget = hidden ? 0 : team.max_budget ?? 0;
|
|
@@ -3353,10 +3393,10 @@ var init_TeamUsage = __esm({
|
|
|
3353
3393
|
const renderDailySpendSection = () => {
|
|
3354
3394
|
if (hidden) return null;
|
|
3355
3395
|
if (usageLoading) {
|
|
3356
|
-
return /* @__PURE__ */
|
|
3396
|
+
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
3397
|
}
|
|
3358
3398
|
if (dailyData.length === 0) return null;
|
|
3359
|
-
return /* @__PURE__ */
|
|
3399
|
+
return /* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { mb: 2.5 }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3360
3400
|
import_Typography9.default,
|
|
3361
3401
|
{
|
|
3362
3402
|
variant: "caption",
|
|
@@ -3364,13 +3404,13 @@ var init_TeamUsage = __esm({
|
|
|
3364
3404
|
sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
|
|
3365
3405
|
},
|
|
3366
3406
|
"Daily spend"
|
|
3367
|
-
), /* @__PURE__ */
|
|
3407
|
+
), /* @__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
3408
|
import_recharts2.Tooltip,
|
|
3369
3409
|
{
|
|
3370
3410
|
cursor: chart.cursor,
|
|
3371
|
-
content: /* @__PURE__ */
|
|
3411
|
+
content: /* @__PURE__ */ import_react10.default.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
3372
3412
|
}
|
|
3373
|
-
), /* @__PURE__ */
|
|
3413
|
+
), /* @__PURE__ */ import_react10.default.createElement(
|
|
3374
3414
|
import_recharts2.Area,
|
|
3375
3415
|
{
|
|
3376
3416
|
type: "monotone",
|
|
@@ -3383,7 +3423,7 @@ var init_TeamUsage = __esm({
|
|
|
3383
3423
|
}
|
|
3384
3424
|
)))));
|
|
3385
3425
|
};
|
|
3386
|
-
const sectionLabel = (label) => /* @__PURE__ */
|
|
3426
|
+
const sectionLabel = (label) => /* @__PURE__ */ import_react10.default.createElement(
|
|
3387
3427
|
import_Typography9.default,
|
|
3388
3428
|
{
|
|
3389
3429
|
variant: "caption",
|
|
@@ -3392,7 +3432,7 @@ var init_TeamUsage = __esm({
|
|
|
3392
3432
|
},
|
|
3393
3433
|
label
|
|
3394
3434
|
);
|
|
3395
|
-
return /* @__PURE__ */
|
|
3435
|
+
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
3436
|
import_Box9.default,
|
|
3397
3437
|
{
|
|
3398
3438
|
sx: (theme) => ({
|
|
@@ -3407,8 +3447,8 @@ var init_TeamUsage = __esm({
|
|
|
3407
3447
|
color: theme.palette.primary.main
|
|
3408
3448
|
})
|
|
3409
3449
|
},
|
|
3410
|
-
/* @__PURE__ */
|
|
3411
|
-
), /* @__PURE__ */
|
|
3450
|
+
/* @__PURE__ */ import_react10.default.createElement(import_icons_material6.Group, { fontSize: "small" })
|
|
3451
|
+
), /* @__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
3452
|
import_Typography9.default,
|
|
3413
3453
|
{
|
|
3414
3454
|
variant: "caption",
|
|
@@ -3416,7 +3456,7 @@ var init_TeamUsage = __esm({
|
|
|
3416
3456
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3417
3457
|
},
|
|
3418
3458
|
team.team_id
|
|
3419
|
-
)), isOver && /* @__PURE__ */
|
|
3459
|
+
)), 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
3460
|
import_Button5.default,
|
|
3421
3461
|
{
|
|
3422
3462
|
size: "small",
|
|
@@ -3425,7 +3465,7 @@ var init_TeamUsage = __esm({
|
|
|
3425
3465
|
sx: { textTransform: "none" }
|
|
3426
3466
|
},
|
|
3427
3467
|
"Edit"
|
|
3428
|
-
), /* @__PURE__ */
|
|
3468
|
+
), /* @__PURE__ */ import_react10.default.createElement(
|
|
3429
3469
|
import_IconButton5.default,
|
|
3430
3470
|
{
|
|
3431
3471
|
size: "small",
|
|
@@ -3438,8 +3478,8 @@ var init_TeamUsage = __esm({
|
|
|
3438
3478
|
transition: theme.transitions.create("transform")
|
|
3439
3479
|
})
|
|
3440
3480
|
},
|
|
3441
|
-
/* @__PURE__ */
|
|
3442
|
-
)), /* @__PURE__ */
|
|
3481
|
+
/* @__PURE__ */ import_react10.default.createElement(import_icons_material6.ExpandMore, null)
|
|
3482
|
+
)), /* @__PURE__ */ import_react10.default.createElement(
|
|
3443
3483
|
import_Box9.default,
|
|
3444
3484
|
{
|
|
3445
3485
|
sx: {
|
|
@@ -3450,20 +3490,20 @@ var init_TeamUsage = __esm({
|
|
|
3450
3490
|
maxWidth: 640
|
|
3451
3491
|
}
|
|
3452
3492
|
},
|
|
3453
|
-
/* @__PURE__ */
|
|
3454
|
-
/* @__PURE__ */
|
|
3455
|
-
/* @__PURE__ */
|
|
3456
|
-
/* @__PURE__ */
|
|
3457
|
-
/* @__PURE__ */
|
|
3458
|
-
/* @__PURE__ */
|
|
3459
|
-
), hidden && /* @__PURE__ */
|
|
3493
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
|
|
3494
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
|
|
3495
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
|
|
3496
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
|
|
3497
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
3498
|
+
/* @__PURE__ */ import_react10.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
3499
|
+
), 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
3500
|
import_TableContainer4.default,
|
|
3461
3501
|
{
|
|
3462
3502
|
component: import_Paper7.default,
|
|
3463
3503
|
variant: "outlined",
|
|
3464
3504
|
sx: { borderRadius: 1.5 }
|
|
3465
3505
|
},
|
|
3466
|
-
/* @__PURE__ */
|
|
3506
|
+
/* @__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
3507
|
import_Typography9.default,
|
|
3468
3508
|
{
|
|
3469
3509
|
variant: "caption",
|
|
@@ -3471,14 +3511,14 @@ var init_TeamUsage = __esm({
|
|
|
3471
3511
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3472
3512
|
},
|
|
3473
3513
|
m.user_id
|
|
3474
|
-
)) : /* @__PURE__ */
|
|
3514
|
+
)) : /* @__PURE__ */ import_react10.default.createElement(
|
|
3475
3515
|
import_Typography9.default,
|
|
3476
3516
|
{
|
|
3477
3517
|
variant: "body2",
|
|
3478
3518
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
3479
3519
|
},
|
|
3480
3520
|
m.user_id
|
|
3481
|
-
)), /* @__PURE__ */
|
|
3521
|
+
)), /* @__PURE__ */ import_react10.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3482
3522
|
StatusPill,
|
|
3483
3523
|
{
|
|
3484
3524
|
label: m.role,
|
|
@@ -3486,7 +3526,7 @@ var init_TeamUsage = __esm({
|
|
|
3486
3526
|
dot: false
|
|
3487
3527
|
}
|
|
3488
3528
|
))))))
|
|
3489
|
-
) : /* @__PURE__ */
|
|
3529
|
+
) : /* @__PURE__ */ import_react10.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
|
|
3490
3530
|
};
|
|
3491
3531
|
TeamUsage = ({
|
|
3492
3532
|
teams,
|
|
@@ -3498,22 +3538,22 @@ var init_TeamUsage = __esm({
|
|
|
3498
3538
|
canCreate,
|
|
3499
3539
|
onCreateTeam
|
|
3500
3540
|
}) => {
|
|
3501
|
-
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */
|
|
3541
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react10.default.createElement(
|
|
3502
3542
|
import_Button5.default,
|
|
3503
3543
|
{
|
|
3504
3544
|
variant: "contained",
|
|
3505
3545
|
color: "primary",
|
|
3506
3546
|
disableElevation: true,
|
|
3507
|
-
startIcon: /* @__PURE__ */
|
|
3547
|
+
startIcon: /* @__PURE__ */ import_react10.default.createElement(import_icons_material6.Add, null),
|
|
3508
3548
|
onClick: onCreateTeam
|
|
3509
3549
|
},
|
|
3510
3550
|
"Create Team"
|
|
3511
3551
|
) : void 0;
|
|
3512
3552
|
if (loading) {
|
|
3513
|
-
return /* @__PURE__ */
|
|
3553
|
+
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
3554
|
}
|
|
3515
3555
|
if (!teams.length) {
|
|
3516
|
-
return /* @__PURE__ */
|
|
3556
|
+
return /* @__PURE__ */ import_react10.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3517
3557
|
EmptyState,
|
|
3518
3558
|
{
|
|
3519
3559
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -3521,14 +3561,14 @@ var init_TeamUsage = __esm({
|
|
|
3521
3561
|
}
|
|
3522
3562
|
));
|
|
3523
3563
|
}
|
|
3524
|
-
return /* @__PURE__ */
|
|
3564
|
+
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3525
3565
|
SectionCard,
|
|
3526
3566
|
{
|
|
3527
3567
|
title: "Teams",
|
|
3528
3568
|
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
3529
3569
|
actions: createAction
|
|
3530
3570
|
},
|
|
3531
|
-
/* @__PURE__ */
|
|
3571
|
+
/* @__PURE__ */ import_react10.default.createElement(import_Box9.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react10.default.createElement(
|
|
3532
3572
|
TeamCard,
|
|
3533
3573
|
{
|
|
3534
3574
|
key: team.team_id,
|
|
@@ -3562,11 +3602,11 @@ function fmtTokens(n) {
|
|
|
3562
3602
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
3563
3603
|
return String(n);
|
|
3564
3604
|
}
|
|
3565
|
-
var
|
|
3605
|
+
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
3606
|
var init_ModelsTable = __esm({
|
|
3567
3607
|
"src/components/ModelsTable.tsx"() {
|
|
3568
3608
|
"use strict";
|
|
3569
|
-
|
|
3609
|
+
import_react11 = __toESM(require("react"));
|
|
3570
3610
|
import_Box10 = __toESM(require("@mui/material/Box"));
|
|
3571
3611
|
import_Table5 = __toESM(require("@mui/material/Table"));
|
|
3572
3612
|
import_TableBody5 = __toESM(require("@mui/material/TableBody"));
|
|
@@ -3592,20 +3632,20 @@ var init_ModelsTable = __esm({
|
|
|
3592
3632
|
ALL_PROXY_MODELS2 = "all-proxy-models";
|
|
3593
3633
|
ModelsTable = ({ allModels, teams, loading }) => {
|
|
3594
3634
|
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,
|
|
3635
|
+
const [selectedTeamId, setSelectedTeamId] = (0, import_react11.useState)("");
|
|
3636
|
+
const [filterText, setFilterText] = (0, import_react11.useState)("");
|
|
3637
|
+
const [copiedSnackbar, setCopiedSnackbar] = (0, import_react11.useState)(false);
|
|
3638
|
+
const [sortKey, setSortKey] = (0, import_react11.useState)("model_name");
|
|
3639
|
+
const [sortDirection, setSortDirection] = (0, import_react11.useState)("asc");
|
|
3640
|
+
const selectedTeam = (0, import_react11.useMemo)(
|
|
3601
3641
|
() => teams.find((t) => t.team_id === selectedTeamId) ?? null,
|
|
3602
3642
|
[teams, selectedTeamId]
|
|
3603
3643
|
);
|
|
3604
|
-
const teamModels = (0,
|
|
3644
|
+
const teamModels = (0, import_react11.useMemo)(() => {
|
|
3605
3645
|
if (!selectedTeam) return [];
|
|
3606
3646
|
return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
|
|
3607
3647
|
}, [allModels, selectedTeam]);
|
|
3608
|
-
const filteredModels = (0,
|
|
3648
|
+
const filteredModels = (0, import_react11.useMemo)(() => {
|
|
3609
3649
|
const q = filterText.trim().toLowerCase();
|
|
3610
3650
|
let models = teamModels;
|
|
3611
3651
|
if (q) {
|
|
@@ -3652,10 +3692,10 @@ var init_ModelsTable = __esm({
|
|
|
3652
3692
|
return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
|
|
3653
3693
|
});
|
|
3654
3694
|
}, [teamModels, filterText, sortKey, sortDirection]);
|
|
3655
|
-
const handleCopyModelId = (0,
|
|
3695
|
+
const handleCopyModelId = (0, import_react11.useCallback)((modelId) => {
|
|
3656
3696
|
navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
|
|
3657
3697
|
}, []);
|
|
3658
|
-
const handleSort = (0,
|
|
3698
|
+
const handleSort = (0, import_react11.useCallback)((key) => {
|
|
3659
3699
|
if (sortKey === key) {
|
|
3660
3700
|
setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
|
|
3661
3701
|
} else {
|
|
@@ -3677,13 +3717,13 @@ var init_ModelsTable = __esm({
|
|
|
3677
3717
|
return theme.palette.info.main;
|
|
3678
3718
|
}
|
|
3679
3719
|
};
|
|
3680
|
-
return /* @__PURE__ */
|
|
3720
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
3681
3721
|
SectionCard,
|
|
3682
3722
|
{
|
|
3683
3723
|
title: "Available Models",
|
|
3684
3724
|
subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
|
|
3685
3725
|
},
|
|
3686
|
-
/* @__PURE__ */
|
|
3726
|
+
/* @__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
3727
|
import_Select.default,
|
|
3688
3728
|
{
|
|
3689
3729
|
labelId: "team-select-label",
|
|
@@ -3691,8 +3731,8 @@ var init_ModelsTable = __esm({
|
|
|
3691
3731
|
label: "Team",
|
|
3692
3732
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
3693
3733
|
},
|
|
3694
|
-
teams.map((t) => /* @__PURE__ */
|
|
3695
|
-
)), /* @__PURE__ */
|
|
3734
|
+
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))
|
|
3735
|
+
)), /* @__PURE__ */ import_react11.default.createElement(
|
|
3696
3736
|
import_TextField5.default,
|
|
3697
3737
|
{
|
|
3698
3738
|
label: "Filter Models",
|
|
@@ -3702,33 +3742,33 @@ var init_ModelsTable = __esm({
|
|
|
3702
3742
|
disabled: !selectedTeamId,
|
|
3703
3743
|
sx: { minWidth: 240, mb: 2 },
|
|
3704
3744
|
InputProps: {
|
|
3705
|
-
startAdornment: /* @__PURE__ */
|
|
3745
|
+
startAdornment: /* @__PURE__ */ import_react11.default.createElement(import_InputAdornment2.default, { position: "start" }, /* @__PURE__ */ import_react11.default.createElement(import_icons_material7.Search, { fontSize: "small" }))
|
|
3706
3746
|
}
|
|
3707
3747
|
}
|
|
3708
3748
|
)),
|
|
3709
|
-
loading && /* @__PURE__ */
|
|
3710
|
-
!loading && !selectedTeamId && /* @__PURE__ */
|
|
3749
|
+
loading && /* @__PURE__ */ import_react11.default.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
3750
|
+
!loading && !selectedTeamId && /* @__PURE__ */ import_react11.default.createElement(
|
|
3711
3751
|
EmptyState,
|
|
3712
3752
|
{
|
|
3713
3753
|
message: "Select a team to see available models",
|
|
3714
3754
|
hint: "Each team may have a different set of models assigned"
|
|
3715
3755
|
}
|
|
3716
3756
|
),
|
|
3717
|
-
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */
|
|
3757
|
+
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ import_react11.default.createElement(
|
|
3718
3758
|
EmptyState,
|
|
3719
3759
|
{
|
|
3720
3760
|
message: "No models available for this team",
|
|
3721
3761
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
3722
3762
|
}
|
|
3723
3763
|
),
|
|
3724
|
-
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */
|
|
3764
|
+
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ import_react11.default.createElement(
|
|
3725
3765
|
EmptyState,
|
|
3726
3766
|
{
|
|
3727
3767
|
message: "No models match your search",
|
|
3728
3768
|
hint: "Try a different search term"
|
|
3729
3769
|
}
|
|
3730
3770
|
),
|
|
3731
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */
|
|
3771
|
+
!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
3772
|
"span",
|
|
3733
3773
|
{
|
|
3734
3774
|
role: "button",
|
|
@@ -3738,8 +3778,8 @@ var init_ModelsTable = __esm({
|
|
|
3738
3778
|
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
3739
3779
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3740
3780
|
},
|
|
3741
|
-
/* @__PURE__ */
|
|
3742
|
-
)), /* @__PURE__ */
|
|
3781
|
+
/* @__PURE__ */ import_react11.default.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
3782
|
+
)), /* @__PURE__ */ import_react11.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react11.default.createElement(
|
|
3743
3783
|
import_Box10.default,
|
|
3744
3784
|
{
|
|
3745
3785
|
component: "span",
|
|
@@ -3750,8 +3790,8 @@ var init_ModelsTable = __esm({
|
|
|
3750
3790
|
sx: quietIconButtonSx("accent"),
|
|
3751
3791
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3752
3792
|
},
|
|
3753
|
-
/* @__PURE__ */
|
|
3754
|
-
)))), /* @__PURE__ */
|
|
3793
|
+
/* @__PURE__ */ import_react11.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
|
|
3794
|
+
)))), /* @__PURE__ */ import_react11.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react11.default.createElement(
|
|
3755
3795
|
import_Box10.default,
|
|
3756
3796
|
{
|
|
3757
3797
|
component: "span",
|
|
@@ -3768,8 +3808,8 @@ var init_ModelsTable = __esm({
|
|
|
3768
3808
|
}
|
|
3769
3809
|
},
|
|
3770
3810
|
m.mode
|
|
3771
|
-
)), /* @__PURE__ */
|
|
3772
|
-
/* @__PURE__ */
|
|
3811
|
+
)), /* @__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" })))))))),
|
|
3812
|
+
/* @__PURE__ */ import_react11.default.createElement(
|
|
3773
3813
|
import_Snackbar.default,
|
|
3774
3814
|
{
|
|
3775
3815
|
open: copiedSnackbar,
|
|
@@ -3777,7 +3817,7 @@ var init_ModelsTable = __esm({
|
|
|
3777
3817
|
onClose: () => setCopiedSnackbar(false),
|
|
3778
3818
|
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
3779
3819
|
},
|
|
3780
|
-
/* @__PURE__ */
|
|
3820
|
+
/* @__PURE__ */ import_react11.default.createElement(import_Alert4.default, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
3781
3821
|
)
|
|
3782
3822
|
);
|
|
3783
3823
|
};
|
|
@@ -3805,10 +3845,10 @@ function formatDateTime(iso) {
|
|
|
3805
3845
|
}
|
|
3806
3846
|
function renderAuditLogBody(loading, entries) {
|
|
3807
3847
|
if (loading) {
|
|
3808
|
-
return /* @__PURE__ */
|
|
3848
|
+
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
3849
|
}
|
|
3810
3850
|
if (entries.length === 0) {
|
|
3811
|
-
return /* @__PURE__ */
|
|
3851
|
+
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
3852
|
EmptyState,
|
|
3813
3853
|
{
|
|
3814
3854
|
message: "No audit events found",
|
|
@@ -3816,13 +3856,13 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3816
3856
|
}
|
|
3817
3857
|
)));
|
|
3818
3858
|
}
|
|
3819
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
3859
|
+
return entries.map((entry) => /* @__PURE__ */ import_react12.default.createElement(DetailRow, { key: entry.id, entry }));
|
|
3820
3860
|
}
|
|
3821
|
-
var
|
|
3861
|
+
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
3862
|
var init_AuditLog = __esm({
|
|
3823
3863
|
"src/components/AuditLog.tsx"() {
|
|
3824
3864
|
"use strict";
|
|
3825
|
-
|
|
3865
|
+
import_react12 = __toESM(require("react"));
|
|
3826
3866
|
import_Box11 = __toESM(require("@mui/material/Box"));
|
|
3827
3867
|
import_Typography11 = __toESM(require("@mui/material/Typography"));
|
|
3828
3868
|
import_Table6 = __toESM(require("@mui/material/Table"));
|
|
@@ -3857,9 +3897,9 @@ var init_AuditLog = __esm({
|
|
|
3857
3897
|
LiteLLM_UserTable: "User"
|
|
3858
3898
|
};
|
|
3859
3899
|
DetailRow = ({ entry }) => {
|
|
3860
|
-
const [open, setOpen] = (0,
|
|
3900
|
+
const [open, setOpen] = (0, import_react12.useState)(false);
|
|
3861
3901
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3862
|
-
return /* @__PURE__ */
|
|
3902
|
+
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
3903
|
import_IconButton6.default,
|
|
3864
3904
|
{
|
|
3865
3905
|
size: "small",
|
|
@@ -3872,8 +3912,8 @@ var init_AuditLog = __esm({
|
|
|
3872
3912
|
transition: theme.transitions.create("transform")
|
|
3873
3913
|
})
|
|
3874
3914
|
},
|
|
3875
|
-
/* @__PURE__ */
|
|
3876
|
-
)), /* @__PURE__ */
|
|
3915
|
+
/* @__PURE__ */ import_react12.default.createElement(import_icons_material8.KeyboardArrowDown, { fontSize: "small" })
|
|
3916
|
+
)), /* @__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
3917
|
import_Typography11.default,
|
|
3878
3918
|
{
|
|
3879
3919
|
variant: "body2",
|
|
@@ -3883,7 +3923,7 @@ var init_AuditLog = __esm({
|
|
|
3883
3923
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3884
3924
|
},
|
|
3885
3925
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3886
|
-
)), /* @__PURE__ */
|
|
3926
|
+
)), /* @__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
3927
|
import_Box11.default,
|
|
3888
3928
|
{
|
|
3889
3929
|
display: "flex",
|
|
@@ -3896,7 +3936,7 @@ var init_AuditLog = __esm({
|
|
|
3896
3936
|
bgcolor: (0, import_styles8.alpha)(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
3897
3937
|
})
|
|
3898
3938
|
},
|
|
3899
|
-
entry.before_value && /* @__PURE__ */
|
|
3939
|
+
entry.before_value && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3900
3940
|
import_Typography11.default,
|
|
3901
3941
|
{
|
|
3902
3942
|
variant: "caption",
|
|
@@ -3906,8 +3946,8 @@ var init_AuditLog = __esm({
|
|
|
3906
3946
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3907
3947
|
},
|
|
3908
3948
|
"Before"
|
|
3909
|
-
), /* @__PURE__ */
|
|
3910
|
-
entry.updated_values && /* @__PURE__ */
|
|
3949
|
+
), /* @__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))),
|
|
3950
|
+
entry.updated_values && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { flex: 1, minWidth: 200 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3911
3951
|
import_Typography11.default,
|
|
3912
3952
|
{
|
|
3913
3953
|
variant: "caption",
|
|
@@ -3917,14 +3957,14 @@ var init_AuditLog = __esm({
|
|
|
3917
3957
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3918
3958
|
},
|
|
3919
3959
|
"After"
|
|
3920
|
-
), /* @__PURE__ */
|
|
3960
|
+
), /* @__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
3961
|
)))));
|
|
3922
3962
|
};
|
|
3923
3963
|
AuditLog = ({ api }) => {
|
|
3924
|
-
const [page, setPage] = (0,
|
|
3925
|
-
const [pageSize, setPageSize] = (0,
|
|
3926
|
-
const [filters, setFilters] = (0,
|
|
3927
|
-
const fetchParams = (0,
|
|
3964
|
+
const [page, setPage] = (0, import_react12.useState)(0);
|
|
3965
|
+
const [pageSize, setPageSize] = (0, import_react12.useState)(25);
|
|
3966
|
+
const [filters, setFilters] = (0, import_react12.useState)({});
|
|
3967
|
+
const fetchParams = (0, import_react12.useMemo)(
|
|
3928
3968
|
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
3929
3969
|
[page, pageSize, filters]
|
|
3930
3970
|
);
|
|
@@ -3934,13 +3974,13 @@ var init_AuditLog = __esm({
|
|
|
3934
3974
|
);
|
|
3935
3975
|
const entries = value?.audit_logs ?? [];
|
|
3936
3976
|
const total = value?.total ?? 0;
|
|
3937
|
-
return /* @__PURE__ */
|
|
3977
|
+
return /* @__PURE__ */ import_react12.default.createElement(
|
|
3938
3978
|
SectionCard,
|
|
3939
3979
|
{
|
|
3940
3980
|
title: "Audit Log",
|
|
3941
3981
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
3942
3982
|
flush: true,
|
|
3943
|
-
actions: /* @__PURE__ */
|
|
3983
|
+
actions: /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(
|
|
3944
3984
|
import_TextField6.default,
|
|
3945
3985
|
{
|
|
3946
3986
|
size: "small",
|
|
@@ -3953,12 +3993,12 @@ var init_AuditLog = __esm({
|
|
|
3953
3993
|
},
|
|
3954
3994
|
sx: { minWidth: 150 }
|
|
3955
3995
|
},
|
|
3956
|
-
/* @__PURE__ */
|
|
3957
|
-
/* @__PURE__ */
|
|
3958
|
-
/* @__PURE__ */
|
|
3959
|
-
/* @__PURE__ */
|
|
3960
|
-
/* @__PURE__ */
|
|
3961
|
-
), /* @__PURE__ */
|
|
3996
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
|
|
3997
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
|
|
3998
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
|
|
3999
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
|
|
4000
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
|
|
4001
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
3962
4002
|
import_TextField6.default,
|
|
3963
4003
|
{
|
|
3964
4004
|
size: "small",
|
|
@@ -3971,13 +4011,13 @@ var init_AuditLog = __esm({
|
|
|
3971
4011
|
},
|
|
3972
4012
|
sx: { minWidth: 150 }
|
|
3973
4013
|
},
|
|
3974
|
-
/* @__PURE__ */
|
|
3975
|
-
/* @__PURE__ */
|
|
3976
|
-
/* @__PURE__ */
|
|
3977
|
-
/* @__PURE__ */
|
|
3978
|
-
/* @__PURE__ */
|
|
3979
|
-
/* @__PURE__ */
|
|
3980
|
-
), /* @__PURE__ */
|
|
4014
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
|
|
4015
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
4016
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
4017
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
|
|
4018
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
|
|
4019
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
|
|
4020
|
+
), /* @__PURE__ */ import_react12.default.createElement(
|
|
3981
4021
|
import_TextField6.default,
|
|
3982
4022
|
{
|
|
3983
4023
|
size: "small",
|
|
@@ -3991,9 +4031,9 @@ var init_AuditLog = __esm({
|
|
|
3991
4031
|
}
|
|
3992
4032
|
))
|
|
3993
4033
|
},
|
|
3994
|
-
error && /* @__PURE__ */
|
|
3995
|
-
/* @__PURE__ */
|
|
3996
|
-
/* @__PURE__ */
|
|
4034
|
+
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)),
|
|
4035
|
+
/* @__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)))),
|
|
4036
|
+
/* @__PURE__ */ import_react12.default.createElement(
|
|
3997
4037
|
import_TablePagination.default,
|
|
3998
4038
|
{
|
|
3999
4039
|
component: "div",
|
|
@@ -4062,11 +4102,11 @@ function initDateRange() {
|
|
|
4062
4102
|
else start.setDate(start.getDate() - 7);
|
|
4063
4103
|
return { start, end };
|
|
4064
4104
|
}
|
|
4065
|
-
var
|
|
4105
|
+
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
4106
|
var init_LiteLLMPage = __esm({
|
|
4067
4107
|
"src/components/LiteLLMPage.tsx"() {
|
|
4068
4108
|
"use strict";
|
|
4069
|
-
|
|
4109
|
+
import_react13 = __toESM(require("react"));
|
|
4070
4110
|
import_Box12 = __toESM(require("@mui/material/Box"));
|
|
4071
4111
|
import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
|
|
4072
4112
|
import_Alert6 = __toESM(require("@mui/material/Alert"));
|
|
@@ -4096,8 +4136,8 @@ var init_LiteLLMPage = __esm({
|
|
|
4096
4136
|
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
4097
4137
|
LiteLLMPage = () => {
|
|
4098
4138
|
const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
|
|
4099
|
-
const [dateRange, setDateRange] = (0,
|
|
4100
|
-
const [currentPreset, setCurrentPreset] = (0,
|
|
4139
|
+
const [dateRange, setDateRange] = (0, import_react13.useState)(initDateRange);
|
|
4140
|
+
const [currentPreset, setCurrentPreset] = (0, import_react13.useState)(() => {
|
|
4101
4141
|
try {
|
|
4102
4142
|
return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
|
|
4103
4143
|
} catch {
|
|
@@ -4105,14 +4145,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4105
4145
|
}
|
|
4106
4146
|
});
|
|
4107
4147
|
const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
|
|
4108
|
-
const [activeTab, setActiveTab] = (0,
|
|
4148
|
+
const [activeTab, setActiveTab] = (0, import_react13.useState)(() => {
|
|
4109
4149
|
const t = searchParams.get("tab");
|
|
4110
4150
|
return isPageTab(t) ? t : "overview";
|
|
4111
4151
|
});
|
|
4112
|
-
const [generateDialogOpen, setGenerateDialogOpen] = (0,
|
|
4152
|
+
const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react13.useState)(
|
|
4113
4153
|
() => searchParams.get("generate") === "1"
|
|
4114
4154
|
);
|
|
4115
|
-
(0,
|
|
4155
|
+
(0, import_react13.useEffect)(() => {
|
|
4116
4156
|
if (searchParams.get("generate") !== "1") return;
|
|
4117
4157
|
setGenerateDialogOpen(true);
|
|
4118
4158
|
setSearchParams(
|
|
@@ -4124,7 +4164,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4124
4164
|
{ replace: true }
|
|
4125
4165
|
);
|
|
4126
4166
|
}, [searchParams, setSearchParams]);
|
|
4127
|
-
const selectTab = (0,
|
|
4167
|
+
const selectTab = (0, import_react13.useCallback)(
|
|
4128
4168
|
(tab) => {
|
|
4129
4169
|
setActiveTab(tab);
|
|
4130
4170
|
setSearchParams(
|
|
@@ -4138,12 +4178,12 @@ var init_LiteLLMPage = __esm({
|
|
|
4138
4178
|
},
|
|
4139
4179
|
[setSearchParams]
|
|
4140
4180
|
);
|
|
4141
|
-
const [snackbar, setSnackbar] = (0,
|
|
4142
|
-
const [manageTeam, setManageTeam] = (0,
|
|
4143
|
-
const [keyToEdit, setKeyToEdit] = (0,
|
|
4181
|
+
const [snackbar, setSnackbar] = (0, import_react13.useState)(null);
|
|
4182
|
+
const [manageTeam, setManageTeam] = (0, import_react13.useState)(null);
|
|
4183
|
+
const [keyToEdit, setKeyToEdit] = (0, import_react13.useState)(null);
|
|
4144
4184
|
const keyFormOpen = generateDialogOpen || !!keyToEdit;
|
|
4145
|
-
const [teamUsageCache, setTeamUsageCache] = (0,
|
|
4146
|
-
const [teamUsageLoading, setTeamUsageLoading] = (0,
|
|
4185
|
+
const [teamUsageCache, setTeamUsageCache] = (0, import_react13.useState)({});
|
|
4186
|
+
const [teamUsageLoading, setTeamUsageLoading] = (0, import_react13.useState)({});
|
|
4147
4187
|
const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
|
|
4148
4188
|
() => api.getUserInfo(),
|
|
4149
4189
|
[api]
|
|
@@ -4187,7 +4227,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4187
4227
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
4188
4228
|
[api, objectPermsEnabled]
|
|
4189
4229
|
);
|
|
4190
|
-
const teams = (0,
|
|
4230
|
+
const teams = (0, import_react13.useMemo)(() => {
|
|
4191
4231
|
if (!allTeams?.length) return [];
|
|
4192
4232
|
if (!userInfo) return allTeams;
|
|
4193
4233
|
const userId = userInfo.user_id;
|
|
@@ -4204,7 +4244,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4204
4244
|
const endDate = dateRange.end.toISOString().split("T")[0];
|
|
4205
4245
|
return api.getUsage(startDate, endDate);
|
|
4206
4246
|
}, [api, dateRange]);
|
|
4207
|
-
const handleDateRangeChange = (0,
|
|
4247
|
+
const handleDateRangeChange = (0, import_react13.useCallback)((range, preset) => {
|
|
4208
4248
|
setDateRange(range);
|
|
4209
4249
|
if (preset) {
|
|
4210
4250
|
setCurrentPreset(preset);
|
|
@@ -4212,7 +4252,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4212
4252
|
setTeamUsageCache({});
|
|
4213
4253
|
setTeamUsageLoading({});
|
|
4214
4254
|
}, [setDateRange, setCurrentPreset]);
|
|
4215
|
-
const loadTeamUsage = (0,
|
|
4255
|
+
const loadTeamUsage = (0, import_react13.useCallback)(async (teamId) => {
|
|
4216
4256
|
if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
|
|
4217
4257
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
|
|
4218
4258
|
try {
|
|
@@ -4226,7 +4266,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4226
4266
|
setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
|
|
4227
4267
|
}
|
|
4228
4268
|
}, [api, dateRange, teamUsageCache, teamUsageLoading]);
|
|
4229
|
-
const allowedModels = (0,
|
|
4269
|
+
const allowedModels = (0, import_react13.useMemo)(() => {
|
|
4230
4270
|
if (!allModels?.length) return [];
|
|
4231
4271
|
const userModels = userInfo?.models;
|
|
4232
4272
|
const teamModels = teams?.flatMap((t) => t.models ?? []);
|
|
@@ -4239,7 +4279,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4239
4279
|
]);
|
|
4240
4280
|
return allModels.filter((m) => allowed.has(m.model_name));
|
|
4241
4281
|
}, [allModels, userInfo, teams]);
|
|
4242
|
-
const handleGenerateKey = (0,
|
|
4282
|
+
const handleGenerateKey = (0, import_react13.useCallback)(
|
|
4243
4283
|
async (request) => {
|
|
4244
4284
|
try {
|
|
4245
4285
|
const response = await api.generateKey(request);
|
|
@@ -4253,7 +4293,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4253
4293
|
},
|
|
4254
4294
|
[api, refreshKeys]
|
|
4255
4295
|
);
|
|
4256
|
-
const handleUpdateKey = (0,
|
|
4296
|
+
const handleUpdateKey = (0, import_react13.useCallback)(
|
|
4257
4297
|
async (keyId, request) => {
|
|
4258
4298
|
try {
|
|
4259
4299
|
await api.updateKey(keyId, request);
|
|
@@ -4266,7 +4306,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4266
4306
|
},
|
|
4267
4307
|
[api, refreshKeys]
|
|
4268
4308
|
);
|
|
4269
|
-
const handleBlockKey = (0,
|
|
4309
|
+
const handleBlockKey = (0, import_react13.useCallback)(
|
|
4270
4310
|
async (keyId) => {
|
|
4271
4311
|
try {
|
|
4272
4312
|
await api.blockKey(keyId);
|
|
@@ -4278,7 +4318,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4278
4318
|
},
|
|
4279
4319
|
[api, refreshKeys]
|
|
4280
4320
|
);
|
|
4281
|
-
const handleUnblockKey = (0,
|
|
4321
|
+
const handleUnblockKey = (0, import_react13.useCallback)(
|
|
4282
4322
|
async (keyId) => {
|
|
4283
4323
|
try {
|
|
4284
4324
|
await api.unblockKey(keyId);
|
|
@@ -4290,7 +4330,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4290
4330
|
},
|
|
4291
4331
|
[api, refreshKeys]
|
|
4292
4332
|
);
|
|
4293
|
-
const handleResetKeySpend = (0,
|
|
4333
|
+
const handleResetKeySpend = (0, import_react13.useCallback)(
|
|
4294
4334
|
async (keyId) => {
|
|
4295
4335
|
try {
|
|
4296
4336
|
await api.resetKeySpend(keyId);
|
|
@@ -4302,7 +4342,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4302
4342
|
},
|
|
4303
4343
|
[api, refreshKeys]
|
|
4304
4344
|
);
|
|
4305
|
-
const handleDeleteKey = (0,
|
|
4345
|
+
const handleDeleteKey = (0, import_react13.useCallback)(
|
|
4306
4346
|
async (keyId) => {
|
|
4307
4347
|
try {
|
|
4308
4348
|
await api.deleteKey(keyId);
|
|
@@ -4319,7 +4359,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4319
4359
|
},
|
|
4320
4360
|
[api, refreshKeys]
|
|
4321
4361
|
);
|
|
4322
|
-
const handlePruneExpiredKeys = (0,
|
|
4362
|
+
const handlePruneExpiredKeys = (0, import_react13.useCallback)(
|
|
4323
4363
|
async () => {
|
|
4324
4364
|
try {
|
|
4325
4365
|
const result = await api.pruneExpiredKeys();
|
|
@@ -4334,14 +4374,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4334
4374
|
);
|
|
4335
4375
|
const isInitialLoading = userLoading && !userInfo;
|
|
4336
4376
|
if (isInitialLoading) {
|
|
4337
|
-
return /* @__PURE__ */
|
|
4377
|
+
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
4378
|
}
|
|
4339
4379
|
if (userError || !userInfo) {
|
|
4340
4380
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
4341
4381
|
const hint = userError?.body?.hint;
|
|
4342
|
-
return /* @__PURE__ */
|
|
4382
|
+
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
4383
|
}
|
|
4344
|
-
const pageTabs = /* @__PURE__ */
|
|
4384
|
+
const pageTabs = /* @__PURE__ */ import_react13.default.createElement(
|
|
4345
4385
|
import_Tabs2.default,
|
|
4346
4386
|
{
|
|
4347
4387
|
value: activeTab,
|
|
@@ -4354,13 +4394,13 @@ var init_LiteLLMPage = __esm({
|
|
|
4354
4394
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
4355
4395
|
}
|
|
4356
4396
|
},
|
|
4357
|
-
/* @__PURE__ */
|
|
4358
|
-
/* @__PURE__ */
|
|
4359
|
-
/* @__PURE__ */
|
|
4360
|
-
/* @__PURE__ */
|
|
4361
|
-
userInfo.can_view_audit && /* @__PURE__ */
|
|
4397
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Overview", value: "overview" }),
|
|
4398
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Keys", value: "keys" }),
|
|
4399
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Teams", value: "teams" }),
|
|
4400
|
+
/* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Models", value: "models" }),
|
|
4401
|
+
userInfo.can_view_audit && /* @__PURE__ */ import_react13.default.createElement(import_Tab2.default, { label: "Audit Log", value: "audit" })
|
|
4362
4402
|
);
|
|
4363
|
-
return /* @__PURE__ */
|
|
4403
|
+
return /* @__PURE__ */ import_react13.default.createElement(import_Box12.default, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ import_react13.default.createElement(
|
|
4364
4404
|
DashboardHeader,
|
|
4365
4405
|
{
|
|
4366
4406
|
userInfo,
|
|
@@ -4370,7 +4410,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4370
4410
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4371
4411
|
tabs: pageTabs
|
|
4372
4412
|
}
|
|
4373
|
-
), activeTab === "overview" && /* @__PURE__ */
|
|
4413
|
+
), 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
4414
|
UsageStats,
|
|
4375
4415
|
{
|
|
4376
4416
|
usage: usage ?? null,
|
|
@@ -4381,7 +4421,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4381
4421
|
loading: usageLoading,
|
|
4382
4422
|
userInfo
|
|
4383
4423
|
}
|
|
4384
|
-
)), /* @__PURE__ */
|
|
4424
|
+
)), /* @__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
4425
|
KeysTable,
|
|
4386
4426
|
{
|
|
4387
4427
|
keys: keys ?? [],
|
|
@@ -4398,7 +4438,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4398
4438
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
4399
4439
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
4400
4440
|
const visibleTeams = Array.from(teamsById.values());
|
|
4401
|
-
return /* @__PURE__ */
|
|
4441
|
+
return /* @__PURE__ */ import_react13.default.createElement(
|
|
4402
4442
|
TeamUsage,
|
|
4403
4443
|
{
|
|
4404
4444
|
teams: visibleTeams,
|
|
@@ -4414,14 +4454,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4414
4454
|
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
4415
4455
|
}
|
|
4416
4456
|
);
|
|
4417
|
-
})(), activeTab === "models" && /* @__PURE__ */
|
|
4457
|
+
})(), activeTab === "models" && /* @__PURE__ */ import_react13.default.createElement(
|
|
4418
4458
|
ModelsTable,
|
|
4419
4459
|
{
|
|
4420
4460
|
allModels: allModels ?? [],
|
|
4421
4461
|
teams: teams ?? [],
|
|
4422
4462
|
loading: modelsLoading
|
|
4423
4463
|
}
|
|
4424
|
-
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */
|
|
4464
|
+
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ import_react13.default.createElement(AuditLog, { api }), /* @__PURE__ */ import_react13.default.createElement(
|
|
4425
4465
|
KeyFormDialog,
|
|
4426
4466
|
{
|
|
4427
4467
|
open: keyFormOpen,
|
|
@@ -4441,7 +4481,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4441
4481
|
onResetKeySpend: handleResetKeySpend,
|
|
4442
4482
|
onGetConfig: () => api.getConfig()
|
|
4443
4483
|
}
|
|
4444
|
-
), /* @__PURE__ */
|
|
4484
|
+
), /* @__PURE__ */ import_react13.default.createElement(
|
|
4445
4485
|
ManageTeamDialog,
|
|
4446
4486
|
{
|
|
4447
4487
|
open: !!manageTeam,
|
|
@@ -4503,7 +4543,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4503
4543
|
refreshTeams();
|
|
4504
4544
|
}
|
|
4505
4545
|
}
|
|
4506
|
-
), /* @__PURE__ */
|
|
4546
|
+
), /* @__PURE__ */ import_react13.default.createElement(
|
|
4507
4547
|
import_Snackbar2.default,
|
|
4508
4548
|
{
|
|
4509
4549
|
open: !!snackbar,
|
|
@@ -4511,7 +4551,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4511
4551
|
onClose: () => setSnackbar(null),
|
|
4512
4552
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
4513
4553
|
},
|
|
4514
|
-
snackbar ? /* @__PURE__ */
|
|
4554
|
+
snackbar ? /* @__PURE__ */ import_react13.default.createElement(import_Alert6.default, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
4515
4555
|
));
|
|
4516
4556
|
};
|
|
4517
4557
|
}
|
|
@@ -4521,6 +4561,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4521
4561
|
var index_exports = {};
|
|
4522
4562
|
__export(index_exports, {
|
|
4523
4563
|
DashboardHeader: () => DashboardHeader,
|
|
4564
|
+
GenerateKeyButton: () => GenerateKeyButton,
|
|
4524
4565
|
GenerateKeyDialog: () => GenerateKeyDialog,
|
|
4525
4566
|
KeyFormDialog: () => KeyFormDialog,
|
|
4526
4567
|
KeysTable: () => KeysTable,
|
|
@@ -4543,7 +4584,7 @@ __export(index_exports, {
|
|
|
4543
4584
|
module.exports = __toCommonJS(index_exports);
|
|
4544
4585
|
|
|
4545
4586
|
// src/plugin.tsx
|
|
4546
|
-
var
|
|
4587
|
+
var import_react14 = __toESM(require("react"));
|
|
4547
4588
|
var import_icons_material9 = require("@mui/icons-material");
|
|
4548
4589
|
var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
|
|
4549
4590
|
init_api();
|
|
@@ -4558,10 +4599,10 @@ var liteLlmPage = import_frontend_plugin_api.PageBlueprint.make({
|
|
|
4558
4599
|
params: {
|
|
4559
4600
|
path: "/litellm",
|
|
4560
4601
|
title: "LiteLLM",
|
|
4561
|
-
icon: /* @__PURE__ */
|
|
4602
|
+
icon: /* @__PURE__ */ import_react14.default.createElement(import_icons_material9.TrendingUp, null),
|
|
4562
4603
|
loader: async () => {
|
|
4563
4604
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
4564
|
-
return /* @__PURE__ */
|
|
4605
|
+
return /* @__PURE__ */ import_react14.default.createElement(LiteLLMPage2, null);
|
|
4565
4606
|
}
|
|
4566
4607
|
}
|
|
4567
4608
|
});
|
|
@@ -4578,7 +4619,7 @@ init_UsageStats();
|
|
|
4578
4619
|
init_TeamUsage();
|
|
4579
4620
|
|
|
4580
4621
|
// src/components/LiteLLMHomeWidget.tsx
|
|
4581
|
-
var
|
|
4622
|
+
var import_react15 = __toESM(require("react"));
|
|
4582
4623
|
var import_Paper9 = __toESM(require("@mui/material/Paper"));
|
|
4583
4624
|
var import_Box13 = __toESM(require("@mui/material/Box"));
|
|
4584
4625
|
var import_Typography13 = __toESM(require("@mui/material/Typography"));
|
|
@@ -4604,19 +4645,19 @@ function presetToDateRange(preset) {
|
|
|
4604
4645
|
}
|
|
4605
4646
|
return { start, end };
|
|
4606
4647
|
}
|
|
4607
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
4648
|
+
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
4649
|
var LiteLLMHomeWidget = ({
|
|
4609
4650
|
defaultPeriod = "7d",
|
|
4610
4651
|
title = "LiteLLM Usage"
|
|
4611
4652
|
}) => {
|
|
4612
4653
|
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,
|
|
4654
|
+
const [period, setPeriod] = (0, import_react15.useState)(defaultPeriod);
|
|
4655
|
+
const [loading, setLoading] = (0, import_react15.useState)(true);
|
|
4656
|
+
const [usageError, setUsageError] = (0, import_react15.useState)(null);
|
|
4657
|
+
const [keysError, setKeysError] = (0, import_react15.useState)(null);
|
|
4658
|
+
const [usage, setUsage] = (0, import_react15.useState)(null);
|
|
4659
|
+
const [keys, setKeys] = (0, import_react15.useState)([]);
|
|
4660
|
+
(0, import_react15.useEffect)(() => {
|
|
4620
4661
|
let cancelled = false;
|
|
4621
4662
|
setLoading(true);
|
|
4622
4663
|
setUsageError(null);
|
|
@@ -4653,17 +4694,17 @@ var LiteLLMHomeWidget = ({
|
|
|
4653
4694
|
spend: d.spend
|
|
4654
4695
|
}));
|
|
4655
4696
|
const hasSparkline = dailyData.length > 0;
|
|
4656
|
-
return /* @__PURE__ */
|
|
4697
|
+
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
4698
|
import_Select2.default,
|
|
4658
4699
|
{
|
|
4659
4700
|
value: period,
|
|
4660
4701
|
onChange: (e) => setPeriod(e.target.value),
|
|
4661
4702
|
displayEmpty: true
|
|
4662
4703
|
},
|
|
4663
|
-
/* @__PURE__ */
|
|
4664
|
-
/* @__PURE__ */
|
|
4665
|
-
/* @__PURE__ */
|
|
4666
|
-
))), loading && /* @__PURE__ */
|
|
4704
|
+
/* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
|
|
4705
|
+
/* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
|
|
4706
|
+
/* @__PURE__ */ import_react15.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
|
|
4707
|
+
))), 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
4708
|
import_recharts3.Area,
|
|
4668
4709
|
{
|
|
4669
4710
|
type: "monotone",
|
|
@@ -4681,7 +4722,7 @@ var LiteLLMHomeWidget = ({
|
|
|
4681
4722
|
init_LiteLLMBudgetWidget();
|
|
4682
4723
|
|
|
4683
4724
|
// src/components/LiteLLMBudgetGauges.tsx
|
|
4684
|
-
var
|
|
4725
|
+
var import_react16 = __toESM(require("react"));
|
|
4685
4726
|
var import_Paper10 = __toESM(require("@mui/material/Paper"));
|
|
4686
4727
|
var import_Box14 = __toESM(require("@mui/material/Box"));
|
|
4687
4728
|
var import_Button6 = __toESM(require("@mui/material/Button"));
|
|
@@ -4690,11 +4731,13 @@ var import_CircularProgress8 = __toESM(require("@mui/material/CircularProgress")
|
|
|
4690
4731
|
var import_Alert8 = __toESM(require("@mui/material/Alert"));
|
|
4691
4732
|
var import_Collapse4 = __toESM(require("@mui/material/Collapse"));
|
|
4692
4733
|
var import_icons_material10 = require("@mui/icons-material");
|
|
4734
|
+
var import_recharts4 = require("recharts");
|
|
4693
4735
|
var import_core_plugin_api6 = require("@backstage/core-plugin-api");
|
|
4694
|
-
var
|
|
4736
|
+
var import_core_components3 = require("@backstage/core-components");
|
|
4695
4737
|
init_api();
|
|
4696
4738
|
init_format();
|
|
4697
4739
|
init_ui();
|
|
4740
|
+
init_GenerateKeyButton();
|
|
4698
4741
|
init_BudgetLimitList();
|
|
4699
4742
|
init_budget();
|
|
4700
4743
|
var MODULE_PATH = "/litellm";
|
|
@@ -4705,7 +4748,7 @@ var LEVEL = {
|
|
|
4705
4748
|
};
|
|
4706
4749
|
var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
|
|
4707
4750
|
var CTA_LABELS = {
|
|
4708
|
-
"new-key": "New
|
|
4751
|
+
"new-key": "Generate New Key",
|
|
4709
4752
|
module: "Open module",
|
|
4710
4753
|
"all-limits": "All limits"
|
|
4711
4754
|
};
|
|
@@ -4731,7 +4774,7 @@ var LevelGauge = ({
|
|
|
4731
4774
|
const limit = gauge.limit;
|
|
4732
4775
|
const tone = limit ? budgetTone2(limit.pct) : "neutral";
|
|
4733
4776
|
const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
|
|
4734
|
-
return /* @__PURE__ */
|
|
4777
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4735
4778
|
import_Box14.default,
|
|
4736
4779
|
{
|
|
4737
4780
|
sx: {
|
|
@@ -4742,7 +4785,7 @@ var LevelGauge = ({
|
|
|
4742
4785
|
flex: 1
|
|
4743
4786
|
}
|
|
4744
4787
|
},
|
|
4745
|
-
/* @__PURE__ */
|
|
4788
|
+
/* @__PURE__ */ import_react16.default.createElement(
|
|
4746
4789
|
Gauge,
|
|
4747
4790
|
{
|
|
4748
4791
|
value: limit?.pct ?? 0,
|
|
@@ -4751,7 +4794,7 @@ var LevelGauge = ({
|
|
|
4751
4794
|
label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
|
|
4752
4795
|
}
|
|
4753
4796
|
),
|
|
4754
|
-
/* @__PURE__ */
|
|
4797
|
+
/* @__PURE__ */ import_react16.default.createElement(
|
|
4755
4798
|
import_Typography14.default,
|
|
4756
4799
|
{
|
|
4757
4800
|
sx: (theme) => ({
|
|
@@ -4765,7 +4808,7 @@ var LevelGauge = ({
|
|
|
4765
4808
|
},
|
|
4766
4809
|
name
|
|
4767
4810
|
),
|
|
4768
|
-
limit ? /* @__PURE__ */
|
|
4811
|
+
limit ? /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(
|
|
4769
4812
|
import_Typography14.default,
|
|
4770
4813
|
{
|
|
4771
4814
|
variant: "caption",
|
|
@@ -4781,7 +4824,7 @@ var LevelGauge = ({
|
|
|
4781
4824
|
}
|
|
4782
4825
|
},
|
|
4783
4826
|
limit.label
|
|
4784
|
-
), /* @__PURE__ */
|
|
4827
|
+
), /* @__PURE__ */ import_react16.default.createElement(
|
|
4785
4828
|
import_Typography14.default,
|
|
4786
4829
|
{
|
|
4787
4830
|
variant: "caption",
|
|
@@ -4793,10 +4836,10 @@ var LevelGauge = ({
|
|
|
4793
4836
|
}
|
|
4794
4837
|
},
|
|
4795
4838
|
spendCaption(limit)
|
|
4796
|
-
), extra > 0 ? /* @__PURE__ */
|
|
4839
|
+
), extra > 0 ? /* @__PURE__ */ import_react16.default.createElement(
|
|
4797
4840
|
import_Typography14.default,
|
|
4798
4841
|
{
|
|
4799
|
-
component:
|
|
4842
|
+
component: import_core_components3.Link,
|
|
4800
4843
|
to: keysHref,
|
|
4801
4844
|
variant: "caption",
|
|
4802
4845
|
sx: {
|
|
@@ -4809,7 +4852,7 @@ var LevelGauge = ({
|
|
|
4809
4852
|
"+",
|
|
4810
4853
|
extra,
|
|
4811
4854
|
" more"
|
|
4812
|
-
) : /* @__PURE__ */
|
|
4855
|
+
) : /* @__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
4856
|
import_Typography14.default,
|
|
4814
4857
|
{
|
|
4815
4858
|
variant: "caption",
|
|
@@ -4820,6 +4863,125 @@ var LevelGauge = ({
|
|
|
4820
4863
|
)
|
|
4821
4864
|
);
|
|
4822
4865
|
};
|
|
4866
|
+
function monthToDateRange(now = /* @__PURE__ */ new Date()) {
|
|
4867
|
+
const toDay = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
|
|
4868
|
+
d.getDate()
|
|
4869
|
+
).padStart(2, "0")}`;
|
|
4870
|
+
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
4871
|
+
return { startDate: toDay(start), endDate: toDay(now) };
|
|
4872
|
+
}
|
|
4873
|
+
var UsageMiniChart = ({ data, totalTokens, loading, height }) => {
|
|
4874
|
+
const renderPlot = () => {
|
|
4875
|
+
if (loading) {
|
|
4876
|
+
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 }));
|
|
4877
|
+
}
|
|
4878
|
+
if (data.length === 0) {
|
|
4879
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4880
|
+
import_Box14.default,
|
|
4881
|
+
{
|
|
4882
|
+
display: "flex",
|
|
4883
|
+
justifyContent: "center",
|
|
4884
|
+
alignItems: "center",
|
|
4885
|
+
height: "100%",
|
|
4886
|
+
sx: (theme) => ({ color: theme.palette.text.secondary, fontSize: 18 })
|
|
4887
|
+
},
|
|
4888
|
+
"\u2014"
|
|
4889
|
+
);
|
|
4890
|
+
}
|
|
4891
|
+
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(
|
|
4892
|
+
import_recharts4.Area,
|
|
4893
|
+
{
|
|
4894
|
+
type: "monotone",
|
|
4895
|
+
dataKey: "input",
|
|
4896
|
+
name: "Input",
|
|
4897
|
+
stackId: "tok",
|
|
4898
|
+
stroke: SERIES.input,
|
|
4899
|
+
strokeWidth: 1.5,
|
|
4900
|
+
fill: SERIES.input,
|
|
4901
|
+
fillOpacity: 0.25,
|
|
4902
|
+
dot: false,
|
|
4903
|
+
isAnimationActive: false
|
|
4904
|
+
}
|
|
4905
|
+
), /* @__PURE__ */ import_react16.default.createElement(
|
|
4906
|
+
import_recharts4.Area,
|
|
4907
|
+
{
|
|
4908
|
+
type: "monotone",
|
|
4909
|
+
dataKey: "output",
|
|
4910
|
+
name: "Output",
|
|
4911
|
+
stackId: "tok",
|
|
4912
|
+
stroke: SERIES.output,
|
|
4913
|
+
strokeWidth: 1.5,
|
|
4914
|
+
fill: SERIES.output,
|
|
4915
|
+
fillOpacity: 0.25,
|
|
4916
|
+
dot: false,
|
|
4917
|
+
isAnimationActive: false
|
|
4918
|
+
}
|
|
4919
|
+
)));
|
|
4920
|
+
};
|
|
4921
|
+
const renderCaption = () => {
|
|
4922
|
+
if (loading) {
|
|
4923
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { mt: 0.25, fontSize: 11 } }, "\u2026");
|
|
4924
|
+
}
|
|
4925
|
+
if (data.length === 0) {
|
|
4926
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4927
|
+
import_Typography14.default,
|
|
4928
|
+
{
|
|
4929
|
+
variant: "caption",
|
|
4930
|
+
color: "text.secondary",
|
|
4931
|
+
sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
|
|
4932
|
+
},
|
|
4933
|
+
"No usage this month"
|
|
4934
|
+
);
|
|
4935
|
+
}
|
|
4936
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement(
|
|
4937
|
+
import_Typography14.default,
|
|
4938
|
+
{
|
|
4939
|
+
variant: "caption",
|
|
4940
|
+
title: `${fmtInt(totalTokens)} tokens month to date`,
|
|
4941
|
+
sx: {
|
|
4942
|
+
mt: 0.25,
|
|
4943
|
+
fontSize: 11,
|
|
4944
|
+
fontWeight: 600,
|
|
4945
|
+
maxWidth: "100%",
|
|
4946
|
+
overflow: "hidden",
|
|
4947
|
+
textOverflow: "ellipsis",
|
|
4948
|
+
whiteSpace: "nowrap",
|
|
4949
|
+
fontVariantNumeric: "tabular-nums"
|
|
4950
|
+
}
|
|
4951
|
+
},
|
|
4952
|
+
fmtCompact(totalTokens),
|
|
4953
|
+
" tokens"
|
|
4954
|
+
), /* @__PURE__ */ import_react16.default.createElement(import_Typography14.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, "month to date"));
|
|
4955
|
+
};
|
|
4956
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4957
|
+
import_Box14.default,
|
|
4958
|
+
{
|
|
4959
|
+
sx: {
|
|
4960
|
+
display: "flex",
|
|
4961
|
+
flexDirection: "column",
|
|
4962
|
+
alignItems: "center",
|
|
4963
|
+
minWidth: 0,
|
|
4964
|
+
flex: 1
|
|
4965
|
+
}
|
|
4966
|
+
},
|
|
4967
|
+
/* @__PURE__ */ import_react16.default.createElement(import_Box14.default, { sx: { height, width: "100%" } }, renderPlot()),
|
|
4968
|
+
/* @__PURE__ */ import_react16.default.createElement(
|
|
4969
|
+
import_Typography14.default,
|
|
4970
|
+
{
|
|
4971
|
+
sx: (theme) => ({
|
|
4972
|
+
mt: 0.75,
|
|
4973
|
+
fontSize: 10.5,
|
|
4974
|
+
fontWeight: 700,
|
|
4975
|
+
letterSpacing: "0.08em",
|
|
4976
|
+
textTransform: "uppercase",
|
|
4977
|
+
color: theme.palette.text.secondary
|
|
4978
|
+
})
|
|
4979
|
+
},
|
|
4980
|
+
"Usage"
|
|
4981
|
+
),
|
|
4982
|
+
renderCaption()
|
|
4983
|
+
);
|
|
4984
|
+
};
|
|
4823
4985
|
var LiteLLMBudgetGauges = ({
|
|
4824
4986
|
title = "Budget",
|
|
4825
4987
|
size = 72,
|
|
@@ -4834,60 +4996,83 @@ var LiteLLMBudgetGauges = ({
|
|
|
4834
4996
|
action
|
|
4835
4997
|
}) => {
|
|
4836
4998
|
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,
|
|
4999
|
+
const [loading, setLoading] = (0, import_react16.useState)(true);
|
|
5000
|
+
const [error, setError] = (0, import_react16.useState)(null);
|
|
5001
|
+
const [user, setUser] = (0, import_react16.useState)(null);
|
|
5002
|
+
const [teams, setTeams] = (0, import_react16.useState)([]);
|
|
5003
|
+
const [keys, setKeys] = (0, import_react16.useState)([]);
|
|
5004
|
+
const [usageLoading, setUsageLoading] = (0, import_react16.useState)(true);
|
|
5005
|
+
const [dailyTokens, setDailyTokens] = (0, import_react16.useState)([]);
|
|
5006
|
+
const [mtdTokens, setMtdTokens] = (0, import_react16.useState)(0);
|
|
4842
5007
|
const isControlled = expanded !== void 0;
|
|
4843
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
5008
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react16.useState)(defaultExpanded);
|
|
4844
5009
|
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
4845
|
-
const toggleExpanded = (0,
|
|
5010
|
+
const toggleExpanded = (0, import_react16.useCallback)(() => {
|
|
4846
5011
|
const next = !isExpanded;
|
|
4847
5012
|
if (!isControlled) setUncontrolledExpanded(next);
|
|
4848
5013
|
onExpandedChange?.(next);
|
|
4849
5014
|
}, [isControlled, isExpanded, onExpandedChange]);
|
|
4850
5015
|
const keysTabHref = keysHref ?? `${moduleHref}?tab=keys`;
|
|
4851
|
-
(0,
|
|
5016
|
+
(0, import_react16.useEffect)(() => {
|
|
4852
5017
|
let cancelled = false;
|
|
4853
5018
|
setLoading(true);
|
|
5019
|
+
setUsageLoading(true);
|
|
4854
5020
|
setError(null);
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
5021
|
+
const { startDate, endDate } = monthToDateRange();
|
|
5022
|
+
Promise.allSettled([
|
|
5023
|
+
api.getUserInfo(),
|
|
5024
|
+
api.getTeams(),
|
|
5025
|
+
api.listKeys(),
|
|
5026
|
+
api.getUsage(startDate, endDate)
|
|
5027
|
+
]).then(([userResult, teamsResult, keysResult, usageResult]) => {
|
|
5028
|
+
if (cancelled) return;
|
|
5029
|
+
if (userResult.status === "fulfilled") {
|
|
5030
|
+
setUser(userResult.value);
|
|
5031
|
+
} else {
|
|
5032
|
+
setUser(null);
|
|
5033
|
+
setError(
|
|
5034
|
+
userResult.reason?.message ?? "Failed to load your LiteLLM profile"
|
|
5035
|
+
);
|
|
4869
5036
|
}
|
|
4870
|
-
|
|
5037
|
+
setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
|
|
5038
|
+
setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
|
|
5039
|
+
setLoading(false);
|
|
5040
|
+
if (usageResult.status === "fulfilled") {
|
|
5041
|
+
const daily = usageResult.value.daily_usage ?? [];
|
|
5042
|
+
setDailyTokens(
|
|
5043
|
+
daily.map((d) => ({
|
|
5044
|
+
date: d.date,
|
|
5045
|
+
input: d.prompt_tokens,
|
|
5046
|
+
output: d.completion_tokens
|
|
5047
|
+
}))
|
|
5048
|
+
);
|
|
5049
|
+
setMtdTokens(usageResult.value.total_tokens ?? 0);
|
|
5050
|
+
} else {
|
|
5051
|
+
setDailyTokens([]);
|
|
5052
|
+
setMtdTokens(0);
|
|
5053
|
+
}
|
|
5054
|
+
setUsageLoading(false);
|
|
5055
|
+
});
|
|
4871
5056
|
return () => {
|
|
4872
5057
|
cancelled = true;
|
|
4873
5058
|
};
|
|
4874
5059
|
}, [api]);
|
|
4875
|
-
const summary = (0,
|
|
5060
|
+
const summary = (0, import_react16.useMemo)(
|
|
4876
5061
|
() => buildBudgetSummary(user, teams, keys, keys.length),
|
|
4877
5062
|
[user, teams, keys]
|
|
4878
5063
|
);
|
|
4879
|
-
const gauges = (0,
|
|
4880
|
-
const headline = (0,
|
|
4881
|
-
const expandedSummary = (0,
|
|
5064
|
+
const gauges = (0, import_react16.useMemo)(() => buildBudgetGauges(summary), [summary]);
|
|
5065
|
+
const headline = (0, import_react16.useMemo)(() => budgetHeadline(summary), [summary]);
|
|
5066
|
+
const expandedSummary = (0, import_react16.useMemo)(
|
|
4882
5067
|
() => buildBudgetSummary(user, teams, keys, maxExpandedKeys),
|
|
4883
5068
|
[user, teams, keys, maxExpandedKeys]
|
|
4884
5069
|
);
|
|
4885
|
-
const expandedLimits = (0,
|
|
5070
|
+
const expandedLimits = (0, import_react16.useMemo)(
|
|
4886
5071
|
() => allBudgetLimits(expandedSummary),
|
|
4887
5072
|
[expandedSummary]
|
|
4888
5073
|
);
|
|
4889
5074
|
const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
|
|
4890
|
-
const resolvedCtas = (0,
|
|
5075
|
+
const resolvedCtas = (0, import_react16.useMemo)(() => {
|
|
4891
5076
|
const list = ctas ?? DEFAULT_CTAS;
|
|
4892
5077
|
return list.map((cta) => typeof cta === "string" ? { kind: cta } : cta).filter((cta) => cta.kind !== "all-limits" || headline.count > 0);
|
|
4893
5078
|
}, [ctas, headline.count]);
|
|
@@ -4896,28 +5081,18 @@ var LiteLLMBudgetGauges = ({
|
|
|
4896
5081
|
const key = `${cta.kind}-${index}`;
|
|
4897
5082
|
switch (cta.kind) {
|
|
4898
5083
|
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
|
-
);
|
|
5084
|
+
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
5085
|
case "module":
|
|
4911
|
-
return /* @__PURE__ */
|
|
5086
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_Button6.default, { key, size: "small", variant: "outlined", component: import_core_components3.Link, to: moduleHref }, label);
|
|
4912
5087
|
case "all-limits":
|
|
4913
|
-
return /* @__PURE__ */
|
|
5088
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
4914
5089
|
import_Button6.default,
|
|
4915
5090
|
{
|
|
4916
5091
|
key,
|
|
4917
5092
|
size: "small",
|
|
4918
5093
|
variant: "text",
|
|
4919
5094
|
onClick: toggleExpanded,
|
|
4920
|
-
endIcon: /* @__PURE__ */
|
|
5095
|
+
endIcon: /* @__PURE__ */ import_react16.default.createElement(
|
|
4921
5096
|
import_icons_material10.ExpandMore,
|
|
4922
5097
|
{
|
|
4923
5098
|
sx: {
|
|
@@ -4935,14 +5110,22 @@ var LiteLLMBudgetGauges = ({
|
|
|
4935
5110
|
}
|
|
4936
5111
|
};
|
|
4937
5112
|
const hasFooter = resolvedCtas.length > 0 || !!action;
|
|
4938
|
-
return /* @__PURE__ */
|
|
5113
|
+
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
5114
|
StatusPill,
|
|
4940
5115
|
{
|
|
4941
5116
|
label: summaryText,
|
|
4942
5117
|
tone: budgetTone2(headline.closest?.pct ?? 0),
|
|
4943
5118
|
dot: headline.count > 0
|
|
4944
5119
|
}
|
|
4945
|
-
)), loading && /* @__PURE__ */
|
|
5120
|
+
)), 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(
|
|
5121
|
+
UsageMiniChart,
|
|
5122
|
+
{
|
|
5123
|
+
data: dailyTokens,
|
|
5124
|
+
totalTokens: mtdTokens,
|
|
5125
|
+
loading: usageLoading,
|
|
5126
|
+
height: size
|
|
5127
|
+
}
|
|
5128
|
+
)), /* @__PURE__ */ import_react16.default.createElement(import_Collapse4.default, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ import_react16.default.createElement(
|
|
4946
5129
|
import_Box14.default,
|
|
4947
5130
|
{
|
|
4948
5131
|
sx: {
|
|
@@ -4953,7 +5136,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4953
5136
|
overflowY: "auto"
|
|
4954
5137
|
}
|
|
4955
5138
|
},
|
|
4956
|
-
/* @__PURE__ */
|
|
5139
|
+
/* @__PURE__ */ import_react16.default.createElement(LimitListPanel, null, /* @__PURE__ */ import_react16.default.createElement(
|
|
4957
5140
|
BudgetLimitList,
|
|
4958
5141
|
{
|
|
4959
5142
|
limits: expandedLimits,
|
|
@@ -4961,7 +5144,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4961
5144
|
keysHref: keysTabHref
|
|
4962
5145
|
}
|
|
4963
5146
|
))
|
|
4964
|
-
))), hasFooter && /* @__PURE__ */
|
|
5147
|
+
))), hasFooter && /* @__PURE__ */ import_react16.default.createElement(
|
|
4965
5148
|
import_Box14.default,
|
|
4966
5149
|
{
|
|
4967
5150
|
sx: (theme) => ({
|
|
@@ -4981,11 +5164,12 @@ var LiteLLMBudgetGauges = ({
|
|
|
4981
5164
|
|
|
4982
5165
|
// src/index.ts
|
|
4983
5166
|
init_KeyFormDialog();
|
|
5167
|
+
init_GenerateKeyButton();
|
|
4984
5168
|
|
|
4985
5169
|
// src/components/GenerateKeyDialog.tsx
|
|
4986
|
-
var
|
|
5170
|
+
var import_react17 = __toESM(require("react"));
|
|
4987
5171
|
init_KeyFormDialog();
|
|
4988
|
-
var GenerateKeyDialog = (props) => /* @__PURE__ */
|
|
5172
|
+
var GenerateKeyDialog = (props) => /* @__PURE__ */ import_react17.default.createElement(
|
|
4989
5173
|
KeyFormDialog,
|
|
4990
5174
|
{
|
|
4991
5175
|
open: props.open,
|