@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.esm.js
CHANGED
|
@@ -836,16 +836,79 @@ var init_ui = __esm({
|
|
|
836
836
|
}
|
|
837
837
|
});
|
|
838
838
|
|
|
839
|
+
// src/components/GenerateKeyButton.tsx
|
|
840
|
+
import React2 from "react";
|
|
841
|
+
import Button from "@mui/material/Button";
|
|
842
|
+
import { Add } from "@mui/icons-material";
|
|
843
|
+
import { Link } from "@backstage/core-components";
|
|
844
|
+
var GenerateKeyButton;
|
|
845
|
+
var init_GenerateKeyButton = __esm({
|
|
846
|
+
"src/components/GenerateKeyButton.tsx"() {
|
|
847
|
+
"use strict";
|
|
848
|
+
GenerateKeyButton = ({
|
|
849
|
+
to,
|
|
850
|
+
onClick,
|
|
851
|
+
label = "Generate New Key",
|
|
852
|
+
size,
|
|
853
|
+
fullWidth = false,
|
|
854
|
+
disabled = false
|
|
855
|
+
}) => {
|
|
856
|
+
if (onClick && !disabled) {
|
|
857
|
+
return /* @__PURE__ */ React2.createElement(
|
|
858
|
+
Button,
|
|
859
|
+
{
|
|
860
|
+
variant: "contained",
|
|
861
|
+
color: "primary",
|
|
862
|
+
disableElevation: true,
|
|
863
|
+
size,
|
|
864
|
+
fullWidth,
|
|
865
|
+
startIcon: /* @__PURE__ */ React2.createElement(Add, null),
|
|
866
|
+
onClick
|
|
867
|
+
},
|
|
868
|
+
label
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
if (to && !disabled) {
|
|
872
|
+
return /* @__PURE__ */ React2.createElement(
|
|
873
|
+
Button,
|
|
874
|
+
{
|
|
875
|
+
variant: "contained",
|
|
876
|
+
color: "primary",
|
|
877
|
+
disableElevation: true,
|
|
878
|
+
size,
|
|
879
|
+
fullWidth,
|
|
880
|
+
startIcon: /* @__PURE__ */ React2.createElement(Add, null),
|
|
881
|
+
component: Link,
|
|
882
|
+
to
|
|
883
|
+
},
|
|
884
|
+
label
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
return /* @__PURE__ */ React2.createElement(
|
|
888
|
+
Button,
|
|
889
|
+
{
|
|
890
|
+
variant: "contained",
|
|
891
|
+
color: "primary",
|
|
892
|
+
disableElevation: true,
|
|
893
|
+
size,
|
|
894
|
+
fullWidth,
|
|
895
|
+
startIcon: /* @__PURE__ */ React2.createElement(Add, null),
|
|
896
|
+
disabled: true
|
|
897
|
+
},
|
|
898
|
+
label
|
|
899
|
+
);
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
|
|
839
904
|
// src/components/DashboardHeader.tsx
|
|
840
|
-
import
|
|
905
|
+
import React3, { useMemo } from "react";
|
|
841
906
|
import Box2 from "@mui/material/Box";
|
|
842
907
|
import Typography2 from "@mui/material/Typography";
|
|
843
908
|
import Skeleton from "@mui/material/Skeleton";
|
|
844
909
|
import Paper2 from "@mui/material/Paper";
|
|
845
|
-
import Button from "@mui/material/Button";
|
|
846
910
|
import Divider from "@mui/material/Divider";
|
|
847
911
|
import { alpha as alpha2 } from "@mui/material/styles";
|
|
848
|
-
import { Add } from "@mui/icons-material";
|
|
849
912
|
function initials(name) {
|
|
850
913
|
const local = name.split("@")[0] ?? name;
|
|
851
914
|
const parts = local.split(/[.\-_\s]+/).filter(Boolean);
|
|
@@ -858,9 +921,10 @@ var init_DashboardHeader = __esm({
|
|
|
858
921
|
"use strict";
|
|
859
922
|
init_api();
|
|
860
923
|
init_ui();
|
|
924
|
+
init_GenerateKeyButton();
|
|
861
925
|
CountStat = ({ value, label, tone }) => {
|
|
862
926
|
const active = value > 0;
|
|
863
|
-
return /* @__PURE__ */
|
|
927
|
+
return /* @__PURE__ */ React3.createElement(Box2, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ React3.createElement(
|
|
864
928
|
Box2,
|
|
865
929
|
{
|
|
866
930
|
sx: (theme) => ({
|
|
@@ -871,14 +935,14 @@ var init_DashboardHeader = __esm({
|
|
|
871
935
|
bgcolor: active ? tone === "danger" && theme.palette.error.main || tone === "warning" && theme.palette.warning.main || theme.palette.primary.main : alpha2(theme.palette.text.primary, 0.25)
|
|
872
936
|
})
|
|
873
937
|
}
|
|
874
|
-
), /* @__PURE__ */
|
|
938
|
+
), /* @__PURE__ */ React3.createElement(
|
|
875
939
|
Typography2,
|
|
876
940
|
{
|
|
877
941
|
component: "span",
|
|
878
942
|
sx: { fontSize: 15, fontWeight: 700, lineHeight: 1, fontVariantNumeric: "tabular-nums" }
|
|
879
943
|
},
|
|
880
944
|
value
|
|
881
|
-
), /* @__PURE__ */
|
|
945
|
+
), /* @__PURE__ */ React3.createElement(
|
|
882
946
|
Typography2,
|
|
883
947
|
{
|
|
884
948
|
component: "span",
|
|
@@ -902,7 +966,7 @@ var init_DashboardHeader = __esm({
|
|
|
902
966
|
return { total: keys.length, expired, expiringSoon };
|
|
903
967
|
}, [keys]);
|
|
904
968
|
const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;
|
|
905
|
-
return /* @__PURE__ */
|
|
969
|
+
return /* @__PURE__ */ React3.createElement(Paper2, { sx: { borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ React3.createElement(
|
|
906
970
|
Box2,
|
|
907
971
|
{
|
|
908
972
|
sx: {
|
|
@@ -915,7 +979,7 @@ var init_DashboardHeader = __esm({
|
|
|
915
979
|
pb: 2
|
|
916
980
|
}
|
|
917
981
|
},
|
|
918
|
-
/* @__PURE__ */
|
|
982
|
+
/* @__PURE__ */ React3.createElement(
|
|
919
983
|
Box2,
|
|
920
984
|
{
|
|
921
985
|
sx: (theme) => ({
|
|
@@ -935,7 +999,7 @@ var init_DashboardHeader = __esm({
|
|
|
935
999
|
},
|
|
936
1000
|
initials(displayName)
|
|
937
1001
|
),
|
|
938
|
-
/* @__PURE__ */
|
|
1002
|
+
/* @__PURE__ */ React3.createElement(Box2, { sx: { flexGrow: 1, minWidth: 220 } }, /* @__PURE__ */ React3.createElement(Typography2, { variant: "h6", sx: { lineHeight: 1.25, wordBreak: "break-word" } }, displayName), /* @__PURE__ */ React3.createElement(
|
|
939
1003
|
Typography2,
|
|
940
1004
|
{
|
|
941
1005
|
variant: "caption",
|
|
@@ -943,7 +1007,7 @@ var init_DashboardHeader = __esm({
|
|
|
943
1007
|
sx: { display: "block", fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
944
1008
|
},
|
|
945
1009
|
userInfo.user_id
|
|
946
|
-
), /* @__PURE__ */
|
|
1010
|
+
), /* @__PURE__ */ React3.createElement(Box2, { sx: { display: "flex", gap: 0.75, flexWrap: "wrap", mt: 1, minHeight: 20 } }, loading ? /* @__PURE__ */ React3.createElement(Skeleton, { variant: "rounded", width: 140, height: 20 }) : teams.map((team) => /* @__PURE__ */ React3.createElement(
|
|
947
1011
|
TagChip,
|
|
948
1012
|
{
|
|
949
1013
|
key: team.team_id,
|
|
@@ -951,18 +1015,8 @@ var init_DashboardHeader = __esm({
|
|
|
951
1015
|
title: team.team_id
|
|
952
1016
|
}
|
|
953
1017
|
)))),
|
|
954
|
-
/* @__PURE__ */
|
|
955
|
-
|
|
956
|
-
{
|
|
957
|
-
variant: "contained",
|
|
958
|
-
color: "primary",
|
|
959
|
-
disableElevation: true,
|
|
960
|
-
startIcon: /* @__PURE__ */ React2.createElement(Add, null),
|
|
961
|
-
onClick: onGenerateKeyClick
|
|
962
|
-
},
|
|
963
|
-
"Generate New Key"
|
|
964
|
-
)
|
|
965
|
-
), /* @__PURE__ */ React2.createElement(Divider, null), /* @__PURE__ */ React2.createElement(
|
|
1018
|
+
/* @__PURE__ */ React3.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick })
|
|
1019
|
+
), /* @__PURE__ */ React3.createElement(Divider, null), /* @__PURE__ */ React3.createElement(
|
|
966
1020
|
Box2,
|
|
967
1021
|
{
|
|
968
1022
|
sx: {
|
|
@@ -974,18 +1028,18 @@ var init_DashboardHeader = __esm({
|
|
|
974
1028
|
py: 1.5
|
|
975
1029
|
}
|
|
976
1030
|
},
|
|
977
|
-
/* @__PURE__ */
|
|
978
|
-
/* @__PURE__ */
|
|
979
|
-
/* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
982
|
-
), tabs && /* @__PURE__ */
|
|
1031
|
+
/* @__PURE__ */ React3.createElement(CountStat, { value: counts.total, label: "keys", tone: "accent" }),
|
|
1032
|
+
/* @__PURE__ */ React3.createElement(Divider, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1033
|
+
/* @__PURE__ */ React3.createElement(CountStat, { value: counts.expired, label: "expired", tone: "danger" }),
|
|
1034
|
+
/* @__PURE__ */ React3.createElement(Divider, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1035
|
+
/* @__PURE__ */ React3.createElement(CountStat, { value: counts.expiringSoon, label: "expiring soon", tone: "warning" })
|
|
1036
|
+
), tabs && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(Divider, null), /* @__PURE__ */ React3.createElement(Box2, { sx: { px: 1 } }, tabs)));
|
|
983
1037
|
};
|
|
984
1038
|
}
|
|
985
1039
|
});
|
|
986
1040
|
|
|
987
1041
|
// src/components/KeysTable.tsx
|
|
988
|
-
import
|
|
1042
|
+
import React4, { useState, useMemo as useMemo2 } from "react";
|
|
989
1043
|
import Table from "@mui/material/Table";
|
|
990
1044
|
import TableBody from "@mui/material/TableBody";
|
|
991
1045
|
import TableCell from "@mui/material/TableCell";
|
|
@@ -1006,7 +1060,7 @@ import CircularProgress from "@mui/material/CircularProgress";
|
|
|
1006
1060
|
import Skeleton2 from "@mui/material/Skeleton";
|
|
1007
1061
|
import InputAdornment from "@mui/material/InputAdornment";
|
|
1008
1062
|
import { alpha as alpha3 } from "@mui/material/styles";
|
|
1009
|
-
import { ContentCopy, Delete,
|
|
1063
|
+
import { ContentCopy, Delete, Edit, Autorenew, Search, Lock, LockOpen } from "@mui/icons-material";
|
|
1010
1064
|
function expiryChipLabel(status, expiresAt) {
|
|
1011
1065
|
if (status === "expired") return "Expired";
|
|
1012
1066
|
if (status === "soon") return `${Math.ceil((new Date(expiresAt).getTime() - Date.now()) / 864e5)}d left`;
|
|
@@ -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__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary" }, "Never");
|
|
1024
1078
|
}
|
|
1025
1079
|
if (status === "ok") {
|
|
1026
|
-
return /* @__PURE__ */
|
|
1080
|
+
return /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary" }, formatDate(expiresAt));
|
|
1027
1081
|
}
|
|
1028
|
-
return /* @__PURE__ */
|
|
1082
|
+
return /* @__PURE__ */ React4.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__ */ React4.createElement(CircularProgress, { size: 18 });
|
|
1098
|
+
if (blocked) return /* @__PURE__ */ React4.createElement(LockOpen, { fontSize: "small" });
|
|
1099
|
+
return /* @__PURE__ */ React4.createElement(Lock, { fontSize: "small" });
|
|
1046
1100
|
}
|
|
1047
1101
|
function BudgetCell({ spend, maxBudget }) {
|
|
1048
1102
|
if (!maxBudget) {
|
|
1049
|
-
return /* @__PURE__ */
|
|
1103
|
+
return /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, "$", spend.toFixed(2), /* @__PURE__ */ React4.createElement(Typography3, { 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__ */ React4.createElement(Box3, { minWidth: 104 }, /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums", mb: 0.5 } }, "$", spend.toFixed(2), /* @__PURE__ */ React4.createElement(Typography3, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 0.5 } }, "/ $", maxBudget)), /* @__PURE__ */ React4.createElement(Meter, { value: pct, tone: budgetTone(pct), height: 4 }));
|
|
1053
1107
|
}
|
|
1054
1108
|
function compareKeys(a, b, sortKey) {
|
|
1055
1109
|
switch (sortKey) {
|
|
@@ -1074,6 +1128,7 @@ var init_KeysTable = __esm({
|
|
|
1074
1128
|
"src/components/KeysTable.tsx"() {
|
|
1075
1129
|
"use strict";
|
|
1076
1130
|
init_api();
|
|
1131
|
+
init_GenerateKeyButton();
|
|
1077
1132
|
init_ui();
|
|
1078
1133
|
shortKeyId = (token) => {
|
|
1079
1134
|
if (!token) return "-";
|
|
@@ -1172,32 +1227,27 @@ var init_KeysTable = __esm({
|
|
|
1172
1227
|
};
|
|
1173
1228
|
const renderTableBody = () => {
|
|
1174
1229
|
if (loading) {
|
|
1175
|
-
return /* @__PURE__ */
|
|
1230
|
+
return /* @__PURE__ */ React4.createElement(TableRow, null, /* @__PURE__ */ React4.createElement(TableCell, { colSpan: 8, sx: { py: 2 } }, /* @__PURE__ */ React4.createElement(Skeleton2, { variant: "rounded", height: 140 })));
|
|
1176
1231
|
}
|
|
1177
1232
|
if (filteredKeys.length === 0) {
|
|
1178
|
-
return /* @__PURE__ */
|
|
1233
|
+
return /* @__PURE__ */ React4.createElement(TableRow, null, /* @__PURE__ */ React4.createElement(TableCell, { colSpan: 8 }, filterText ? /* @__PURE__ */ React4.createElement(
|
|
1179
1234
|
EmptyState,
|
|
1180
1235
|
{
|
|
1181
1236
|
message: "No keys match that filter",
|
|
1182
1237
|
hint: "Try a different alias or model name."
|
|
1183
1238
|
}
|
|
1184
|
-
) : /* @__PURE__ */
|
|
1239
|
+
) : /* @__PURE__ */ React4.createElement(
|
|
1185
1240
|
EmptyState,
|
|
1186
1241
|
{
|
|
1187
1242
|
message: "No keys yet",
|
|
1188
1243
|
hint: "Generate your first key to start calling models.",
|
|
1189
|
-
action: /* @__PURE__ */
|
|
1190
|
-
|
|
1244
|
+
action: /* @__PURE__ */ React4.createElement(
|
|
1245
|
+
GenerateKeyButton,
|
|
1191
1246
|
{
|
|
1192
|
-
variant: "contained",
|
|
1193
|
-
color: "primary",
|
|
1194
1247
|
size: "small",
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
sx: { mt: 1 }
|
|
1199
|
-
},
|
|
1200
|
-
"Generate Your First Key"
|
|
1248
|
+
label: "Generate Your First Key",
|
|
1249
|
+
onClick: onGenerateKeyClick
|
|
1250
|
+
}
|
|
1201
1251
|
)
|
|
1202
1252
|
}
|
|
1203
1253
|
)));
|
|
@@ -1206,7 +1256,7 @@ var init_KeysTable = __esm({
|
|
|
1206
1256
|
const keyId = key.token ?? key.key;
|
|
1207
1257
|
const isBlocking = blockingKeyId === keyId;
|
|
1208
1258
|
const keyModels = key.models ?? [];
|
|
1209
|
-
return /* @__PURE__ */
|
|
1259
|
+
return /* @__PURE__ */ React4.createElement(
|
|
1210
1260
|
TableRow,
|
|
1211
1261
|
{
|
|
1212
1262
|
key: keyId,
|
|
@@ -1217,8 +1267,8 @@ var init_KeysTable = __esm({
|
|
|
1217
1267
|
"& td": { color: theme.palette.text.secondary }
|
|
1218
1268
|
}) : void 0
|
|
1219
1269
|
},
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1270
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(Box3, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2" }, key.key_alias || "\u2014"), key.blocked && /* @__PURE__ */ React4.createElement(StatusPill, { label: "Blocked", tone: "danger" }))),
|
|
1271
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(Box3, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React4.createElement(
|
|
1222
1272
|
Typography3,
|
|
1223
1273
|
{
|
|
1224
1274
|
variant: "body2",
|
|
@@ -1235,7 +1285,7 @@ var init_KeysTable = __esm({
|
|
|
1235
1285
|
})
|
|
1236
1286
|
},
|
|
1237
1287
|
shortKeyId(keyId)
|
|
1238
|
-
), /* @__PURE__ */
|
|
1288
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1239
1289
|
IconButton,
|
|
1240
1290
|
{
|
|
1241
1291
|
size: "small",
|
|
@@ -1243,13 +1293,13 @@ var init_KeysTable = __esm({
|
|
|
1243
1293
|
title: "Copy Key ID",
|
|
1244
1294
|
sx: quietIconButtonSx("accent")
|
|
1245
1295
|
},
|
|
1246
|
-
/* @__PURE__ */
|
|
1296
|
+
/* @__PURE__ */ React4.createElement(ContentCopy, { sx: { fontSize: 15 } })
|
|
1247
1297
|
))),
|
|
1248
|
-
/* @__PURE__ */
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1298
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary" }, formatDate(key.created_at))),
|
|
1299
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(ExpiryCell, { expiresAt: key.expires_at })),
|
|
1300
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })),
|
|
1301
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, key.tpm_limit ?? "\u221E", " / ", key.rpm_limit ?? "\u221E")),
|
|
1302
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, keyModels.length === 0 ? /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary" }, "All models") : /* @__PURE__ */ React4.createElement(Box3, { display: "flex", gap: 0.5, flexWrap: "wrap" }, keyModels.slice(0, 2).map((model) => /* @__PURE__ */ React4.createElement(TagChip, { key: model, label: model, title: model })), keyModels.length > 2 && /* @__PURE__ */ React4.createElement(
|
|
1253
1303
|
TagChip,
|
|
1254
1304
|
{
|
|
1255
1305
|
label: `+${keyModels.length - 2}`,
|
|
@@ -1257,7 +1307,7 @@ var init_KeysTable = __esm({
|
|
|
1257
1307
|
title: keyModels.slice(2).join(", ")
|
|
1258
1308
|
}
|
|
1259
1309
|
))),
|
|
1260
|
-
/* @__PURE__ */
|
|
1310
|
+
/* @__PURE__ */ React4.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React4.createElement(Box3, { display: "flex", justifyContent: "flex-end", gap: 0.25 }, /* @__PURE__ */ React4.createElement(
|
|
1261
1311
|
IconButton,
|
|
1262
1312
|
{
|
|
1263
1313
|
size: "small",
|
|
@@ -1265,8 +1315,8 @@ var init_KeysTable = __esm({
|
|
|
1265
1315
|
title: "Edit key",
|
|
1266
1316
|
sx: quietIconButtonSx("accent")
|
|
1267
1317
|
},
|
|
1268
|
-
/* @__PURE__ */
|
|
1269
|
-
), /* @__PURE__ */
|
|
1318
|
+
/* @__PURE__ */ React4.createElement(Edit, { fontSize: "small" })
|
|
1319
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1270
1320
|
IconButton,
|
|
1271
1321
|
{
|
|
1272
1322
|
size: "small",
|
|
@@ -1276,7 +1326,7 @@ var init_KeysTable = __esm({
|
|
|
1276
1326
|
sx: quietIconButtonSx("warning")
|
|
1277
1327
|
},
|
|
1278
1328
|
keyBlockIcon(isBlocking, key.blocked)
|
|
1279
|
-
), /* @__PURE__ */
|
|
1329
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1280
1330
|
IconButton,
|
|
1281
1331
|
{
|
|
1282
1332
|
size: "small",
|
|
@@ -1284,18 +1334,18 @@ var init_KeysTable = __esm({
|
|
|
1284
1334
|
title: "Revoke key",
|
|
1285
1335
|
sx: quietIconButtonSx("danger")
|
|
1286
1336
|
},
|
|
1287
|
-
/* @__PURE__ */
|
|
1337
|
+
/* @__PURE__ */ React4.createElement(Delete, { fontSize: "small" })
|
|
1288
1338
|
)))
|
|
1289
1339
|
);
|
|
1290
1340
|
});
|
|
1291
1341
|
};
|
|
1292
|
-
return /* @__PURE__ */
|
|
1342
|
+
return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
|
|
1293
1343
|
SectionCard,
|
|
1294
1344
|
{
|
|
1295
1345
|
title: "Virtual Keys",
|
|
1296
1346
|
subtitle: loading ? "Loading\u2026" : `${filteredKeys.length}${filterText ? ` of ${keys.length}` : ""} key${filteredKeys.length === 1 ? "" : "s"}`,
|
|
1297
1347
|
flush: true,
|
|
1298
|
-
actions: /* @__PURE__ */
|
|
1348
|
+
actions: /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
|
|
1299
1349
|
TextField,
|
|
1300
1350
|
{
|
|
1301
1351
|
size: "small",
|
|
@@ -1305,15 +1355,15 @@ var init_KeysTable = __esm({
|
|
|
1305
1355
|
onChange: (e) => setFilterText(e.target.value),
|
|
1306
1356
|
sx: { minWidth: 240 },
|
|
1307
1357
|
InputProps: {
|
|
1308
|
-
startAdornment: /* @__PURE__ */
|
|
1358
|
+
startAdornment: /* @__PURE__ */ React4.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React4.createElement(Search, { fontSize: "small", color: "disabled" }))
|
|
1309
1359
|
}
|
|
1310
1360
|
}
|
|
1311
|
-
), expiredKeys.length > 0 && /* @__PURE__ */
|
|
1361
|
+
), expiredKeys.length > 0 && /* @__PURE__ */ React4.createElement(
|
|
1312
1362
|
Button2,
|
|
1313
1363
|
{
|
|
1314
1364
|
variant: "outlined",
|
|
1315
1365
|
color: "inherit",
|
|
1316
|
-
startIcon: /* @__PURE__ */
|
|
1366
|
+
startIcon: /* @__PURE__ */ React4.createElement(Autorenew, null),
|
|
1317
1367
|
onClick: () => setPruneConfirmCount(expiredKeys.length),
|
|
1318
1368
|
sx: (theme) => ({
|
|
1319
1369
|
color: theme.palette.text.secondary,
|
|
@@ -1328,20 +1378,10 @@ var init_KeysTable = __esm({
|
|
|
1328
1378
|
"Prune expired (",
|
|
1329
1379
|
expiredKeys.length,
|
|
1330
1380
|
")"
|
|
1331
|
-
), /* @__PURE__ */
|
|
1332
|
-
Button2,
|
|
1333
|
-
{
|
|
1334
|
-
variant: "contained",
|
|
1335
|
-
color: "primary",
|
|
1336
|
-
disableElevation: true,
|
|
1337
|
-
startIcon: /* @__PURE__ */ React3.createElement(Add2, null),
|
|
1338
|
-
onClick: onGenerateKeyClick
|
|
1339
|
-
},
|
|
1340
|
-
"Generate New Key"
|
|
1341
|
-
))
|
|
1381
|
+
), /* @__PURE__ */ React4.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick }))
|
|
1342
1382
|
},
|
|
1343
|
-
/* @__PURE__ */
|
|
1344
|
-
), /* @__PURE__ */
|
|
1383
|
+
/* @__PURE__ */ React4.createElement(TableContainer, null, /* @__PURE__ */ React4.createElement(Table, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React4.createElement(TableHead, null, /* @__PURE__ */ React4.createElement(TableRow, null, /* @__PURE__ */ React4.createElement(TableCell, { sortDirection: sortKey === "alias" ? sortDirection : false }, /* @__PURE__ */ React4.createElement(TableSortLabel, { active: sortKey === "alias", direction: sortKey === "alias" ? sortDirection : "asc", onClick: () => handleSort("alias") }, "Alias")), /* @__PURE__ */ React4.createElement(TableCell, null, "Key ID"), /* @__PURE__ */ React4.createElement(TableCell, { sortDirection: sortKey === "created" ? sortDirection : false }, /* @__PURE__ */ React4.createElement(TableSortLabel, { active: sortKey === "created", direction: sortKey === "created" ? sortDirection : "asc", onClick: () => handleSort("created") }, "Created")), /* @__PURE__ */ React4.createElement(TableCell, { sortDirection: sortKey === "expires" ? sortDirection : false }, /* @__PURE__ */ React4.createElement(TableSortLabel, { active: sortKey === "expires", direction: sortKey === "expires" ? sortDirection : "asc", onClick: () => handleSort("expires") }, "Expires")), /* @__PURE__ */ React4.createElement(TableCell, { sortDirection: sortKey === "budget" ? sortDirection : false }, /* @__PURE__ */ React4.createElement(TableSortLabel, { active: sortKey === "budget", direction: sortKey === "budget" ? sortDirection : "asc", onClick: () => handleSort("budget") }, "Budget")), /* @__PURE__ */ React4.createElement(TableCell, { sortDirection: sortKey === "limits" ? sortDirection : false }, /* @__PURE__ */ React4.createElement(TableSortLabel, { active: sortKey === "limits", direction: sortKey === "limits" ? sortDirection : "asc", onClick: () => handleSort("limits") }, "TPM / RPM")), /* @__PURE__ */ React4.createElement(TableCell, { sortDirection: sortKey === "models" ? sortDirection : false }, /* @__PURE__ */ React4.createElement(TableSortLabel, { active: sortKey === "models", direction: sortKey === "models" ? sortDirection : "asc", onClick: () => handleSort("models") }, "Models")), /* @__PURE__ */ React4.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React4.createElement(TableBody, null, renderTableBody())))
|
|
1384
|
+
), /* @__PURE__ */ React4.createElement(Dialog, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ React4.createElement(DialogTitle, null, "Revoke Key?"), /* @__PURE__ */ React4.createElement(DialogContent, null, /* @__PURE__ */ React4.createElement(Typography3, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ React4.createElement(DialogActions, null, /* @__PURE__ */ React4.createElement(Button2, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ React4.createElement(
|
|
1345
1385
|
Button2,
|
|
1346
1386
|
{
|
|
1347
1387
|
onClick: handleConfirmDelete,
|
|
@@ -1349,8 +1389,8 @@ var init_KeysTable = __esm({
|
|
|
1349
1389
|
color: "error",
|
|
1350
1390
|
disabled: deleteSubmitting
|
|
1351
1391
|
},
|
|
1352
|
-
deleteSubmitting ? /* @__PURE__ */
|
|
1353
|
-
))), /* @__PURE__ */
|
|
1392
|
+
deleteSubmitting ? /* @__PURE__ */ React4.createElement(CircularProgress, { size: 20 }) : "Revoke"
|
|
1393
|
+
))), /* @__PURE__ */ React4.createElement(Dialog, { open: pruneConfirmCount !== null, onClose: () => setPruneConfirmCount(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ React4.createElement(DialogTitle, null, "Prune Expired Keys?"), /* @__PURE__ */ React4.createElement(DialogContent, null, /* @__PURE__ */ React4.createElement(Typography3, null, "This will permanently delete ", pruneConfirmCount, " expired key", pruneConfirmCount !== 1 ? "s" : "", " from LiteLLM. Any integrations using them will stop working immediately.")), /* @__PURE__ */ React4.createElement(DialogActions, null, /* @__PURE__ */ React4.createElement(Button2, { onClick: () => setPruneConfirmCount(null), disabled: pruneSubmitting }, "Cancel"), /* @__PURE__ */ React4.createElement(
|
|
1354
1394
|
Button2,
|
|
1355
1395
|
{
|
|
1356
1396
|
onClick: handlePruneExpired,
|
|
@@ -1358,7 +1398,7 @@ var init_KeysTable = __esm({
|
|
|
1358
1398
|
color: "error",
|
|
1359
1399
|
disabled: pruneSubmitting
|
|
1360
1400
|
},
|
|
1361
|
-
pruneSubmitting ? /* @__PURE__ */
|
|
1401
|
+
pruneSubmitting ? /* @__PURE__ */ React4.createElement(CircularProgress, { size: 20 }) : "Prune"
|
|
1362
1402
|
))));
|
|
1363
1403
|
};
|
|
1364
1404
|
}
|
|
@@ -1379,7 +1419,7 @@ var init_format = __esm({
|
|
|
1379
1419
|
});
|
|
1380
1420
|
|
|
1381
1421
|
// src/components/KeyFormDialog.tsx
|
|
1382
|
-
import
|
|
1422
|
+
import React5, { useState as useState2, useEffect, useMemo as useMemo3 } from "react";
|
|
1383
1423
|
import Box4 from "@mui/material/Box";
|
|
1384
1424
|
import Typography4 from "@mui/material/Typography";
|
|
1385
1425
|
import Dialog2 from "@mui/material/Dialog";
|
|
@@ -1556,14 +1596,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1556
1596
|
const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
|
|
1557
1597
|
const code = snippets[snippetKey];
|
|
1558
1598
|
const fileHint = SNIPPET_FILE_HINTS[tab];
|
|
1559
|
-
return /* @__PURE__ */
|
|
1599
|
+
return /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 }, variant: "scrollable" }, /* @__PURE__ */ React5.createElement(Tab, { label: "curl", value: "curl" }), /* @__PURE__ */ React5.createElement(Tab, { label: "OpenAI SDK", value: "openai" }), /* @__PURE__ */ React5.createElement(Tab, { label: "opencode", value: "opencode" }), /* @__PURE__ */ React5.createElement(Tab, { label: "pi", value: "pi" }), /* @__PURE__ */ React5.createElement(Tab, { label: "Claude Code", value: "claude-code" })), fileHint && /* @__PURE__ */ React5.createElement(Typography4, { variant: "caption", color: "text.secondary", display: "block", mb: 0.5 }, fileHint), /* @__PURE__ */ React5.createElement(
|
|
1560
1600
|
Box4,
|
|
1561
1601
|
{
|
|
1562
1602
|
position: "relative",
|
|
1563
1603
|
p: 1.5,
|
|
1564
1604
|
sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
|
|
1565
1605
|
},
|
|
1566
|
-
/* @__PURE__ */
|
|
1606
|
+
/* @__PURE__ */ React5.createElement(
|
|
1567
1607
|
IconButton2,
|
|
1568
1608
|
{
|
|
1569
1609
|
size: "small",
|
|
@@ -1571,9 +1611,9 @@ var init_KeyFormDialog = __esm({
|
|
|
1571
1611
|
title: "Copy snippet",
|
|
1572
1612
|
sx: { position: "absolute", top: 4, right: 4 }
|
|
1573
1613
|
},
|
|
1574
|
-
/* @__PURE__ */
|
|
1614
|
+
/* @__PURE__ */ React5.createElement(ContentCopy2, { fontSize: "small" })
|
|
1575
1615
|
),
|
|
1576
|
-
/* @__PURE__ */
|
|
1616
|
+
/* @__PURE__ */ React5.createElement(
|
|
1577
1617
|
Typography4,
|
|
1578
1618
|
{
|
|
1579
1619
|
component: "pre",
|
|
@@ -1588,7 +1628,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1588
1628
|
},
|
|
1589
1629
|
code
|
|
1590
1630
|
),
|
|
1591
|
-
model && /* @__PURE__ */
|
|
1631
|
+
model && /* @__PURE__ */ React5.createElement(Typography4, { variant: "caption", color: "text.secondary", display: "block", mt: 1 }, "Using model \u201C", model, "\u201D \u2014 swap it for any model you have access to.")
|
|
1592
1632
|
));
|
|
1593
1633
|
};
|
|
1594
1634
|
KeyFormDialog = ({
|
|
@@ -1743,11 +1783,11 @@ var init_KeyFormDialog = __esm({
|
|
|
1743
1783
|
const inCost = fmtCost(m.input_cost_per_token);
|
|
1744
1784
|
const outCost = fmtCost(m.output_cost_per_token);
|
|
1745
1785
|
const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
|
|
1746
|
-
return /* @__PURE__ */
|
|
1786
|
+
return /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ React5.createElement(Typography4, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ React5.createElement(Typography4, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
|
|
1747
1787
|
};
|
|
1748
1788
|
const renderTeamField = () => {
|
|
1749
1789
|
if (teams.length > 0) {
|
|
1750
|
-
return /* @__PURE__ */
|
|
1790
|
+
return /* @__PURE__ */ React5.createElement(
|
|
1751
1791
|
Autocomplete,
|
|
1752
1792
|
{
|
|
1753
1793
|
options: teams,
|
|
@@ -1761,7 +1801,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1761
1801
|
setFormData({ team_id: team?.team_id, models: restrictedModels });
|
|
1762
1802
|
},
|
|
1763
1803
|
disabled: !isCreate,
|
|
1764
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1804
|
+
renderInput: (params) => /* @__PURE__ */ React5.createElement(
|
|
1765
1805
|
TextField2,
|
|
1766
1806
|
{
|
|
1767
1807
|
...params,
|
|
@@ -1776,14 +1816,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1776
1816
|
);
|
|
1777
1817
|
}
|
|
1778
1818
|
if (teamRequired && isCreate) {
|
|
1779
|
-
return /* @__PURE__ */
|
|
1819
|
+
return /* @__PURE__ */ React5.createElement(Typography4, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.");
|
|
1780
1820
|
}
|
|
1781
1821
|
return null;
|
|
1782
1822
|
};
|
|
1783
1823
|
const editKeyId = keyToEdit?.token ?? keyToEdit?.key ?? "";
|
|
1784
1824
|
const renderDangerZone = () => {
|
|
1785
1825
|
if (isCreate || !onResetKeySpend) return null;
|
|
1786
|
-
return /* @__PURE__ */
|
|
1826
|
+
return /* @__PURE__ */ React5.createElement(Box4, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ React5.createElement(Typography4, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ React5.createElement(
|
|
1787
1827
|
Button3,
|
|
1788
1828
|
{
|
|
1789
1829
|
size: "small",
|
|
@@ -1792,7 +1832,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1792
1832
|
onClick: () => setResetSpendConfirm(true)
|
|
1793
1833
|
},
|
|
1794
1834
|
"Reset Spend to $0"
|
|
1795
|
-
) : /* @__PURE__ */
|
|
1835
|
+
) : /* @__PURE__ */ React5.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ React5.createElement(Typography4, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ React5.createElement(
|
|
1796
1836
|
Button3,
|
|
1797
1837
|
{
|
|
1798
1838
|
size: "small",
|
|
@@ -1801,8 +1841,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1801
1841
|
disabled: resetSpendSubmitting,
|
|
1802
1842
|
onClick: handleResetSpend
|
|
1803
1843
|
},
|
|
1804
|
-
resetSpendSubmitting ? /* @__PURE__ */
|
|
1805
|
-
), /* @__PURE__ */
|
|
1844
|
+
resetSpendSubmitting ? /* @__PURE__ */ React5.createElement(CircularProgress2, { size: 16 }) : "Confirm"
|
|
1845
|
+
), /* @__PURE__ */ React5.createElement(Button3, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel")));
|
|
1806
1846
|
};
|
|
1807
1847
|
const maskKey = (key) => {
|
|
1808
1848
|
if (key.length <= 8) return "***";
|
|
@@ -1812,7 +1852,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1812
1852
|
if (newKeyValue) dialogTitle = "Key Generated";
|
|
1813
1853
|
else if (isCreate) dialogTitle = "Generate New Key";
|
|
1814
1854
|
const submitLabel = isCreate ? "Generate" : "Save";
|
|
1815
|
-
return /* @__PURE__ */
|
|
1855
|
+
return /* @__PURE__ */ React5.createElement(Dialog2, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React5.createElement(DialogTitle2, null, dialogTitle), /* @__PURE__ */ React5.createElement(DialogContent2, null, newKeyValue ? /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Typography4, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ React5.createElement(
|
|
1816
1856
|
Box4,
|
|
1817
1857
|
{
|
|
1818
1858
|
display: "flex",
|
|
@@ -1827,7 +1867,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1827
1867
|
borderRadius: 1
|
|
1828
1868
|
}
|
|
1829
1869
|
},
|
|
1830
|
-
/* @__PURE__ */
|
|
1870
|
+
/* @__PURE__ */ React5.createElement(
|
|
1831
1871
|
Typography4,
|
|
1832
1872
|
{
|
|
1833
1873
|
component: "code",
|
|
@@ -1836,8 +1876,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1836
1876
|
},
|
|
1837
1877
|
newKeyValue
|
|
1838
1878
|
),
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
), newKeySnippets && /* @__PURE__ */
|
|
1879
|
+
/* @__PURE__ */ React5.createElement(IconButton2, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ React5.createElement(ContentCopy2, null))
|
|
1880
|
+
), newKeySnippets && /* @__PURE__ */ React5.createElement(Box4, { mt: 2 }, /* @__PURE__ */ React5.createElement(Typography4, { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "Public endpoint \u2014 paste into any tool's base URL / API base field"), /* @__PURE__ */ React5.createElement(
|
|
1841
1881
|
Box4,
|
|
1842
1882
|
{
|
|
1843
1883
|
display: "flex",
|
|
@@ -1851,7 +1891,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1851
1891
|
borderRadius: 1
|
|
1852
1892
|
}
|
|
1853
1893
|
},
|
|
1854
|
-
/* @__PURE__ */
|
|
1894
|
+
/* @__PURE__ */ React5.createElement(
|
|
1855
1895
|
Typography4,
|
|
1856
1896
|
{
|
|
1857
1897
|
component: "code",
|
|
@@ -1860,8 +1900,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1860
1900
|
},
|
|
1861
1901
|
newKeySnippets.publicEndpoint
|
|
1862
1902
|
),
|
|
1863
|
-
/* @__PURE__ */
|
|
1864
|
-
)), newKeySnippets && /* @__PURE__ */
|
|
1903
|
+
/* @__PURE__ */ React5.createElement(IconButton2, { size: "small", onClick: () => copyToClipboard(newKeySnippets.publicEndpoint) }, /* @__PURE__ */ React5.createElement(ContentCopy2, { fontSize: "small" }))
|
|
1904
|
+
)), newKeySnippets && /* @__PURE__ */ React5.createElement(Box4, { mt: 3 }, /* @__PURE__ */ React5.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React5.createElement(Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ React5.createElement(Typography4, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ React5.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ React5.createElement(Box4, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, !isCreate && /* @__PURE__ */ React5.createElement(Typography4, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ React5.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editKeyId))), generateError && /* @__PURE__ */ React5.createElement(Alert, { severity: "error", onClose: () => setGenerateError(null) }, generateError), editError && /* @__PURE__ */ React5.createElement(Alert, { severity: "error", onClose: () => setEditError(null) }, editError), /* @__PURE__ */ React5.createElement(
|
|
1865
1905
|
TextField2,
|
|
1866
1906
|
{
|
|
1867
1907
|
label: "Alias",
|
|
@@ -1877,7 +1917,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1877
1917
|
required: true,
|
|
1878
1918
|
fullWidth: true
|
|
1879
1919
|
}
|
|
1880
|
-
), !isCreate ? null : /* @__PURE__ */
|
|
1920
|
+
), !isCreate ? null : /* @__PURE__ */ React5.createElement(
|
|
1881
1921
|
TextField2,
|
|
1882
1922
|
{
|
|
1883
1923
|
select: true,
|
|
@@ -1886,12 +1926,12 @@ var init_KeyFormDialog = __esm({
|
|
|
1886
1926
|
onChange: (e) => setFormData({ duration: e.target.value }),
|
|
1887
1927
|
fullWidth: true
|
|
1888
1928
|
},
|
|
1889
|
-
/* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
/* @__PURE__ */
|
|
1892
|
-
/* @__PURE__ */
|
|
1893
|
-
/* @__PURE__ */
|
|
1894
|
-
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */
|
|
1929
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "1d" }, "1 Day"),
|
|
1930
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "7d" }, "7 Days"),
|
|
1931
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "30d" }, "30 Days"),
|
|
1932
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "90d" }, "90 Days"),
|
|
1933
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "1y" }, "1 Year")
|
|
1934
|
+
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ React5.createElement(
|
|
1895
1935
|
Autocomplete,
|
|
1896
1936
|
{
|
|
1897
1937
|
multiple: true,
|
|
@@ -1900,8 +1940,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1900
1940
|
getOptionLabel: (m) => m.model_name,
|
|
1901
1941
|
value: selectedModels,
|
|
1902
1942
|
onChange: (_e, selected) => setFormData({ models: selected.map((m) => m.model_name) }),
|
|
1903
|
-
renderOption: (props, m) => /* @__PURE__ */
|
|
1904
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1943
|
+
renderOption: (props, m) => /* @__PURE__ */ React5.createElement("li", { ...props }, modelOption(m)),
|
|
1944
|
+
renderInput: (params) => /* @__PURE__ */ React5.createElement(
|
|
1905
1945
|
TextField2,
|
|
1906
1946
|
{
|
|
1907
1947
|
...params,
|
|
@@ -1911,10 +1951,10 @@ var init_KeyFormDialog = __esm({
|
|
|
1911
1951
|
}
|
|
1912
1952
|
)
|
|
1913
1953
|
}
|
|
1914
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
1954
|
+
), allowUnlimitedBudget && /* @__PURE__ */ React5.createElement(
|
|
1915
1955
|
FormControlLabel,
|
|
1916
1956
|
{
|
|
1917
|
-
control: /* @__PURE__ */
|
|
1957
|
+
control: /* @__PURE__ */ React5.createElement(
|
|
1918
1958
|
Checkbox,
|
|
1919
1959
|
{
|
|
1920
1960
|
checked: unlimitedBudget,
|
|
@@ -1930,7 +1970,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1930
1970
|
),
|
|
1931
1971
|
label: "Unlimited budget"
|
|
1932
1972
|
}
|
|
1933
|
-
), /* @__PURE__ */
|
|
1973
|
+
), /* @__PURE__ */ React5.createElement(
|
|
1934
1974
|
TextField2,
|
|
1935
1975
|
{
|
|
1936
1976
|
label: "Max Budget (USD)",
|
|
@@ -1943,7 +1983,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1943
1983
|
required: true,
|
|
1944
1984
|
fullWidth: true
|
|
1945
1985
|
}
|
|
1946
|
-
), /* @__PURE__ */
|
|
1986
|
+
), /* @__PURE__ */ React5.createElement(Accordion, { disableGutters: true, elevation: 0, sx: { border: "1px solid", borderColor: "divider", "&:before": { display: "none" } } }, /* @__PURE__ */ React5.createElement(AccordionSummary, { expandIcon: /* @__PURE__ */ React5.createElement(ExpandMore, null) }, /* @__PURE__ */ React5.createElement(Typography4, { variant: "body2", fontWeight: 600 }, "Advanced")), /* @__PURE__ */ React5.createElement(AccordionDetails, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React5.createElement(
|
|
1947
1987
|
TextField2,
|
|
1948
1988
|
{
|
|
1949
1989
|
label: "TPM Limit",
|
|
@@ -1953,7 +1993,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1953
1993
|
helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
|
|
1954
1994
|
fullWidth: true
|
|
1955
1995
|
}
|
|
1956
|
-
), /* @__PURE__ */
|
|
1996
|
+
), /* @__PURE__ */ React5.createElement(
|
|
1957
1997
|
TextField2,
|
|
1958
1998
|
{
|
|
1959
1999
|
label: "RPM Limit",
|
|
@@ -1963,7 +2003,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1963
2003
|
helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
|
|
1964
2004
|
fullWidth: true
|
|
1965
2005
|
}
|
|
1966
|
-
))), renderDangerZone())), /* @__PURE__ */
|
|
2006
|
+
))), renderDangerZone())), /* @__PURE__ */ React5.createElement(DialogActions2, null, newKeyValue ? /* @__PURE__ */ React5.createElement(Button3, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Button3, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ React5.createElement(
|
|
1967
2007
|
Button3,
|
|
1968
2008
|
{
|
|
1969
2009
|
onClick: isCreate ? handleGenerate : handleUpdate,
|
|
@@ -1971,14 +2011,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1971
2011
|
color: "primary",
|
|
1972
2012
|
disabled: !canSubmit
|
|
1973
2013
|
},
|
|
1974
|
-
submitting ? /* @__PURE__ */
|
|
2014
|
+
submitting ? /* @__PURE__ */ React5.createElement(CircularProgress2, { size: 24 }) : submitLabel
|
|
1975
2015
|
))));
|
|
1976
2016
|
};
|
|
1977
2017
|
}
|
|
1978
2018
|
});
|
|
1979
2019
|
|
|
1980
2020
|
// src/components/ManageTeamDialog.tsx
|
|
1981
|
-
import
|
|
2021
|
+
import React6, { useState as useState3, useEffect as useEffect2, useMemo as useMemo4 } from "react";
|
|
1982
2022
|
import Dialog3 from "@mui/material/Dialog";
|
|
1983
2023
|
import DialogTitle3 from "@mui/material/DialogTitle";
|
|
1984
2024
|
import DialogContent3 from "@mui/material/DialogContent";
|
|
@@ -2247,7 +2287,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2247
2287
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
2248
2288
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
2249
2289
|
const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
|
|
2250
|
-
return /* @__PURE__ */
|
|
2290
|
+
return /* @__PURE__ */ React6.createElement(Dialog3, { open, onClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React6.createElement(DialogTitle3, null, mode === "create" ? "Create Team" : "Edit Team"), /* @__PURE__ */ React6.createElement(DialogContent3, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 } }, error && /* @__PURE__ */ React6.createElement(Alert2, { severity: "error" }, error), /* @__PURE__ */ React6.createElement(
|
|
2251
2291
|
TextField3,
|
|
2252
2292
|
{
|
|
2253
2293
|
label: "Team Alias",
|
|
@@ -2256,7 +2296,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2256
2296
|
disabled: submitting,
|
|
2257
2297
|
fullWidth: true
|
|
2258
2298
|
}
|
|
2259
|
-
), /* @__PURE__ */
|
|
2299
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2260
2300
|
Autocomplete2,
|
|
2261
2301
|
{
|
|
2262
2302
|
multiple: true,
|
|
@@ -2264,7 +2304,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2264
2304
|
value: models,
|
|
2265
2305
|
onChange: (_, newValue) => setModels(newValue),
|
|
2266
2306
|
disabled: submitting,
|
|
2267
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2307
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(
|
|
2268
2308
|
TextField3,
|
|
2269
2309
|
{
|
|
2270
2310
|
...params,
|
|
@@ -2274,7 +2314,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2274
2314
|
}
|
|
2275
2315
|
)
|
|
2276
2316
|
}
|
|
2277
|
-
), /* @__PURE__ */
|
|
2317
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2278
2318
|
TextField3,
|
|
2279
2319
|
{
|
|
2280
2320
|
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
@@ -2286,13 +2326,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
2286
2326
|
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
2287
2327
|
fullWidth: true
|
|
2288
2328
|
}
|
|
2289
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
2329
|
+
), allowUnlimitedBudget && /* @__PURE__ */ React6.createElement(
|
|
2290
2330
|
FormControlLabel2,
|
|
2291
2331
|
{
|
|
2292
|
-
control: /* @__PURE__ */
|
|
2332
|
+
control: /* @__PURE__ */ React6.createElement(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
2293
2333
|
label: "Unlimited Budget"
|
|
2294
2334
|
}
|
|
2295
|
-
), /* @__PURE__ */
|
|
2335
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2296
2336
|
TextField3,
|
|
2297
2337
|
{
|
|
2298
2338
|
select: true,
|
|
@@ -2303,8 +2343,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2303
2343
|
helperText: "Spend-reset period for the team budget",
|
|
2304
2344
|
fullWidth: true
|
|
2305
2345
|
},
|
|
2306
|
-
durationOptions.map((o) => /* @__PURE__ */
|
|
2307
|
-
), showMembers && /* @__PURE__ */
|
|
2346
|
+
durationOptions.map((o) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: o.value, value: o.value }, o.label))
|
|
2347
|
+
), showMembers && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React6.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ React6.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ React6.createElement(Typography5, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "No members yet.") : /* @__PURE__ */ React6.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 } }, /* @__PURE__ */ React6.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead2, null, /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, null, "User"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Role"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right", sx: { width: 40 } }))), /* @__PURE__ */ React6.createElement(TableBody2, null, members.map((m) => /* @__PURE__ */ React6.createElement(TableRow2, { key: m.user_id }, /* @__PURE__ */ React6.createElement(TableCell2, null, m.user_email ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Typography5, { variant: "body2" }, m.user_email), /* @__PURE__ */ React6.createElement(
|
|
2308
2348
|
Typography5,
|
|
2309
2349
|
{
|
|
2310
2350
|
variant: "caption",
|
|
@@ -2312,14 +2352,14 @@ var init_ManageTeamDialog = __esm({
|
|
|
2312
2352
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
2313
2353
|
},
|
|
2314
2354
|
m.user_id
|
|
2315
|
-
)) : /* @__PURE__ */
|
|
2355
|
+
)) : /* @__PURE__ */ React6.createElement(
|
|
2316
2356
|
Typography5,
|
|
2317
2357
|
{
|
|
2318
2358
|
variant: "body2",
|
|
2319
2359
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
2320
2360
|
},
|
|
2321
2361
|
m.user_id
|
|
2322
|
-
)), /* @__PURE__ */
|
|
2362
|
+
)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, /* @__PURE__ */ React6.createElement(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, /* @__PURE__ */ React6.createElement(
|
|
2323
2363
|
IconButton3,
|
|
2324
2364
|
{
|
|
2325
2365
|
edge: "end",
|
|
@@ -2328,8 +2368,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2328
2368
|
disabled: memberBusy,
|
|
2329
2369
|
onClick: () => handleRemoveMember(m.user_id)
|
|
2330
2370
|
},
|
|
2331
|
-
/* @__PURE__ */
|
|
2332
|
-
))))))), /* @__PURE__ */
|
|
2371
|
+
/* @__PURE__ */ React6.createElement(DeleteIcon, { fontSize: "small" })
|
|
2372
|
+
))))))), /* @__PURE__ */ React6.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ React6.createElement(
|
|
2333
2373
|
Autocomplete2,
|
|
2334
2374
|
{
|
|
2335
2375
|
freeSolo: true,
|
|
@@ -2353,7 +2393,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2353
2393
|
isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
|
|
2354
2394
|
disabled: memberBusy,
|
|
2355
2395
|
sx: { flex: 1, minWidth: 260 },
|
|
2356
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2396
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(
|
|
2357
2397
|
TextField3,
|
|
2358
2398
|
{
|
|
2359
2399
|
...params,
|
|
@@ -2363,12 +2403,12 @@ var init_ManageTeamDialog = __esm({
|
|
|
2363
2403
|
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
2364
2404
|
InputProps: {
|
|
2365
2405
|
...params.InputProps,
|
|
2366
|
-
endAdornment: /* @__PURE__ */
|
|
2406
|
+
endAdornment: /* @__PURE__ */ React6.createElement(React6.Fragment, null, usersLoading ? /* @__PURE__ */ React6.createElement(CircularProgress3, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
|
|
2367
2407
|
}
|
|
2368
2408
|
}
|
|
2369
2409
|
)
|
|
2370
2410
|
}
|
|
2371
|
-
), /* @__PURE__ */
|
|
2411
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2372
2412
|
TextField3,
|
|
2373
2413
|
{
|
|
2374
2414
|
label: "Max budget in team ($)",
|
|
@@ -2379,7 +2419,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2379
2419
|
size: "small",
|
|
2380
2420
|
sx: { width: 160 }
|
|
2381
2421
|
}
|
|
2382
|
-
), /* @__PURE__ */
|
|
2422
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2383
2423
|
Button4,
|
|
2384
2424
|
{
|
|
2385
2425
|
onClick: handleAddMember,
|
|
@@ -2388,7 +2428,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2388
2428
|
sx: { mt: 0.5 }
|
|
2389
2429
|
},
|
|
2390
2430
|
"Add"
|
|
2391
|
-
))), showKnowledgeBases && /* @__PURE__ */
|
|
2431
|
+
))), showKnowledgeBases && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React6.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Knowledge Bases"), /* @__PURE__ */ React6.createElement(Typography5, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching a knowledge base exposes its documents to every key in this team."), kbError && /* @__PURE__ */ React6.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setKbError(null) }, kbError), /* @__PURE__ */ React6.createElement(
|
|
2392
2432
|
Autocomplete2,
|
|
2393
2433
|
{
|
|
2394
2434
|
multiple: true,
|
|
@@ -2397,9 +2437,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2397
2437
|
getOptionLabel: kbLabel,
|
|
2398
2438
|
onChange: (_, v) => setKbIds(v),
|
|
2399
2439
|
disabled: kbBusy,
|
|
2400
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2440
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(TextField3, { ...params, label: "Attached knowledge bases" })
|
|
2401
2441
|
}
|
|
2402
|
-
), /* @__PURE__ */
|
|
2442
|
+
), /* @__PURE__ */ React6.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React6.createElement(
|
|
2403
2443
|
Button4,
|
|
2404
2444
|
{
|
|
2405
2445
|
onClick: handleSaveKnowledgeBases,
|
|
@@ -2407,7 +2447,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2407
2447
|
variant: "outlined"
|
|
2408
2448
|
},
|
|
2409
2449
|
kbBusy ? "Saving\u2026" : "Save knowledge bases"
|
|
2410
|
-
))), showMcpServers && /* @__PURE__ */
|
|
2450
|
+
))), showMcpServers && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React6.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "MCP Servers"), /* @__PURE__ */ React6.createElement(Typography5, { 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__ */ React6.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMcpError(null) }, mcpError), /* @__PURE__ */ React6.createElement(
|
|
2411
2451
|
Autocomplete2,
|
|
2412
2452
|
{
|
|
2413
2453
|
multiple: true,
|
|
@@ -2416,9 +2456,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2416
2456
|
getOptionLabel: mcpLabel,
|
|
2417
2457
|
onChange: (_, v) => setMcpIds(v),
|
|
2418
2458
|
disabled: mcpBusy,
|
|
2419
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2459
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(TextField3, { ...params, label: "Attached MCP servers" })
|
|
2420
2460
|
}
|
|
2421
|
-
), /* @__PURE__ */
|
|
2461
|
+
), /* @__PURE__ */ React6.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React6.createElement(
|
|
2422
2462
|
Button4,
|
|
2423
2463
|
{
|
|
2424
2464
|
onClick: handleSaveMcpServers,
|
|
@@ -2426,13 +2466,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
2426
2466
|
variant: "outlined"
|
|
2427
2467
|
},
|
|
2428
2468
|
mcpBusy ? "Saving\u2026" : "Save MCP servers"
|
|
2429
|
-
)))), /* @__PURE__ */
|
|
2469
|
+
)))), /* @__PURE__ */ React6.createElement(DialogActions3, null, /* @__PURE__ */ React6.createElement(Button4, { onClick: onClose, disabled: submitting }, "Cancel"), /* @__PURE__ */ React6.createElement(Button4, { onClick: handleSubmit, disabled: submitting, variant: "contained" }, submitting ? "Saving..." : "Save")));
|
|
2430
2470
|
};
|
|
2431
2471
|
}
|
|
2432
2472
|
});
|
|
2433
2473
|
|
|
2434
2474
|
// src/components/UsageStats.tsx
|
|
2435
|
-
import
|
|
2475
|
+
import React7, { useMemo as useMemo5, useState as useState4 } from "react";
|
|
2436
2476
|
import Paper4 from "@mui/material/Paper";
|
|
2437
2477
|
import Box6 from "@mui/material/Box";
|
|
2438
2478
|
import Typography6 from "@mui/material/Typography";
|
|
@@ -2482,16 +2522,16 @@ var init_UsageStats = __esm({
|
|
|
2482
2522
|
"30d": "Last 30 days"
|
|
2483
2523
|
};
|
|
2484
2524
|
fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
|
|
2485
|
-
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */
|
|
2525
|
+
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ React7.createElement(Skeleton3, { variant: "rounded", height });
|
|
2486
2526
|
ChartOrFallback = ({ loading, empty, height = 240, children }) => {
|
|
2487
|
-
if (loading) return /* @__PURE__ */
|
|
2488
|
-
if (empty) return /* @__PURE__ */
|
|
2489
|
-
return /* @__PURE__ */
|
|
2527
|
+
if (loading) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { height });
|
|
2528
|
+
if (empty) return /* @__PURE__ */ React7.createElement(EmptyState, { message: "No data for this period", height });
|
|
2529
|
+
return /* @__PURE__ */ React7.createElement(React7.Fragment, null, children);
|
|
2490
2530
|
};
|
|
2491
2531
|
SuccessRateCell = ({ rate, requests }) => {
|
|
2492
|
-
if (requests === 0) return /* @__PURE__ */
|
|
2532
|
+
if (requests === 0) return /* @__PURE__ */ React7.createElement(Typography6, { variant: "body2", color: "text.secondary" }, "\u2014");
|
|
2493
2533
|
const tone = rateTone(rate);
|
|
2494
|
-
return /* @__PURE__ */
|
|
2534
|
+
return /* @__PURE__ */ React7.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React7.createElement(Box6, { width: 72, flexShrink: 0 }, /* @__PURE__ */ React7.createElement(Meter, { value: rate * 100, tone, height: 4 })), /* @__PURE__ */ React7.createElement(
|
|
2495
2535
|
Typography6,
|
|
2496
2536
|
{
|
|
2497
2537
|
variant: "caption",
|
|
@@ -2640,23 +2680,23 @@ var init_UsageStats = __esm({
|
|
|
2640
2680
|
];
|
|
2641
2681
|
const renderKeyRows = () => {
|
|
2642
2682
|
if (loading) {
|
|
2643
|
-
return /* @__PURE__ */
|
|
2683
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React7.createElement(Skeleton3, { variant: "rounded", height: 80 })));
|
|
2644
2684
|
}
|
|
2645
2685
|
if (keyRows.length === 0) {
|
|
2646
|
-
return /* @__PURE__ */
|
|
2686
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 7 }, /* @__PURE__ */ React7.createElement(EmptyState, { message: "No key activity in this period" })));
|
|
2647
2687
|
}
|
|
2648
|
-
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */
|
|
2688
|
+
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React7.createElement(TableRow3, { key: r.keyHash }, /* @__PURE__ */ React7.createElement(TableCell3, null, /* @__PURE__ */ React7.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ React7.createElement(Typography6, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React7.createElement(TableCell3, null, /* @__PURE__ */ React7.createElement(Box6, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ React7.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ React7.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React7.createElement(TableCell3, null, /* @__PURE__ */ React7.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
|
|
2649
2689
|
};
|
|
2650
2690
|
const renderModelRows = () => {
|
|
2651
2691
|
if (loading) {
|
|
2652
|
-
return /* @__PURE__ */
|
|
2692
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React7.createElement(Skeleton3, { variant: "rounded", height: 80 })));
|
|
2653
2693
|
}
|
|
2654
2694
|
if (modelRows.length === 0) {
|
|
2655
|
-
return /* @__PURE__ */
|
|
2695
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 8 }, /* @__PURE__ */ React7.createElement(EmptyState, { message: "No model activity in this period" })));
|
|
2656
2696
|
}
|
|
2657
|
-
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */
|
|
2697
|
+
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React7.createElement(TableRow3, { key: r.model }, /* @__PURE__ */ React7.createElement(TableCell3, null, /* @__PURE__ */ React7.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React7.createElement(Box6, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ React7.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ React7.createElement(TableCell3, null, /* @__PURE__ */ React7.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
|
|
2658
2698
|
};
|
|
2659
|
-
const periodSelect = /* @__PURE__ */
|
|
2699
|
+
const periodSelect = /* @__PURE__ */ React7.createElement(
|
|
2660
2700
|
TextField4,
|
|
2661
2701
|
{
|
|
2662
2702
|
select: true,
|
|
@@ -2666,17 +2706,17 @@ var init_UsageStats = __esm({
|
|
|
2666
2706
|
onChange: (e) => handlePresetChange(e.target.value),
|
|
2667
2707
|
sx: { minWidth: 160 }
|
|
2668
2708
|
},
|
|
2669
|
-
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */
|
|
2709
|
+
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React7.createElement(MenuItem3, { key: p, value: p }, PRESET_LABELS[p]))
|
|
2670
2710
|
);
|
|
2671
|
-
return /* @__PURE__ */
|
|
2711
|
+
return /* @__PURE__ */ React7.createElement(
|
|
2672
2712
|
SectionCard,
|
|
2673
2713
|
{
|
|
2674
2714
|
title: "Usage Analytics",
|
|
2675
2715
|
subtitle: `${PRESET_LABELS[currentPreset]} \xB7 ${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
2676
2716
|
actions: periodSelect
|
|
2677
2717
|
},
|
|
2678
|
-
/* @__PURE__ */
|
|
2679
|
-
/* @__PURE__ */
|
|
2718
|
+
/* @__PURE__ */ React7.createElement(MetricStrip, { metrics }),
|
|
2719
|
+
/* @__PURE__ */ React7.createElement(
|
|
2680
2720
|
Box6,
|
|
2681
2721
|
{
|
|
2682
2722
|
sx: {
|
|
@@ -2689,7 +2729,7 @@ var init_UsageStats = __esm({
|
|
|
2689
2729
|
mb: 2
|
|
2690
2730
|
}
|
|
2691
2731
|
},
|
|
2692
|
-
/* @__PURE__ */
|
|
2732
|
+
/* @__PURE__ */ React7.createElement(
|
|
2693
2733
|
SegmentedControl,
|
|
2694
2734
|
{
|
|
2695
2735
|
value: tab,
|
|
@@ -2701,7 +2741,7 @@ var init_UsageStats = __esm({
|
|
|
2701
2741
|
]
|
|
2702
2742
|
}
|
|
2703
2743
|
),
|
|
2704
|
-
tab === "models" && /* @__PURE__ */
|
|
2744
|
+
tab === "models" && /* @__PURE__ */ React7.createElement(
|
|
2705
2745
|
TextField4,
|
|
2706
2746
|
{
|
|
2707
2747
|
select: true,
|
|
@@ -2711,17 +2751,17 @@ var init_UsageStats = __esm({
|
|
|
2711
2751
|
onChange: (e) => setSelectedModel(e.target.value),
|
|
2712
2752
|
sx: { minWidth: 220 }
|
|
2713
2753
|
},
|
|
2714
|
-
/* @__PURE__ */
|
|
2715
|
-
models.map((m) => /* @__PURE__ */
|
|
2754
|
+
/* @__PURE__ */ React7.createElement(MenuItem3, { value: "all" }, "All models"),
|
|
2755
|
+
models.map((m) => /* @__PURE__ */ React7.createElement(MenuItem3, { key: m.model_name, value: m.model_name }, m.model_name))
|
|
2716
2756
|
)
|
|
2717
2757
|
),
|
|
2718
|
-
tab === "costs" && /* @__PURE__ */
|
|
2758
|
+
tab === "costs" && /* @__PURE__ */ React7.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React7.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ React7.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React7.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ React7.createElement(
|
|
2719
2759
|
Tooltip,
|
|
2720
2760
|
{
|
|
2721
2761
|
cursor: chart.cursor,
|
|
2722
|
-
content: /* @__PURE__ */
|
|
2762
|
+
content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
|
|
2723
2763
|
}
|
|
2724
|
-
), topSpendModels.map((m) => /* @__PURE__ */
|
|
2764
|
+
), topSpendModels.map((m) => /* @__PURE__ */ React7.createElement(
|
|
2725
2765
|
Area,
|
|
2726
2766
|
{
|
|
2727
2767
|
key: m,
|
|
@@ -2733,13 +2773,13 @@ var init_UsageStats = __esm({
|
|
|
2733
2773
|
fill: chartColor(m),
|
|
2734
2774
|
fillOpacity: 0.28
|
|
2735
2775
|
}
|
|
2736
|
-
))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */
|
|
2776
|
+
))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */ React7.createElement(SeriesLegend, { series: topSpendModels.map((m) => ({ name: m, color: chartColor(m) })) }))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React7.createElement(ChartCard, { title: "Daily token usage" }, /* @__PURE__ */ React7.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React7.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ React7.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ React7.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React7.createElement(Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }), /* @__PURE__ */ React7.createElement(Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })))))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React7.createElement(ChartCard, { title: "Daily requests" }, /* @__PURE__ */ React7.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(BarChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 }, barCategoryGap: "30%" }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React7.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ React7.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ React7.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React7.createElement(Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }), /* @__PURE__ */ React7.createElement(Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })))))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React7.createElement(ChartCard, { title: "Daily success rate" }, /* @__PURE__ */ React7.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(LineChart, { data: successRateData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React7.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }), /* @__PURE__ */ React7.createElement(
|
|
2737
2777
|
Tooltip,
|
|
2738
2778
|
{
|
|
2739
2779
|
cursor: chart.cursor,
|
|
2740
|
-
content: /* @__PURE__ */
|
|
2780
|
+
content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
|
|
2741
2781
|
}
|
|
2742
|
-
), /* @__PURE__ */
|
|
2782
|
+
), /* @__PURE__ */ React7.createElement(ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ React7.createElement(
|
|
2743
2783
|
Line,
|
|
2744
2784
|
{
|
|
2745
2785
|
type: "monotone",
|
|
@@ -2750,13 +2790,13 @@ var init_UsageStats = __esm({
|
|
|
2750
2790
|
dot: { r: 2.5, strokeWidth: 0, fill: SERIES.input },
|
|
2751
2791
|
activeDot: { r: 4 }
|
|
2752
2792
|
}
|
|
2753
|
-
)))))), /* @__PURE__ */
|
|
2793
|
+
)))))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React7.createElement(
|
|
2754
2794
|
ChartCard,
|
|
2755
2795
|
{
|
|
2756
2796
|
title: maxBudget > 0 ? "Cumulative spend vs budget" : "Cumulative spend",
|
|
2757
2797
|
meta: maxBudget > 0 ? `${fmtUsd(totalCumSpend)} used \xB7 ${fmtUsd(Math.max(0, maxBudget - totalCumSpend))} left` : void 0
|
|
2758
2798
|
},
|
|
2759
|
-
/* @__PURE__ */
|
|
2799
|
+
/* @__PURE__ */ React7.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(AreaChart, { data: cumulativeData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React7.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React7.createElement(
|
|
2760
2800
|
YAxis,
|
|
2761
2801
|
{
|
|
2762
2802
|
tickFormatter: fmtUsdCompact,
|
|
@@ -2764,13 +2804,13 @@ var init_UsageStats = __esm({
|
|
|
2764
2804
|
domain: cumulativeDomain,
|
|
2765
2805
|
...chart.axis
|
|
2766
2806
|
}
|
|
2767
|
-
), /* @__PURE__ */
|
|
2807
|
+
), /* @__PURE__ */ React7.createElement(
|
|
2768
2808
|
Tooltip,
|
|
2769
2809
|
{
|
|
2770
2810
|
cursor: chart.cursor,
|
|
2771
|
-
content: /* @__PURE__ */
|
|
2811
|
+
content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtUsd })
|
|
2772
2812
|
}
|
|
2773
|
-
), maxBudget > 0 && /* @__PURE__ */
|
|
2813
|
+
), maxBudget > 0 && /* @__PURE__ */ React7.createElement(
|
|
2774
2814
|
ReferenceLine,
|
|
2775
2815
|
{
|
|
2776
2816
|
y: maxBudget,
|
|
@@ -2778,7 +2818,7 @@ var init_UsageStats = __esm({
|
|
|
2778
2818
|
strokeDasharray: "5 4",
|
|
2779
2819
|
label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 10, fill: SERIES.budget }
|
|
2780
2820
|
}
|
|
2781
|
-
), /* @__PURE__ */
|
|
2821
|
+
), /* @__PURE__ */ React7.createElement(
|
|
2782
2822
|
Area,
|
|
2783
2823
|
{
|
|
2784
2824
|
type: "monotone",
|
|
@@ -2791,32 +2831,32 @@ var init_UsageStats = __esm({
|
|
|
2791
2831
|
}
|
|
2792
2832
|
))))
|
|
2793
2833
|
))),
|
|
2794
|
-
tab === "models" && /* @__PURE__ */
|
|
2834
|
+
tab === "models" && /* @__PURE__ */ React7.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React7.createElement(ChartCard, { title: "Spend by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ React7.createElement(
|
|
2795
2835
|
ChartOrFallback,
|
|
2796
2836
|
{
|
|
2797
2837
|
loading,
|
|
2798
2838
|
empty: topModelSpendBars.length === 0,
|
|
2799
2839
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2800
2840
|
},
|
|
2801
|
-
/* @__PURE__ */
|
|
2802
|
-
))), /* @__PURE__ */
|
|
2841
|
+
/* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React7.createElement(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React7.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React7.createElement(Bar, { dataKey: "spend", name: "Spend", radius: [0, 3, 3, 0], barSize: 14 }, topModelSpendBars.map((r) => /* @__PURE__ */ React7.createElement(Cell, { key: r.model, fill: chartColor(r.model) })))))
|
|
2842
|
+
))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React7.createElement(ChartCard, { title: "Tokens by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ React7.createElement(
|
|
2803
2843
|
ChartOrFallback,
|
|
2804
2844
|
{
|
|
2805
2845
|
loading,
|
|
2806
2846
|
empty: modelRows.length === 0,
|
|
2807
2847
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2808
2848
|
},
|
|
2809
|
-
/* @__PURE__ */
|
|
2810
|
-
))), /* @__PURE__ */
|
|
2811
|
-
tab === "keys" && /* @__PURE__ */
|
|
2849
|
+
/* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React7.createElement(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React7.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ React7.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React7.createElement(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ React7.createElement(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
|
|
2850
|
+
))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React7.createElement(TableContainer3, { component: Paper4, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React7.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead3, null, /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, null, "Model"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Spend"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Requests"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Success"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Failed"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Input"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Output"), /* @__PURE__ */ React7.createElement(TableCell3, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React7.createElement(TableBody3, null, renderModelRows()))))),
|
|
2851
|
+
tab === "keys" && /* @__PURE__ */ React7.createElement(Grid, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React7.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ React7.createElement(
|
|
2812
2852
|
ChartOrFallback,
|
|
2813
2853
|
{
|
|
2814
2854
|
loading,
|
|
2815
2855
|
empty: topKeySpendBars.length === 0,
|
|
2816
2856
|
height: Math.max(160, topKeySpendBars.length * 32)
|
|
2817
2857
|
},
|
|
2818
|
-
/* @__PURE__ */
|
|
2819
|
-
))), /* @__PURE__ */
|
|
2858
|
+
/* @__PURE__ */ React7.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React7.createElement(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React7.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React7.createElement(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
|
|
2859
|
+
))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React7.createElement(TableContainer3, { component: Paper4, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React7.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead3, null, /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, null, "Key"), /* @__PURE__ */ React7.createElement(TableCell3, null, "Models"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Spend"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Requests"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Tokens"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Failed"), /* @__PURE__ */ React7.createElement(TableCell3, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React7.createElement(TableBody3, null, renderKeyRows())))))
|
|
2820
2860
|
);
|
|
2821
2861
|
};
|
|
2822
2862
|
}
|
|
@@ -2944,12 +2984,12 @@ var init_budget = __esm({
|
|
|
2944
2984
|
});
|
|
2945
2985
|
|
|
2946
2986
|
// src/components/BudgetLimitList.tsx
|
|
2947
|
-
import
|
|
2987
|
+
import React8 from "react";
|
|
2948
2988
|
import Box7 from "@mui/material/Box";
|
|
2949
2989
|
import Paper5 from "@mui/material/Paper";
|
|
2950
2990
|
import Typography7 from "@mui/material/Typography";
|
|
2951
2991
|
import { alpha as alpha4, useTheme as useTheme2 } from "@mui/material/styles";
|
|
2952
|
-
import { Link } from "@backstage/core-components";
|
|
2992
|
+
import { Link as Link2 } from "@backstage/core-components";
|
|
2953
2993
|
function levelToneColor(tone, theme) {
|
|
2954
2994
|
switch (tone) {
|
|
2955
2995
|
case "success":
|
|
@@ -2975,7 +3015,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2975
3015
|
const tone = budgetTone2(limit.pct);
|
|
2976
3016
|
const pct = Math.round(limit.pct);
|
|
2977
3017
|
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
2978
|
-
return /* @__PURE__ */
|
|
3018
|
+
return /* @__PURE__ */ React8.createElement(Paper5, { variant: "outlined", sx: { px: 1.5, py: 1.25, borderRadius: 1.5 } }, /* @__PURE__ */ React8.createElement(Box7, { sx: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1.5 } }, /* @__PURE__ */ React8.createElement(Box7, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React8.createElement(
|
|
2979
3019
|
Typography7,
|
|
2980
3020
|
{
|
|
2981
3021
|
variant: "body2",
|
|
@@ -2983,7 +3023,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2983
3023
|
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
2984
3024
|
},
|
|
2985
3025
|
limit.label
|
|
2986
|
-
), limit.sublabel && /* @__PURE__ */
|
|
3026
|
+
), limit.sublabel && /* @__PURE__ */ React8.createElement(
|
|
2987
3027
|
Typography7,
|
|
2988
3028
|
{
|
|
2989
3029
|
variant: "caption",
|
|
@@ -2998,7 +3038,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2998
3038
|
}
|
|
2999
3039
|
},
|
|
3000
3040
|
limit.sublabel
|
|
3001
|
-
)), limit.hidden ? /* @__PURE__ */
|
|
3041
|
+
)), limit.hidden ? /* @__PURE__ */ React8.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, pct, "% used") : /* @__PURE__ */ React8.createElement(Typography7, { variant: "body2", sx: { fontWeight: 600, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" } }, fmtUsd(limit.spend), /* @__PURE__ */ React8.createElement(Box7, { component: "span", color: "text.secondary", sx: { fontWeight: 400 } }, " / ", fmtUsd(limit.budget)))), /* @__PURE__ */ React8.createElement(Box7, { sx: { mt: 1 } }, /* @__PURE__ */ React8.createElement(Meter, { value: limit.pct, tone, height: 6 })), /* @__PURE__ */ React8.createElement(
|
|
3002
3042
|
Box7,
|
|
3003
3043
|
{
|
|
3004
3044
|
sx: {
|
|
@@ -3009,14 +3049,14 @@ var init_BudgetLimitList = __esm({
|
|
|
3009
3049
|
mt: 0.75
|
|
3010
3050
|
}
|
|
3011
3051
|
},
|
|
3012
|
-
/* @__PURE__ */
|
|
3013
|
-
/* @__PURE__ */
|
|
3052
|
+
/* @__PURE__ */ React8.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, limit.hidden ? /* @__PURE__ */ React8.createElement(React8.Fragment, null, "Hidden by admin") : /* @__PURE__ */ React8.createElement(React8.Fragment, null, pct, "% of budget", closeTo && ` \xB7 soft-limit warning`)),
|
|
3053
|
+
/* @__PURE__ */ React8.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3014
3054
|
));
|
|
3015
3055
|
};
|
|
3016
3056
|
TaggedLimit = ({ limit }) => {
|
|
3017
3057
|
const theme = useTheme2();
|
|
3018
3058
|
const tone = budgetTone2(limit.pct);
|
|
3019
|
-
return /* @__PURE__ */
|
|
3059
|
+
return /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(
|
|
3020
3060
|
Typography7,
|
|
3021
3061
|
{
|
|
3022
3062
|
sx: {
|
|
@@ -3029,7 +3069,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3029
3069
|
}
|
|
3030
3070
|
},
|
|
3031
3071
|
limit.kind
|
|
3032
|
-
), /* @__PURE__ */
|
|
3072
|
+
), /* @__PURE__ */ React8.createElement(LimitCard, { limit }));
|
|
3033
3073
|
};
|
|
3034
3074
|
MoreKeysNote = ({
|
|
3035
3075
|
count,
|
|
@@ -3038,12 +3078,12 @@ var init_BudgetLimitList = __esm({
|
|
|
3038
3078
|
}) => {
|
|
3039
3079
|
const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
|
|
3040
3080
|
if (!href) {
|
|
3041
|
-
return /* @__PURE__ */
|
|
3081
|
+
return /* @__PURE__ */ React8.createElement(Typography7, { variant: "caption", color: "text.secondary" }, text);
|
|
3042
3082
|
}
|
|
3043
|
-
return /* @__PURE__ */
|
|
3083
|
+
return /* @__PURE__ */ React8.createElement(Link2, { to: href, variant: "caption" }, text, " \u2192");
|
|
3044
3084
|
};
|
|
3045
|
-
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */
|
|
3046
|
-
LimitListPanel = ({ children }) => /* @__PURE__ */
|
|
3085
|
+
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */ React8.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, limits.map((limit) => /* @__PURE__ */ React8.createElement(TaggedLimit, { key: `${limit.kind}:${limit.sublabel ?? limit.label}`, limit })), hiddenKeyCount > 0 && /* @__PURE__ */ React8.createElement(MoreKeysNote, { count: hiddenKeyCount, href: keysHref }));
|
|
3086
|
+
LimitListPanel = ({ children }) => /* @__PURE__ */ React8.createElement(
|
|
3047
3087
|
Box7,
|
|
3048
3088
|
{
|
|
3049
3089
|
sx: (theme) => ({
|
|
@@ -3059,7 +3099,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3059
3099
|
});
|
|
3060
3100
|
|
|
3061
3101
|
// src/components/LiteLLMBudgetWidget.tsx
|
|
3062
|
-
import
|
|
3102
|
+
import React9, { useEffect as useEffect3, useMemo as useMemo6, useState as useState5 } from "react";
|
|
3063
3103
|
import Paper6 from "@mui/material/Paper";
|
|
3064
3104
|
import Box8 from "@mui/material/Box";
|
|
3065
3105
|
import Typography8 from "@mui/material/Typography";
|
|
@@ -3086,7 +3126,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3086
3126
|
tone,
|
|
3087
3127
|
isLast,
|
|
3088
3128
|
children
|
|
3089
|
-
}) => /* @__PURE__ */
|
|
3129
|
+
}) => /* @__PURE__ */ React9.createElement(Box8, { sx: { display: "flex", gap: 1.5 } }, /* @__PURE__ */ React9.createElement(Box8, { sx: { display: "flex", flexDirection: "column", alignItems: "center", width: 26, flexShrink: 0 } }, /* @__PURE__ */ React9.createElement(
|
|
3090
3130
|
Box8,
|
|
3091
3131
|
{
|
|
3092
3132
|
sx: (theme) => ({
|
|
@@ -3104,7 +3144,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3104
3144
|
})
|
|
3105
3145
|
},
|
|
3106
3146
|
rank
|
|
3107
|
-
), /* @__PURE__ */
|
|
3147
|
+
), /* @__PURE__ */ React9.createElement(
|
|
3108
3148
|
Box8,
|
|
3109
3149
|
{
|
|
3110
3150
|
sx: (theme) => ({
|
|
@@ -3114,7 +3154,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3114
3154
|
bgcolor: isLast ? "transparent" : alpha5(theme.palette.text.primary, 0.12)
|
|
3115
3155
|
})
|
|
3116
3156
|
}
|
|
3117
|
-
)), /* @__PURE__ */
|
|
3157
|
+
)), /* @__PURE__ */ React9.createElement(Box8, { sx: { flex: 1, minWidth: 0, pb: isLast ? 0 : 2 } }, /* @__PURE__ */ React9.createElement(Box8, { sx: { display: "flex", alignItems: "baseline", gap: 1, flexWrap: "wrap", mb: 0.75 } }, /* @__PURE__ */ React9.createElement(
|
|
3118
3158
|
Typography8,
|
|
3119
3159
|
{
|
|
3120
3160
|
sx: (theme) => ({
|
|
@@ -3126,8 +3166,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3126
3166
|
})
|
|
3127
3167
|
},
|
|
3128
3168
|
name
|
|
3129
|
-
), /* @__PURE__ */
|
|
3130
|
-
EmptyLimitCard = ({ children }) => /* @__PURE__ */
|
|
3169
|
+
), /* @__PURE__ */ React9.createElement(Typography8, { variant: "caption", color: "text.secondary", sx: { fontSize: 11.5 } }, tagline), note && /* @__PURE__ */ React9.createElement(StatusPill, { label: note, tone: "neutral", dot: false, sx: { ml: "auto" } })), /* @__PURE__ */ React9.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, children)));
|
|
3170
|
+
EmptyLimitCard = ({ children }) => /* @__PURE__ */ React9.createElement(
|
|
3131
3171
|
Paper6,
|
|
3132
3172
|
{
|
|
3133
3173
|
variant: "outlined",
|
|
@@ -3138,7 +3178,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3138
3178
|
bgcolor: alpha5(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
|
|
3139
3179
|
})
|
|
3140
3180
|
},
|
|
3141
|
-
/* @__PURE__ */
|
|
3181
|
+
/* @__PURE__ */ React9.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
|
|
3142
3182
|
);
|
|
3143
3183
|
CLOSEST_LEVEL_LABEL = {
|
|
3144
3184
|
key: "key",
|
|
@@ -3146,7 +3186,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3146
3186
|
team: "team"
|
|
3147
3187
|
};
|
|
3148
3188
|
KEYS_PATH = "/litellm?tab=keys";
|
|
3149
|
-
HierarchyNote = ({ withReset }) => /* @__PURE__ */
|
|
3189
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ React9.createElement(
|
|
3150
3190
|
Box8,
|
|
3151
3191
|
{
|
|
3152
3192
|
sx: (theme) => ({
|
|
@@ -3157,11 +3197,11 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3157
3197
|
borderColor: alpha5(theme.palette.text.primary, 0.18)
|
|
3158
3198
|
})
|
|
3159
3199
|
},
|
|
3160
|
-
/* @__PURE__ */
|
|
3200
|
+
/* @__PURE__ */ React9.createElement(Typography8, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ React9.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.")
|
|
3161
3201
|
);
|
|
3162
3202
|
FullBody = ({ summary }) => {
|
|
3163
3203
|
const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
|
|
3164
|
-
return /* @__PURE__ */
|
|
3204
|
+
return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
|
|
3165
3205
|
LevelFrame,
|
|
3166
3206
|
{
|
|
3167
3207
|
rank: 1,
|
|
@@ -3169,10 +3209,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3169
3209
|
tagline: "per-key cap \xB7 highest priority",
|
|
3170
3210
|
note: hasBudgetedKeys ? void 0 : "no key budgets set"
|
|
3171
3211
|
},
|
|
3172
|
-
!hasBudgetedKeys && /* @__PURE__ */
|
|
3173
|
-
summary.keys.map((k) => /* @__PURE__ */
|
|
3174
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */
|
|
3175
|
-
), /* @__PURE__ */
|
|
3212
|
+
!hasBudgetedKeys && /* @__PURE__ */ React9.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3213
|
+
summary.keys.map((k) => /* @__PURE__ */ React9.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3214
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React9.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
|
|
3215
|
+
), /* @__PURE__ */ React9.createElement(
|
|
3176
3216
|
LevelFrame,
|
|
3177
3217
|
{
|
|
3178
3218
|
rank: 2,
|
|
@@ -3180,15 +3220,15 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3180
3220
|
tagline: "cap on everything you spend with your own keys",
|
|
3181
3221
|
note: "skipped for team-bound keys"
|
|
3182
3222
|
},
|
|
3183
|
-
summary.user ? /* @__PURE__ */
|
|
3184
|
-
), /* @__PURE__ */
|
|
3223
|
+
summary.user ? /* @__PURE__ */ React9.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React9.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
|
|
3224
|
+
), /* @__PURE__ */ React9.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__ */ React9.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React9.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React9.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React9.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__ */ React9.createElement(Box8, { sx: { mt: 2 } }, /* @__PURE__ */ React9.createElement(HierarchyNote, { withReset: true })));
|
|
3185
3225
|
};
|
|
3186
3226
|
CompactBody = ({ summary }) => {
|
|
3187
3227
|
const { count } = budgetHeadline(summary);
|
|
3188
3228
|
if (count === 0) {
|
|
3189
|
-
return /* @__PURE__ */
|
|
3229
|
+
return /* @__PURE__ */ React9.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
|
|
3190
3230
|
}
|
|
3191
|
-
return /* @__PURE__ */
|
|
3231
|
+
return /* @__PURE__ */ React9.createElement(Box8, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ React9.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ React9.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React9.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH }), summary.user && /* @__PURE__ */ React9.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React9.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
|
|
3192
3232
|
};
|
|
3193
3233
|
LiteLLMBudgetWidget = ({
|
|
3194
3234
|
title = "Budget Policy",
|
|
@@ -3239,8 +3279,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3239
3279
|
headline.closest.pct
|
|
3240
3280
|
)}%`;
|
|
3241
3281
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3242
|
-
const body = /* @__PURE__ */
|
|
3243
|
-
return /* @__PURE__ */
|
|
3282
|
+
const body = /* @__PURE__ */ React9.createElement(React9.Fragment, null, loading && /* @__PURE__ */ React9.createElement(Box8, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 140 }, /* @__PURE__ */ React9.createElement(CircularProgress4, { size: 32 })), !loading && error && /* @__PURE__ */ React9.createElement(Alert3, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && (compact ? /* @__PURE__ */ React9.createElement(CompactBody, { summary }) : /* @__PURE__ */ React9.createElement(FullBody, { summary })));
|
|
3283
|
+
return /* @__PURE__ */ React9.createElement(Paper6, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ React9.createElement(
|
|
3244
3284
|
Box8,
|
|
3245
3285
|
{
|
|
3246
3286
|
onClick: () => setExpanded((e) => !e),
|
|
@@ -3253,14 +3293,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3253
3293
|
mb: expanded ? 1.5 : 0
|
|
3254
3294
|
}
|
|
3255
3295
|
},
|
|
3256
|
-
/* @__PURE__ */
|
|
3296
|
+
/* @__PURE__ */ React9.createElement(Box8, { sx: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React9.createElement(Typography8, { variant: "h6", lineHeight: 1.2 }, title), !loading && !error && /* @__PURE__ */ React9.createElement(Box8, { sx: { mt: 0.5 } }, /* @__PURE__ */ React9.createElement(
|
|
3257
3297
|
StatusPill,
|
|
3258
3298
|
{
|
|
3259
3299
|
label: summaryText,
|
|
3260
3300
|
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3261
3301
|
}
|
|
3262
3302
|
))),
|
|
3263
|
-
/* @__PURE__ */
|
|
3303
|
+
/* @__PURE__ */ React9.createElement(
|
|
3264
3304
|
IconButton4,
|
|
3265
3305
|
{
|
|
3266
3306
|
size: "small",
|
|
@@ -3276,9 +3316,9 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3276
3316
|
transition: theme.transitions.create("transform")
|
|
3277
3317
|
})
|
|
3278
3318
|
},
|
|
3279
|
-
/* @__PURE__ */
|
|
3319
|
+
/* @__PURE__ */ React9.createElement(ExpandMore2, null)
|
|
3280
3320
|
)
|
|
3281
|
-
) : /* @__PURE__ */
|
|
3321
|
+
) : /* @__PURE__ */ React9.createElement(Box8, { sx: { mb: 1.5 } }, /* @__PURE__ */ React9.createElement(Typography8, { variant: "h6", lineHeight: 1.2 }, title), /* @__PURE__ */ React9.createElement(Typography8, { variant: "caption", color: "text.secondary" }, "How LiteLLM caps spend \u2014 and where you stand right now")), collapsible ? /* @__PURE__ */ React9.createElement(Collapse, { in: expanded, unmountOnExit: true }, body) : body, action && /* @__PURE__ */ React9.createElement(
|
|
3282
3322
|
Box8,
|
|
3283
3323
|
{
|
|
3284
3324
|
sx: (theme) => ({
|
|
@@ -3294,7 +3334,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3294
3334
|
});
|
|
3295
3335
|
|
|
3296
3336
|
// src/components/TeamUsage.tsx
|
|
3297
|
-
import
|
|
3337
|
+
import React10, { useState as useState6 } from "react";
|
|
3298
3338
|
import Paper7 from "@mui/material/Paper";
|
|
3299
3339
|
import Box9 from "@mui/material/Box";
|
|
3300
3340
|
import Typography9 from "@mui/material/Typography";
|
|
@@ -3311,7 +3351,7 @@ import IconButton5 from "@mui/material/IconButton";
|
|
|
3311
3351
|
import Button5 from "@mui/material/Button";
|
|
3312
3352
|
import CircularProgress5 from "@mui/material/CircularProgress";
|
|
3313
3353
|
import { alpha as alpha6 } from "@mui/material/styles";
|
|
3314
|
-
import { ExpandMore as ExpandMore3, Group, Add as
|
|
3354
|
+
import { ExpandMore as ExpandMore3, Group, Add as Add2 } from "@mui/icons-material";
|
|
3315
3355
|
import {
|
|
3316
3356
|
AreaChart as AreaChart2,
|
|
3317
3357
|
Area as Area2,
|
|
@@ -3354,10 +3394,10 @@ var init_TeamUsage = __esm({
|
|
|
3354
3394
|
const renderDailySpendSection = () => {
|
|
3355
3395
|
if (hidden) return null;
|
|
3356
3396
|
if (usageLoading) {
|
|
3357
|
-
return /* @__PURE__ */
|
|
3397
|
+
return /* @__PURE__ */ React10.createElement(Box9, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React10.createElement(CircularProgress5, { size: 22 }));
|
|
3358
3398
|
}
|
|
3359
3399
|
if (dailyData.length === 0) return null;
|
|
3360
|
-
return /* @__PURE__ */
|
|
3400
|
+
return /* @__PURE__ */ React10.createElement(Box9, { mb: 2.5 }, /* @__PURE__ */ React10.createElement(
|
|
3361
3401
|
Typography9,
|
|
3362
3402
|
{
|
|
3363
3403
|
variant: "caption",
|
|
@@ -3365,13 +3405,13 @@ var init_TeamUsage = __esm({
|
|
|
3365
3405
|
sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
|
|
3366
3406
|
},
|
|
3367
3407
|
"Daily spend"
|
|
3368
|
-
), /* @__PURE__ */
|
|
3408
|
+
), /* @__PURE__ */ React10.createElement(Box9, { height: 150 }, /* @__PURE__ */ React10.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React10.createElement(AreaChart2, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React10.createElement(CartesianGrid2, { ...chart.grid }), /* @__PURE__ */ React10.createElement(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React10.createElement(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ React10.createElement(
|
|
3369
3409
|
Tooltip2,
|
|
3370
3410
|
{
|
|
3371
3411
|
cursor: chart.cursor,
|
|
3372
|
-
content: /* @__PURE__ */
|
|
3412
|
+
content: /* @__PURE__ */ React10.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
3373
3413
|
}
|
|
3374
|
-
), /* @__PURE__ */
|
|
3414
|
+
), /* @__PURE__ */ React10.createElement(
|
|
3375
3415
|
Area2,
|
|
3376
3416
|
{
|
|
3377
3417
|
type: "monotone",
|
|
@@ -3384,7 +3424,7 @@ var init_TeamUsage = __esm({
|
|
|
3384
3424
|
}
|
|
3385
3425
|
)))));
|
|
3386
3426
|
};
|
|
3387
|
-
const sectionLabel = (label) => /* @__PURE__ */
|
|
3427
|
+
const sectionLabel = (label) => /* @__PURE__ */ React10.createElement(
|
|
3388
3428
|
Typography9,
|
|
3389
3429
|
{
|
|
3390
3430
|
variant: "caption",
|
|
@@ -3393,7 +3433,7 @@ var init_TeamUsage = __esm({
|
|
|
3393
3433
|
},
|
|
3394
3434
|
label
|
|
3395
3435
|
);
|
|
3396
|
-
return /* @__PURE__ */
|
|
3436
|
+
return /* @__PURE__ */ React10.createElement(Paper7, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React10.createElement(Box9, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React10.createElement(
|
|
3397
3437
|
Box9,
|
|
3398
3438
|
{
|
|
3399
3439
|
sx: (theme) => ({
|
|
@@ -3408,8 +3448,8 @@ var init_TeamUsage = __esm({
|
|
|
3408
3448
|
color: theme.palette.primary.main
|
|
3409
3449
|
})
|
|
3410
3450
|
},
|
|
3411
|
-
/* @__PURE__ */
|
|
3412
|
-
), /* @__PURE__ */
|
|
3451
|
+
/* @__PURE__ */ React10.createElement(Group, { fontSize: "small" })
|
|
3452
|
+
), /* @__PURE__ */ React10.createElement(Box9, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ React10.createElement(Typography9, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ React10.createElement(
|
|
3413
3453
|
Typography9,
|
|
3414
3454
|
{
|
|
3415
3455
|
variant: "caption",
|
|
@@ -3417,7 +3457,7 @@ var init_TeamUsage = __esm({
|
|
|
3417
3457
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3418
3458
|
},
|
|
3419
3459
|
team.team_id
|
|
3420
|
-
)), isOver && /* @__PURE__ */
|
|
3460
|
+
)), isOver && /* @__PURE__ */ React10.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ React10.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ React10.createElement(
|
|
3421
3461
|
Button5,
|
|
3422
3462
|
{
|
|
3423
3463
|
size: "small",
|
|
@@ -3426,7 +3466,7 @@ var init_TeamUsage = __esm({
|
|
|
3426
3466
|
sx: { textTransform: "none" }
|
|
3427
3467
|
},
|
|
3428
3468
|
"Edit"
|
|
3429
|
-
), /* @__PURE__ */
|
|
3469
|
+
), /* @__PURE__ */ React10.createElement(
|
|
3430
3470
|
IconButton5,
|
|
3431
3471
|
{
|
|
3432
3472
|
size: "small",
|
|
@@ -3439,8 +3479,8 @@ var init_TeamUsage = __esm({
|
|
|
3439
3479
|
transition: theme.transitions.create("transform")
|
|
3440
3480
|
})
|
|
3441
3481
|
},
|
|
3442
|
-
/* @__PURE__ */
|
|
3443
|
-
)), /* @__PURE__ */
|
|
3482
|
+
/* @__PURE__ */ React10.createElement(ExpandMore3, null)
|
|
3483
|
+
)), /* @__PURE__ */ React10.createElement(
|
|
3444
3484
|
Box9,
|
|
3445
3485
|
{
|
|
3446
3486
|
sx: {
|
|
@@ -3451,20 +3491,20 @@ var init_TeamUsage = __esm({
|
|
|
3451
3491
|
maxWidth: 640
|
|
3452
3492
|
}
|
|
3453
3493
|
},
|
|
3454
|
-
/* @__PURE__ */
|
|
3455
|
-
/* @__PURE__ */
|
|
3456
|
-
/* @__PURE__ */
|
|
3457
|
-
/* @__PURE__ */
|
|
3458
|
-
/* @__PURE__ */
|
|
3459
|
-
/* @__PURE__ */
|
|
3460
|
-
), hidden && /* @__PURE__ */
|
|
3494
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
|
|
3495
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
|
|
3496
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
|
|
3497
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
|
|
3498
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
3499
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
3500
|
+
), hidden && /* @__PURE__ */ React10.createElement(Box9, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React10.createElement(Box9, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React10.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "% used"), /* @__PURE__ */ React10.createElement(Typography9, { variant: "caption", color: "text.secondary" }, "Hidden by admin")), /* @__PURE__ */ React10.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 }), /* @__PURE__ */ React10.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 0.75 } }, "Team budget is hidden \u2014 showing usage level only", team.budget_duration ? ` \xB7 resets every ${team.budget_duration}` : "", ".")), !hidden && budget > 0 && /* @__PURE__ */ React10.createElement(Box9, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React10.createElement(Box9, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React10.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React10.createElement(Typography9, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React10.createElement(Meter, { value: budgetPct, tone: budgetTone3(isOver, isNear), height: 5 })), /* @__PURE__ */ React10.createElement(Collapse2, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React10.createElement(Divider3, { sx: { my: 2.5 } }), /* @__PURE__ */ React10.createElement(Box9, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React10.createElement(Box9, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React10.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React10.createElement(Typography9, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React10.createElement(Box9, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React10.createElement(
|
|
3461
3501
|
TableContainer4,
|
|
3462
3502
|
{
|
|
3463
3503
|
component: Paper7,
|
|
3464
3504
|
variant: "outlined",
|
|
3465
3505
|
sx: { borderRadius: 1.5 }
|
|
3466
3506
|
},
|
|
3467
|
-
/* @__PURE__ */
|
|
3507
|
+
/* @__PURE__ */ React10.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React10.createElement(TableHead4, null, /* @__PURE__ */ React10.createElement(TableRow4, null, /* @__PURE__ */ React10.createElement(TableCell4, null, "User"), /* @__PURE__ */ React10.createElement(TableCell4, { align: "right" }, "Role"))), /* @__PURE__ */ React10.createElement(TableBody4, null, team.members_with_roles.map((m) => /* @__PURE__ */ React10.createElement(TableRow4, { key: m.user_id }, /* @__PURE__ */ React10.createElement(TableCell4, null, m.user_email ? /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Typography9, { variant: "body2" }, m.user_email), /* @__PURE__ */ React10.createElement(
|
|
3468
3508
|
Typography9,
|
|
3469
3509
|
{
|
|
3470
3510
|
variant: "caption",
|
|
@@ -3472,14 +3512,14 @@ var init_TeamUsage = __esm({
|
|
|
3472
3512
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3473
3513
|
},
|
|
3474
3514
|
m.user_id
|
|
3475
|
-
)) : /* @__PURE__ */
|
|
3515
|
+
)) : /* @__PURE__ */ React10.createElement(
|
|
3476
3516
|
Typography9,
|
|
3477
3517
|
{
|
|
3478
3518
|
variant: "body2",
|
|
3479
3519
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
3480
3520
|
},
|
|
3481
3521
|
m.user_id
|
|
3482
|
-
)), /* @__PURE__ */
|
|
3522
|
+
)), /* @__PURE__ */ React10.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React10.createElement(
|
|
3483
3523
|
StatusPill,
|
|
3484
3524
|
{
|
|
3485
3525
|
label: m.role,
|
|
@@ -3487,7 +3527,7 @@ var init_TeamUsage = __esm({
|
|
|
3487
3527
|
dot: false
|
|
3488
3528
|
}
|
|
3489
3529
|
))))))
|
|
3490
|
-
) : /* @__PURE__ */
|
|
3530
|
+
) : /* @__PURE__ */ React10.createElement(Typography9, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
|
|
3491
3531
|
};
|
|
3492
3532
|
TeamUsage = ({
|
|
3493
3533
|
teams,
|
|
@@ -3499,22 +3539,22 @@ var init_TeamUsage = __esm({
|
|
|
3499
3539
|
canCreate,
|
|
3500
3540
|
onCreateTeam
|
|
3501
3541
|
}) => {
|
|
3502
|
-
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */
|
|
3542
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React10.createElement(
|
|
3503
3543
|
Button5,
|
|
3504
3544
|
{
|
|
3505
3545
|
variant: "contained",
|
|
3506
3546
|
color: "primary",
|
|
3507
3547
|
disableElevation: true,
|
|
3508
|
-
startIcon: /* @__PURE__ */
|
|
3548
|
+
startIcon: /* @__PURE__ */ React10.createElement(Add2, null),
|
|
3509
3549
|
onClick: onCreateTeam
|
|
3510
3550
|
},
|
|
3511
3551
|
"Create Team"
|
|
3512
3552
|
) : void 0;
|
|
3513
3553
|
if (loading) {
|
|
3514
|
-
return /* @__PURE__ */
|
|
3554
|
+
return /* @__PURE__ */ React10.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React10.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React10.createElement(Skeleton4, { variant: "rounded", height: 120 }));
|
|
3515
3555
|
}
|
|
3516
3556
|
if (!teams.length) {
|
|
3517
|
-
return /* @__PURE__ */
|
|
3557
|
+
return /* @__PURE__ */ React10.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React10.createElement(
|
|
3518
3558
|
EmptyState,
|
|
3519
3559
|
{
|
|
3520
3560
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -3522,14 +3562,14 @@ var init_TeamUsage = __esm({
|
|
|
3522
3562
|
}
|
|
3523
3563
|
));
|
|
3524
3564
|
}
|
|
3525
|
-
return /* @__PURE__ */
|
|
3565
|
+
return /* @__PURE__ */ React10.createElement(
|
|
3526
3566
|
SectionCard,
|
|
3527
3567
|
{
|
|
3528
3568
|
title: "Teams",
|
|
3529
3569
|
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
3530
3570
|
actions: createAction
|
|
3531
3571
|
},
|
|
3532
|
-
/* @__PURE__ */
|
|
3572
|
+
/* @__PURE__ */ React10.createElement(Box9, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React10.createElement(
|
|
3533
3573
|
TeamCard,
|
|
3534
3574
|
{
|
|
3535
3575
|
key: team.team_id,
|
|
@@ -3546,7 +3586,7 @@ var init_TeamUsage = __esm({
|
|
|
3546
3586
|
});
|
|
3547
3587
|
|
|
3548
3588
|
// src/components/ModelsTable.tsx
|
|
3549
|
-
import
|
|
3589
|
+
import React11, { useState as useState7, useMemo as useMemo7, useCallback } from "react";
|
|
3550
3590
|
import Box10 from "@mui/material/Box";
|
|
3551
3591
|
import Table5 from "@mui/material/Table";
|
|
3552
3592
|
import TableBody5 from "@mui/material/TableBody";
|
|
@@ -3678,13 +3718,13 @@ var init_ModelsTable = __esm({
|
|
|
3678
3718
|
return theme.palette.info.main;
|
|
3679
3719
|
}
|
|
3680
3720
|
};
|
|
3681
|
-
return /* @__PURE__ */
|
|
3721
|
+
return /* @__PURE__ */ React11.createElement(
|
|
3682
3722
|
SectionCard,
|
|
3683
3723
|
{
|
|
3684
3724
|
title: "Available Models",
|
|
3685
3725
|
subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
|
|
3686
3726
|
},
|
|
3687
|
-
/* @__PURE__ */
|
|
3727
|
+
/* @__PURE__ */ React11.createElement(Box10, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ React11.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React11.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React11.createElement(
|
|
3688
3728
|
Select,
|
|
3689
3729
|
{
|
|
3690
3730
|
labelId: "team-select-label",
|
|
@@ -3692,8 +3732,8 @@ var init_ModelsTable = __esm({
|
|
|
3692
3732
|
label: "Team",
|
|
3693
3733
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
3694
3734
|
},
|
|
3695
|
-
teams.map((t) => /* @__PURE__ */
|
|
3696
|
-
)), /* @__PURE__ */
|
|
3735
|
+
teams.map((t) => /* @__PURE__ */ React11.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
|
|
3736
|
+
)), /* @__PURE__ */ React11.createElement(
|
|
3697
3737
|
TextField5,
|
|
3698
3738
|
{
|
|
3699
3739
|
label: "Filter Models",
|
|
@@ -3703,33 +3743,33 @@ var init_ModelsTable = __esm({
|
|
|
3703
3743
|
disabled: !selectedTeamId,
|
|
3704
3744
|
sx: { minWidth: 240, mb: 2 },
|
|
3705
3745
|
InputProps: {
|
|
3706
|
-
startAdornment: /* @__PURE__ */
|
|
3746
|
+
startAdornment: /* @__PURE__ */ React11.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React11.createElement(Search2, { fontSize: "small" }))
|
|
3707
3747
|
}
|
|
3708
3748
|
}
|
|
3709
3749
|
)),
|
|
3710
|
-
loading && /* @__PURE__ */
|
|
3711
|
-
!loading && !selectedTeamId && /* @__PURE__ */
|
|
3750
|
+
loading && /* @__PURE__ */ React11.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
3751
|
+
!loading && !selectedTeamId && /* @__PURE__ */ React11.createElement(
|
|
3712
3752
|
EmptyState,
|
|
3713
3753
|
{
|
|
3714
3754
|
message: "Select a team to see available models",
|
|
3715
3755
|
hint: "Each team may have a different set of models assigned"
|
|
3716
3756
|
}
|
|
3717
3757
|
),
|
|
3718
|
-
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */
|
|
3758
|
+
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React11.createElement(
|
|
3719
3759
|
EmptyState,
|
|
3720
3760
|
{
|
|
3721
3761
|
message: "No models available for this team",
|
|
3722
3762
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
3723
3763
|
}
|
|
3724
3764
|
),
|
|
3725
|
-
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */
|
|
3765
|
+
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React11.createElement(
|
|
3726
3766
|
EmptyState,
|
|
3727
3767
|
{
|
|
3728
3768
|
message: "No models match your search",
|
|
3729
3769
|
hint: "Try a different search term"
|
|
3730
3770
|
}
|
|
3731
3771
|
),
|
|
3732
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */
|
|
3772
|
+
!loading && filteredModels.length > 0 && /* @__PURE__ */ React11.createElement(TableContainer5, null, /* @__PURE__ */ React11.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React11.createElement(TableHead5, null, /* @__PURE__ */ React11.createElement(TableRow5, null, /* @__PURE__ */ React11.createElement(TableCell5, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ React11.createElement(TableSortLabel2, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ React11.createElement(TableCell5, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ React11.createElement(TableSortLabel2, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React11.createElement(TableSortLabel2, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React11.createElement(TableSortLabel2, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ React11.createElement(TableSortLabel2, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ React11.createElement(TableSortLabel2, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ React11.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React11.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React11.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React11.createElement(TableCell5, null, /* @__PURE__ */ React11.createElement(Box10, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React11.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React11.createElement(
|
|
3733
3773
|
"span",
|
|
3734
3774
|
{
|
|
3735
3775
|
role: "button",
|
|
@@ -3739,8 +3779,8 @@ var init_ModelsTable = __esm({
|
|
|
3739
3779
|
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
3740
3780
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3741
3781
|
},
|
|
3742
|
-
/* @__PURE__ */
|
|
3743
|
-
)), /* @__PURE__ */
|
|
3782
|
+
/* @__PURE__ */ React11.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
3783
|
+
)), /* @__PURE__ */ React11.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React11.createElement(
|
|
3744
3784
|
Box10,
|
|
3745
3785
|
{
|
|
3746
3786
|
component: "span",
|
|
@@ -3751,8 +3791,8 @@ var init_ModelsTable = __esm({
|
|
|
3751
3791
|
sx: quietIconButtonSx("accent"),
|
|
3752
3792
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3753
3793
|
},
|
|
3754
|
-
/* @__PURE__ */
|
|
3755
|
-
)))), /* @__PURE__ */
|
|
3794
|
+
/* @__PURE__ */ React11.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
|
|
3795
|
+
)))), /* @__PURE__ */ React11.createElement(TableCell5, null, /* @__PURE__ */ React11.createElement(
|
|
3756
3796
|
Box10,
|
|
3757
3797
|
{
|
|
3758
3798
|
component: "span",
|
|
@@ -3769,8 +3809,8 @@ var init_ModelsTable = __esm({
|
|
|
3769
3809
|
}
|
|
3770
3810
|
},
|
|
3771
3811
|
m.mode
|
|
3772
|
-
)), /* @__PURE__ */
|
|
3773
|
-
/* @__PURE__ */
|
|
3812
|
+
)), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React11.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React11.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React11.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React11.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React11.createElement(Typography10, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React11.createElement(TableCell5, null, /* @__PURE__ */ React11.createElement(Box10, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React11.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React11.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
|
|
3813
|
+
/* @__PURE__ */ React11.createElement(
|
|
3774
3814
|
Snackbar,
|
|
3775
3815
|
{
|
|
3776
3816
|
open: copiedSnackbar,
|
|
@@ -3778,7 +3818,7 @@ var init_ModelsTable = __esm({
|
|
|
3778
3818
|
onClose: () => setCopiedSnackbar(false),
|
|
3779
3819
|
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
3780
3820
|
},
|
|
3781
|
-
/* @__PURE__ */
|
|
3821
|
+
/* @__PURE__ */ React11.createElement(Alert4, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
3782
3822
|
)
|
|
3783
3823
|
);
|
|
3784
3824
|
};
|
|
@@ -3786,7 +3826,7 @@ var init_ModelsTable = __esm({
|
|
|
3786
3826
|
});
|
|
3787
3827
|
|
|
3788
3828
|
// src/components/AuditLog.tsx
|
|
3789
|
-
import
|
|
3829
|
+
import React12, { useState as useState8, useMemo as useMemo8 } from "react";
|
|
3790
3830
|
import Box11 from "@mui/material/Box";
|
|
3791
3831
|
import Typography11 from "@mui/material/Typography";
|
|
3792
3832
|
import Table6 from "@mui/material/Table";
|
|
@@ -3825,10 +3865,10 @@ function formatDateTime(iso) {
|
|
|
3825
3865
|
}
|
|
3826
3866
|
function renderAuditLogBody(loading, entries) {
|
|
3827
3867
|
if (loading) {
|
|
3828
|
-
return /* @__PURE__ */
|
|
3868
|
+
return /* @__PURE__ */ React12.createElement(TableRow6, null, /* @__PURE__ */ React12.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React12.createElement(Skeleton5, { variant: "rounded", height: 160 })));
|
|
3829
3869
|
}
|
|
3830
3870
|
if (entries.length === 0) {
|
|
3831
|
-
return /* @__PURE__ */
|
|
3871
|
+
return /* @__PURE__ */ React12.createElement(TableRow6, null, /* @__PURE__ */ React12.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React12.createElement(
|
|
3832
3872
|
EmptyState,
|
|
3833
3873
|
{
|
|
3834
3874
|
message: "No audit events found",
|
|
@@ -3836,7 +3876,7 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3836
3876
|
}
|
|
3837
3877
|
)));
|
|
3838
3878
|
}
|
|
3839
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
3879
|
+
return entries.map((entry) => /* @__PURE__ */ React12.createElement(DetailRow, { key: entry.id, entry }));
|
|
3840
3880
|
}
|
|
3841
3881
|
var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
3842
3882
|
var init_AuditLog = __esm({
|
|
@@ -3860,7 +3900,7 @@ var init_AuditLog = __esm({
|
|
|
3860
3900
|
DetailRow = ({ entry }) => {
|
|
3861
3901
|
const [open, setOpen] = useState8(false);
|
|
3862
3902
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3863
|
-
return /* @__PURE__ */
|
|
3903
|
+
return /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(TableRow6, null, /* @__PURE__ */ React12.createElement(TableCell6, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React12.createElement(
|
|
3864
3904
|
IconButton6,
|
|
3865
3905
|
{
|
|
3866
3906
|
size: "small",
|
|
@@ -3873,8 +3913,8 @@ var init_AuditLog = __esm({
|
|
|
3873
3913
|
transition: theme.transitions.create("transform")
|
|
3874
3914
|
})
|
|
3875
3915
|
},
|
|
3876
|
-
/* @__PURE__ */
|
|
3877
|
-
)), /* @__PURE__ */
|
|
3916
|
+
/* @__PURE__ */ React12.createElement(KeyboardArrowDown, { fontSize: "small" })
|
|
3917
|
+
)), /* @__PURE__ */ React12.createElement(TableCell6, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React12.createElement(Typography11, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React12.createElement(TableCell6, null, entry.action && /* @__PURE__ */ React12.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React12.createElement(TableCell6, null, /* @__PURE__ */ React12.createElement(Typography11, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React12.createElement(TableCell6, null, /* @__PURE__ */ React12.createElement(
|
|
3878
3918
|
Typography11,
|
|
3879
3919
|
{
|
|
3880
3920
|
variant: "body2",
|
|
@@ -3884,7 +3924,7 @@ var init_AuditLog = __esm({
|
|
|
3884
3924
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3885
3925
|
},
|
|
3886
3926
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3887
|
-
)), /* @__PURE__ */
|
|
3927
|
+
)), /* @__PURE__ */ React12.createElement(TableCell6, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React12.createElement(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React12.createElement(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React12.createElement(Collapse3, { in: open, unmountOnExit: true }, /* @__PURE__ */ React12.createElement(
|
|
3888
3928
|
Box11,
|
|
3889
3929
|
{
|
|
3890
3930
|
display: "flex",
|
|
@@ -3897,7 +3937,7 @@ var init_AuditLog = __esm({
|
|
|
3897
3937
|
bgcolor: alpha8(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
3898
3938
|
})
|
|
3899
3939
|
},
|
|
3900
|
-
entry.before_value && /* @__PURE__ */
|
|
3940
|
+
entry.before_value && /* @__PURE__ */ React12.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React12.createElement(
|
|
3901
3941
|
Typography11,
|
|
3902
3942
|
{
|
|
3903
3943
|
variant: "caption",
|
|
@@ -3907,8 +3947,8 @@ var init_AuditLog = __esm({
|
|
|
3907
3947
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3908
3948
|
},
|
|
3909
3949
|
"Before"
|
|
3910
|
-
), /* @__PURE__ */
|
|
3911
|
-
entry.updated_values && /* @__PURE__ */
|
|
3950
|
+
), /* @__PURE__ */ React12.createElement(Typography11, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
|
|
3951
|
+
entry.updated_values && /* @__PURE__ */ React12.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React12.createElement(
|
|
3912
3952
|
Typography11,
|
|
3913
3953
|
{
|
|
3914
3954
|
variant: "caption",
|
|
@@ -3918,7 +3958,7 @@ var init_AuditLog = __esm({
|
|
|
3918
3958
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3919
3959
|
},
|
|
3920
3960
|
"After"
|
|
3921
|
-
), /* @__PURE__ */
|
|
3961
|
+
), /* @__PURE__ */ React12.createElement(Typography11, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
|
|
3922
3962
|
)))));
|
|
3923
3963
|
};
|
|
3924
3964
|
AuditLog = ({ api }) => {
|
|
@@ -3935,13 +3975,13 @@ var init_AuditLog = __esm({
|
|
|
3935
3975
|
);
|
|
3936
3976
|
const entries = value?.audit_logs ?? [];
|
|
3937
3977
|
const total = value?.total ?? 0;
|
|
3938
|
-
return /* @__PURE__ */
|
|
3978
|
+
return /* @__PURE__ */ React12.createElement(
|
|
3939
3979
|
SectionCard,
|
|
3940
3980
|
{
|
|
3941
3981
|
title: "Audit Log",
|
|
3942
3982
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
3943
3983
|
flush: true,
|
|
3944
|
-
actions: /* @__PURE__ */
|
|
3984
|
+
actions: /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(
|
|
3945
3985
|
TextField6,
|
|
3946
3986
|
{
|
|
3947
3987
|
size: "small",
|
|
@@ -3954,12 +3994,12 @@ var init_AuditLog = __esm({
|
|
|
3954
3994
|
},
|
|
3955
3995
|
sx: { minWidth: 150 }
|
|
3956
3996
|
},
|
|
3957
|
-
/* @__PURE__ */
|
|
3958
|
-
/* @__PURE__ */
|
|
3959
|
-
/* @__PURE__ */
|
|
3960
|
-
/* @__PURE__ */
|
|
3961
|
-
/* @__PURE__ */
|
|
3962
|
-
), /* @__PURE__ */
|
|
3997
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "" }, "All actions"),
|
|
3998
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "created" }, "Created"),
|
|
3999
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "updated" }, "Updated"),
|
|
4000
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
|
|
4001
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "blocked" }, "Blocked")
|
|
4002
|
+
), /* @__PURE__ */ React12.createElement(
|
|
3963
4003
|
TextField6,
|
|
3964
4004
|
{
|
|
3965
4005
|
size: "small",
|
|
@@ -3972,13 +4012,13 @@ var init_AuditLog = __esm({
|
|
|
3972
4012
|
},
|
|
3973
4013
|
sx: { minWidth: 150 }
|
|
3974
4014
|
},
|
|
3975
|
-
/* @__PURE__ */
|
|
3976
|
-
/* @__PURE__ */
|
|
3977
|
-
/* @__PURE__ */
|
|
3978
|
-
/* @__PURE__ */
|
|
3979
|
-
/* @__PURE__ */
|
|
3980
|
-
/* @__PURE__ */
|
|
3981
|
-
), /* @__PURE__ */
|
|
4015
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "" }, "All tables"),
|
|
4016
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
4017
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
4018
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
|
|
4019
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
|
|
4020
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
|
|
4021
|
+
), /* @__PURE__ */ React12.createElement(
|
|
3982
4022
|
TextField6,
|
|
3983
4023
|
{
|
|
3984
4024
|
size: "small",
|
|
@@ -3992,9 +4032,9 @@ var init_AuditLog = __esm({
|
|
|
3992
4032
|
}
|
|
3993
4033
|
))
|
|
3994
4034
|
},
|
|
3995
|
-
error && /* @__PURE__ */
|
|
3996
|
-
/* @__PURE__ */
|
|
3997
|
-
/* @__PURE__ */
|
|
4035
|
+
error && /* @__PURE__ */ React12.createElement(Box11, { px: 2.5, pb: 2 }, /* @__PURE__ */ React12.createElement(Alert5, { severity: "error" }, error.message)),
|
|
4036
|
+
/* @__PURE__ */ React12.createElement(TableContainer6, null, /* @__PURE__ */ React12.createElement(Table6, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React12.createElement(TableHead6, null, /* @__PURE__ */ React12.createElement(TableRow6, null, /* @__PURE__ */ React12.createElement(TableCell6, { sx: { width: 40 } }), /* @__PURE__ */ React12.createElement(TableCell6, null, "Time"), /* @__PURE__ */ React12.createElement(TableCell6, null, "Action"), /* @__PURE__ */ React12.createElement(TableCell6, null, "Table"), /* @__PURE__ */ React12.createElement(TableCell6, null, "Object ID"), /* @__PURE__ */ React12.createElement(TableCell6, null, "Changed By"))), /* @__PURE__ */ React12.createElement(TableBody6, null, renderAuditLogBody(loading, entries)))),
|
|
4037
|
+
/* @__PURE__ */ React12.createElement(
|
|
3998
4038
|
TablePagination,
|
|
3999
4039
|
{
|
|
4000
4040
|
component: "div",
|
|
@@ -4049,7 +4089,7 @@ var LiteLLMPage_exports = {};
|
|
|
4049
4089
|
__export(LiteLLMPage_exports, {
|
|
4050
4090
|
LiteLLMPage: () => LiteLLMPage
|
|
4051
4091
|
});
|
|
4052
|
-
import
|
|
4092
|
+
import React13, { useState as useState9, useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo9 } from "react";
|
|
4053
4093
|
import Box12 from "@mui/material/Box";
|
|
4054
4094
|
import Snackbar2 from "@mui/material/Snackbar";
|
|
4055
4095
|
import Alert6 from "@mui/material/Alert";
|
|
@@ -4335,14 +4375,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4335
4375
|
);
|
|
4336
4376
|
const isInitialLoading = userLoading && !userInfo;
|
|
4337
4377
|
if (isInitialLoading) {
|
|
4338
|
-
return /* @__PURE__ */
|
|
4378
|
+
return /* @__PURE__ */ React13.createElement(Box12, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React13.createElement(CircularProgress6, null));
|
|
4339
4379
|
}
|
|
4340
4380
|
if (userError || !userInfo) {
|
|
4341
4381
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
4342
4382
|
const hint = userError?.body?.hint;
|
|
4343
|
-
return /* @__PURE__ */
|
|
4383
|
+
return /* @__PURE__ */ React13.createElement(Box12, { p: 3 }, /* @__PURE__ */ React13.createElement(Paper8, { sx: { p: 3 } }, /* @__PURE__ */ React13.createElement(Typography12, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React13.createElement(Typography12, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React13.createElement(Typography12, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React13.createElement(Typography12, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
|
|
4344
4384
|
}
|
|
4345
|
-
const pageTabs = /* @__PURE__ */
|
|
4385
|
+
const pageTabs = /* @__PURE__ */ React13.createElement(
|
|
4346
4386
|
Tabs2,
|
|
4347
4387
|
{
|
|
4348
4388
|
value: activeTab,
|
|
@@ -4355,13 +4395,13 @@ var init_LiteLLMPage = __esm({
|
|
|
4355
4395
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
4356
4396
|
}
|
|
4357
4397
|
},
|
|
4358
|
-
/* @__PURE__ */
|
|
4359
|
-
/* @__PURE__ */
|
|
4360
|
-
/* @__PURE__ */
|
|
4361
|
-
/* @__PURE__ */
|
|
4362
|
-
userInfo.can_view_audit && /* @__PURE__ */
|
|
4398
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Overview", value: "overview" }),
|
|
4399
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Keys", value: "keys" }),
|
|
4400
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Teams", value: "teams" }),
|
|
4401
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Models", value: "models" }),
|
|
4402
|
+
userInfo.can_view_audit && /* @__PURE__ */ React13.createElement(Tab2, { label: "Audit Log", value: "audit" })
|
|
4363
4403
|
);
|
|
4364
|
-
return /* @__PURE__ */
|
|
4404
|
+
return /* @__PURE__ */ React13.createElement(Box12, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React13.createElement(
|
|
4365
4405
|
DashboardHeader,
|
|
4366
4406
|
{
|
|
4367
4407
|
userInfo,
|
|
@@ -4371,7 +4411,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4371
4411
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4372
4412
|
tabs: pageTabs
|
|
4373
4413
|
}
|
|
4374
|
-
), activeTab === "overview" && /* @__PURE__ */
|
|
4414
|
+
), activeTab === "overview" && /* @__PURE__ */ React13.createElement(Grid2, { container: true, spacing: 2, alignItems: "flex-start" }, /* @__PURE__ */ React13.createElement(Grid2, { item: true, xs: 12, lg: 8 }, /* @__PURE__ */ React13.createElement(
|
|
4375
4415
|
UsageStats,
|
|
4376
4416
|
{
|
|
4377
4417
|
usage: usage ?? null,
|
|
@@ -4382,7 +4422,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4382
4422
|
loading: usageLoading,
|
|
4383
4423
|
userInfo
|
|
4384
4424
|
}
|
|
4385
|
-
)), /* @__PURE__ */
|
|
4425
|
+
)), /* @__PURE__ */ React13.createElement(Grid2, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ React13.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ React13.createElement(
|
|
4386
4426
|
KeysTable,
|
|
4387
4427
|
{
|
|
4388
4428
|
keys: keys ?? [],
|
|
@@ -4399,7 +4439,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4399
4439
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
4400
4440
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
4401
4441
|
const visibleTeams = Array.from(teamsById.values());
|
|
4402
|
-
return /* @__PURE__ */
|
|
4442
|
+
return /* @__PURE__ */ React13.createElement(
|
|
4403
4443
|
TeamUsage,
|
|
4404
4444
|
{
|
|
4405
4445
|
teams: visibleTeams,
|
|
@@ -4415,14 +4455,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4415
4455
|
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
4416
4456
|
}
|
|
4417
4457
|
);
|
|
4418
|
-
})(), activeTab === "models" && /* @__PURE__ */
|
|
4458
|
+
})(), activeTab === "models" && /* @__PURE__ */ React13.createElement(
|
|
4419
4459
|
ModelsTable,
|
|
4420
4460
|
{
|
|
4421
4461
|
allModels: allModels ?? [],
|
|
4422
4462
|
teams: teams ?? [],
|
|
4423
4463
|
loading: modelsLoading
|
|
4424
4464
|
}
|
|
4425
|
-
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */
|
|
4465
|
+
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React13.createElement(AuditLog, { api }), /* @__PURE__ */ React13.createElement(
|
|
4426
4466
|
KeyFormDialog,
|
|
4427
4467
|
{
|
|
4428
4468
|
open: keyFormOpen,
|
|
@@ -4442,7 +4482,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4442
4482
|
onResetKeySpend: handleResetKeySpend,
|
|
4443
4483
|
onGetConfig: () => api.getConfig()
|
|
4444
4484
|
}
|
|
4445
|
-
), /* @__PURE__ */
|
|
4485
|
+
), /* @__PURE__ */ React13.createElement(
|
|
4446
4486
|
ManageTeamDialog,
|
|
4447
4487
|
{
|
|
4448
4488
|
open: !!manageTeam,
|
|
@@ -4504,7 +4544,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4504
4544
|
refreshTeams();
|
|
4505
4545
|
}
|
|
4506
4546
|
}
|
|
4507
|
-
), /* @__PURE__ */
|
|
4547
|
+
), /* @__PURE__ */ React13.createElement(
|
|
4508
4548
|
Snackbar2,
|
|
4509
4549
|
{
|
|
4510
4550
|
open: !!snackbar,
|
|
@@ -4512,7 +4552,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4512
4552
|
onClose: () => setSnackbar(null),
|
|
4513
4553
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
4514
4554
|
},
|
|
4515
|
-
snackbar ? /* @__PURE__ */
|
|
4555
|
+
snackbar ? /* @__PURE__ */ React13.createElement(Alert6, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
4516
4556
|
));
|
|
4517
4557
|
};
|
|
4518
4558
|
}
|
|
@@ -4520,7 +4560,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4520
4560
|
|
|
4521
4561
|
// src/plugin.tsx
|
|
4522
4562
|
init_api();
|
|
4523
|
-
import
|
|
4563
|
+
import React14 from "react";
|
|
4524
4564
|
import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
|
|
4525
4565
|
import {
|
|
4526
4566
|
createFrontendPlugin,
|
|
@@ -4539,10 +4579,10 @@ var liteLlmPage = PageBlueprint.make({
|
|
|
4539
4579
|
params: {
|
|
4540
4580
|
path: "/litellm",
|
|
4541
4581
|
title: "LiteLLM",
|
|
4542
|
-
icon: /* @__PURE__ */
|
|
4582
|
+
icon: /* @__PURE__ */ React14.createElement(TrendingUpIcon, null),
|
|
4543
4583
|
loader: async () => {
|
|
4544
4584
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
4545
|
-
return /* @__PURE__ */
|
|
4585
|
+
return /* @__PURE__ */ React14.createElement(LiteLLMPage2, null);
|
|
4546
4586
|
}
|
|
4547
4587
|
}
|
|
4548
4588
|
});
|
|
@@ -4561,7 +4601,7 @@ init_TeamUsage();
|
|
|
4561
4601
|
// src/components/LiteLLMHomeWidget.tsx
|
|
4562
4602
|
init_api();
|
|
4563
4603
|
init_format();
|
|
4564
|
-
import
|
|
4604
|
+
import React15, { useState as useState10, useEffect as useEffect5 } from "react";
|
|
4565
4605
|
import Paper9 from "@mui/material/Paper";
|
|
4566
4606
|
import Box13 from "@mui/material/Box";
|
|
4567
4607
|
import Typography13 from "@mui/material/Typography";
|
|
@@ -4585,7 +4625,7 @@ function presetToDateRange(preset) {
|
|
|
4585
4625
|
}
|
|
4586
4626
|
return { start, end };
|
|
4587
4627
|
}
|
|
4588
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
4628
|
+
var Kpi = ({ label, value }) => /* @__PURE__ */ React15.createElement(Box13, null, /* @__PURE__ */ React15.createElement(Typography13, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React15.createElement(Typography13, { variant: "subtitle1", fontWeight: 600 }, value));
|
|
4589
4629
|
var LiteLLMHomeWidget = ({
|
|
4590
4630
|
defaultPeriod = "7d",
|
|
4591
4631
|
title = "LiteLLM Usage"
|
|
@@ -4634,17 +4674,17 @@ var LiteLLMHomeWidget = ({
|
|
|
4634
4674
|
spend: d.spend
|
|
4635
4675
|
}));
|
|
4636
4676
|
const hasSparkline = dailyData.length > 0;
|
|
4637
|
-
return /* @__PURE__ */
|
|
4677
|
+
return /* @__PURE__ */ React15.createElement(Paper9, { sx: { p: 2 } }, /* @__PURE__ */ React15.createElement(Box13, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React15.createElement(Typography13, { variant: "h6" }, title), /* @__PURE__ */ React15.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React15.createElement(
|
|
4638
4678
|
Select2,
|
|
4639
4679
|
{
|
|
4640
4680
|
value: period,
|
|
4641
4681
|
onChange: (e) => setPeriod(e.target.value),
|
|
4642
4682
|
displayEmpty: true
|
|
4643
4683
|
},
|
|
4644
|
-
/* @__PURE__ */
|
|
4645
|
-
/* @__PURE__ */
|
|
4646
|
-
/* @__PURE__ */
|
|
4647
|
-
))), loading && /* @__PURE__ */
|
|
4684
|
+
/* @__PURE__ */ React15.createElement(MenuItem6, { value: "today" }, "Today"),
|
|
4685
|
+
/* @__PURE__ */ React15.createElement(MenuItem6, { value: "7d" }, "7d"),
|
|
4686
|
+
/* @__PURE__ */ React15.createElement(MenuItem6, { value: "30d" }, "30d")
|
|
4687
|
+
))), loading && /* @__PURE__ */ React15.createElement(Box13, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React15.createElement(CircularProgress7, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React15.createElement(Alert7, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React15.createElement(React15.Fragment, null, partialFailure && /* @__PURE__ */ React15.createElement(Alert7, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React15.createElement(Grid3, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React15.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React15.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React15.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React15.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React15.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React15.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React15.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React15.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React15.createElement(Box13, { height: 120 }, /* @__PURE__ */ React15.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React15.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React15.createElement(
|
|
4648
4688
|
Area3,
|
|
4649
4689
|
{
|
|
4650
4690
|
type: "monotone",
|
|
@@ -4665,9 +4705,10 @@ init_LiteLLMBudgetWidget();
|
|
|
4665
4705
|
init_api();
|
|
4666
4706
|
init_format();
|
|
4667
4707
|
init_ui();
|
|
4708
|
+
init_GenerateKeyButton();
|
|
4668
4709
|
init_BudgetLimitList();
|
|
4669
4710
|
init_budget();
|
|
4670
|
-
import
|
|
4711
|
+
import React16, { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo10, useState as useState11 } from "react";
|
|
4671
4712
|
import Paper10 from "@mui/material/Paper";
|
|
4672
4713
|
import Box14 from "@mui/material/Box";
|
|
4673
4714
|
import Button6 from "@mui/material/Button";
|
|
@@ -4676,8 +4717,9 @@ import CircularProgress8 from "@mui/material/CircularProgress";
|
|
|
4676
4717
|
import Alert8 from "@mui/material/Alert";
|
|
4677
4718
|
import Collapse4 from "@mui/material/Collapse";
|
|
4678
4719
|
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
4720
|
+
import { AreaChart as AreaChart4, Area as Area4, ResponsiveContainer as ResponsiveContainer4, Tooltip as Tooltip4 } from "recharts";
|
|
4679
4721
|
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
4680
|
-
import { Link as
|
|
4722
|
+
import { Link as Link3 } from "@backstage/core-components";
|
|
4681
4723
|
var MODULE_PATH = "/litellm";
|
|
4682
4724
|
var LEVEL = {
|
|
4683
4725
|
key: { name: "Key", none: "No key budget" },
|
|
@@ -4686,7 +4728,7 @@ var LEVEL = {
|
|
|
4686
4728
|
};
|
|
4687
4729
|
var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
|
|
4688
4730
|
var CTA_LABELS = {
|
|
4689
|
-
"new-key": "New
|
|
4731
|
+
"new-key": "Generate New Key",
|
|
4690
4732
|
module: "Open module",
|
|
4691
4733
|
"all-limits": "All limits"
|
|
4692
4734
|
};
|
|
@@ -4712,7 +4754,7 @@ var LevelGauge = ({
|
|
|
4712
4754
|
const limit = gauge.limit;
|
|
4713
4755
|
const tone = limit ? budgetTone2(limit.pct) : "neutral";
|
|
4714
4756
|
const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
|
|
4715
|
-
return /* @__PURE__ */
|
|
4757
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4716
4758
|
Box14,
|
|
4717
4759
|
{
|
|
4718
4760
|
sx: {
|
|
@@ -4723,7 +4765,7 @@ var LevelGauge = ({
|
|
|
4723
4765
|
flex: 1
|
|
4724
4766
|
}
|
|
4725
4767
|
},
|
|
4726
|
-
/* @__PURE__ */
|
|
4768
|
+
/* @__PURE__ */ React16.createElement(
|
|
4727
4769
|
Gauge,
|
|
4728
4770
|
{
|
|
4729
4771
|
value: limit?.pct ?? 0,
|
|
@@ -4732,7 +4774,7 @@ var LevelGauge = ({
|
|
|
4732
4774
|
label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
|
|
4733
4775
|
}
|
|
4734
4776
|
),
|
|
4735
|
-
/* @__PURE__ */
|
|
4777
|
+
/* @__PURE__ */ React16.createElement(
|
|
4736
4778
|
Typography14,
|
|
4737
4779
|
{
|
|
4738
4780
|
sx: (theme) => ({
|
|
@@ -4746,7 +4788,7 @@ var LevelGauge = ({
|
|
|
4746
4788
|
},
|
|
4747
4789
|
name
|
|
4748
4790
|
),
|
|
4749
|
-
limit ? /* @__PURE__ */
|
|
4791
|
+
limit ? /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(
|
|
4750
4792
|
Typography14,
|
|
4751
4793
|
{
|
|
4752
4794
|
variant: "caption",
|
|
@@ -4762,7 +4804,7 @@ var LevelGauge = ({
|
|
|
4762
4804
|
}
|
|
4763
4805
|
},
|
|
4764
4806
|
limit.label
|
|
4765
|
-
), /* @__PURE__ */
|
|
4807
|
+
), /* @__PURE__ */ React16.createElement(
|
|
4766
4808
|
Typography14,
|
|
4767
4809
|
{
|
|
4768
4810
|
variant: "caption",
|
|
@@ -4774,10 +4816,10 @@ var LevelGauge = ({
|
|
|
4774
4816
|
}
|
|
4775
4817
|
},
|
|
4776
4818
|
spendCaption(limit)
|
|
4777
|
-
), extra > 0 ? /* @__PURE__ */
|
|
4819
|
+
), extra > 0 ? /* @__PURE__ */ React16.createElement(
|
|
4778
4820
|
Typography14,
|
|
4779
4821
|
{
|
|
4780
|
-
component:
|
|
4822
|
+
component: Link3,
|
|
4781
4823
|
to: keysHref,
|
|
4782
4824
|
variant: "caption",
|
|
4783
4825
|
sx: {
|
|
@@ -4790,7 +4832,7 @@ var LevelGauge = ({
|
|
|
4790
4832
|
"+",
|
|
4791
4833
|
extra,
|
|
4792
4834
|
" more"
|
|
4793
|
-
) : /* @__PURE__ */
|
|
4835
|
+
) : /* @__PURE__ */ React16.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ React16.createElement(
|
|
4794
4836
|
Typography14,
|
|
4795
4837
|
{
|
|
4796
4838
|
variant: "caption",
|
|
@@ -4801,6 +4843,125 @@ var LevelGauge = ({
|
|
|
4801
4843
|
)
|
|
4802
4844
|
);
|
|
4803
4845
|
};
|
|
4846
|
+
function monthToDateRange(now = /* @__PURE__ */ new Date()) {
|
|
4847
|
+
const toDay = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
|
|
4848
|
+
d.getDate()
|
|
4849
|
+
).padStart(2, "0")}`;
|
|
4850
|
+
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
4851
|
+
return { startDate: toDay(start), endDate: toDay(now) };
|
|
4852
|
+
}
|
|
4853
|
+
var UsageMiniChart = ({ data, totalTokens, loading, height }) => {
|
|
4854
|
+
const renderPlot = () => {
|
|
4855
|
+
if (loading) {
|
|
4856
|
+
return /* @__PURE__ */ React16.createElement(Box14, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%" }, /* @__PURE__ */ React16.createElement(CircularProgress8, { size: 20 }));
|
|
4857
|
+
}
|
|
4858
|
+
if (data.length === 0) {
|
|
4859
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4860
|
+
Box14,
|
|
4861
|
+
{
|
|
4862
|
+
display: "flex",
|
|
4863
|
+
justifyContent: "center",
|
|
4864
|
+
alignItems: "center",
|
|
4865
|
+
height: "100%",
|
|
4866
|
+
sx: (theme) => ({ color: theme.palette.text.secondary, fontSize: 18 })
|
|
4867
|
+
},
|
|
4868
|
+
"\u2014"
|
|
4869
|
+
);
|
|
4870
|
+
}
|
|
4871
|
+
return /* @__PURE__ */ React16.createElement(ResponsiveContainer4, { width: "100%", height: "100%" }, /* @__PURE__ */ React16.createElement(AreaChart4, { data, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React16.createElement(Tooltip4, { content: /* @__PURE__ */ React16.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ React16.createElement(
|
|
4872
|
+
Area4,
|
|
4873
|
+
{
|
|
4874
|
+
type: "monotone",
|
|
4875
|
+
dataKey: "input",
|
|
4876
|
+
name: "Input",
|
|
4877
|
+
stackId: "tok",
|
|
4878
|
+
stroke: SERIES.input,
|
|
4879
|
+
strokeWidth: 1.5,
|
|
4880
|
+
fill: SERIES.input,
|
|
4881
|
+
fillOpacity: 0.25,
|
|
4882
|
+
dot: false,
|
|
4883
|
+
isAnimationActive: false
|
|
4884
|
+
}
|
|
4885
|
+
), /* @__PURE__ */ React16.createElement(
|
|
4886
|
+
Area4,
|
|
4887
|
+
{
|
|
4888
|
+
type: "monotone",
|
|
4889
|
+
dataKey: "output",
|
|
4890
|
+
name: "Output",
|
|
4891
|
+
stackId: "tok",
|
|
4892
|
+
stroke: SERIES.output,
|
|
4893
|
+
strokeWidth: 1.5,
|
|
4894
|
+
fill: SERIES.output,
|
|
4895
|
+
fillOpacity: 0.25,
|
|
4896
|
+
dot: false,
|
|
4897
|
+
isAnimationActive: false
|
|
4898
|
+
}
|
|
4899
|
+
)));
|
|
4900
|
+
};
|
|
4901
|
+
const renderCaption = () => {
|
|
4902
|
+
if (loading) {
|
|
4903
|
+
return /* @__PURE__ */ React16.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { mt: 0.25, fontSize: 11 } }, "\u2026");
|
|
4904
|
+
}
|
|
4905
|
+
if (data.length === 0) {
|
|
4906
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4907
|
+
Typography14,
|
|
4908
|
+
{
|
|
4909
|
+
variant: "caption",
|
|
4910
|
+
color: "text.secondary",
|
|
4911
|
+
sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
|
|
4912
|
+
},
|
|
4913
|
+
"No usage this month"
|
|
4914
|
+
);
|
|
4915
|
+
}
|
|
4916
|
+
return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(
|
|
4917
|
+
Typography14,
|
|
4918
|
+
{
|
|
4919
|
+
variant: "caption",
|
|
4920
|
+
title: `${fmtInt(totalTokens)} tokens month to date`,
|
|
4921
|
+
sx: {
|
|
4922
|
+
mt: 0.25,
|
|
4923
|
+
fontSize: 11,
|
|
4924
|
+
fontWeight: 600,
|
|
4925
|
+
maxWidth: "100%",
|
|
4926
|
+
overflow: "hidden",
|
|
4927
|
+
textOverflow: "ellipsis",
|
|
4928
|
+
whiteSpace: "nowrap",
|
|
4929
|
+
fontVariantNumeric: "tabular-nums"
|
|
4930
|
+
}
|
|
4931
|
+
},
|
|
4932
|
+
fmtCompact(totalTokens),
|
|
4933
|
+
" tokens"
|
|
4934
|
+
), /* @__PURE__ */ React16.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, "month to date"));
|
|
4935
|
+
};
|
|
4936
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4937
|
+
Box14,
|
|
4938
|
+
{
|
|
4939
|
+
sx: {
|
|
4940
|
+
display: "flex",
|
|
4941
|
+
flexDirection: "column",
|
|
4942
|
+
alignItems: "center",
|
|
4943
|
+
minWidth: 0,
|
|
4944
|
+
flex: 1
|
|
4945
|
+
}
|
|
4946
|
+
},
|
|
4947
|
+
/* @__PURE__ */ React16.createElement(Box14, { sx: { height, width: "100%" } }, renderPlot()),
|
|
4948
|
+
/* @__PURE__ */ React16.createElement(
|
|
4949
|
+
Typography14,
|
|
4950
|
+
{
|
|
4951
|
+
sx: (theme) => ({
|
|
4952
|
+
mt: 0.75,
|
|
4953
|
+
fontSize: 10.5,
|
|
4954
|
+
fontWeight: 700,
|
|
4955
|
+
letterSpacing: "0.08em",
|
|
4956
|
+
textTransform: "uppercase",
|
|
4957
|
+
color: theme.palette.text.secondary
|
|
4958
|
+
})
|
|
4959
|
+
},
|
|
4960
|
+
"Usage"
|
|
4961
|
+
),
|
|
4962
|
+
renderCaption()
|
|
4963
|
+
);
|
|
4964
|
+
};
|
|
4804
4965
|
var LiteLLMBudgetGauges = ({
|
|
4805
4966
|
title = "Budget",
|
|
4806
4967
|
size = 72,
|
|
@@ -4820,6 +4981,9 @@ var LiteLLMBudgetGauges = ({
|
|
|
4820
4981
|
const [user, setUser] = useState11(null);
|
|
4821
4982
|
const [teams, setTeams] = useState11([]);
|
|
4822
4983
|
const [keys, setKeys] = useState11([]);
|
|
4984
|
+
const [usageLoading, setUsageLoading] = useState11(true);
|
|
4985
|
+
const [dailyTokens, setDailyTokens] = useState11([]);
|
|
4986
|
+
const [mtdTokens, setMtdTokens] = useState11(0);
|
|
4823
4987
|
const isControlled = expanded !== void 0;
|
|
4824
4988
|
const [uncontrolledExpanded, setUncontrolledExpanded] = useState11(defaultExpanded);
|
|
4825
4989
|
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
@@ -4832,23 +4996,43 @@ var LiteLLMBudgetGauges = ({
|
|
|
4832
4996
|
useEffect6(() => {
|
|
4833
4997
|
let cancelled = false;
|
|
4834
4998
|
setLoading(true);
|
|
4999
|
+
setUsageLoading(true);
|
|
4835
5000
|
setError(null);
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
5001
|
+
const { startDate, endDate } = monthToDateRange();
|
|
5002
|
+
Promise.allSettled([
|
|
5003
|
+
api.getUserInfo(),
|
|
5004
|
+
api.getTeams(),
|
|
5005
|
+
api.listKeys(),
|
|
5006
|
+
api.getUsage(startDate, endDate)
|
|
5007
|
+
]).then(([userResult, teamsResult, keysResult, usageResult]) => {
|
|
5008
|
+
if (cancelled) return;
|
|
5009
|
+
if (userResult.status === "fulfilled") {
|
|
5010
|
+
setUser(userResult.value);
|
|
5011
|
+
} else {
|
|
5012
|
+
setUser(null);
|
|
5013
|
+
setError(
|
|
5014
|
+
userResult.reason?.message ?? "Failed to load your LiteLLM profile"
|
|
5015
|
+
);
|
|
4850
5016
|
}
|
|
4851
|
-
|
|
5017
|
+
setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
|
|
5018
|
+
setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
|
|
5019
|
+
setLoading(false);
|
|
5020
|
+
if (usageResult.status === "fulfilled") {
|
|
5021
|
+
const daily = usageResult.value.daily_usage ?? [];
|
|
5022
|
+
setDailyTokens(
|
|
5023
|
+
daily.map((d) => ({
|
|
5024
|
+
date: d.date,
|
|
5025
|
+
input: d.prompt_tokens,
|
|
5026
|
+
output: d.completion_tokens
|
|
5027
|
+
}))
|
|
5028
|
+
);
|
|
5029
|
+
setMtdTokens(usageResult.value.total_tokens ?? 0);
|
|
5030
|
+
} else {
|
|
5031
|
+
setDailyTokens([]);
|
|
5032
|
+
setMtdTokens(0);
|
|
5033
|
+
}
|
|
5034
|
+
setUsageLoading(false);
|
|
5035
|
+
});
|
|
4852
5036
|
return () => {
|
|
4853
5037
|
cancelled = true;
|
|
4854
5038
|
};
|
|
@@ -4877,28 +5061,18 @@ var LiteLLMBudgetGauges = ({
|
|
|
4877
5061
|
const key = `${cta.kind}-${index}`;
|
|
4878
5062
|
switch (cta.kind) {
|
|
4879
5063
|
case "new-key":
|
|
4880
|
-
return onCreateKey ? /* @__PURE__ */
|
|
4881
|
-
Button6,
|
|
4882
|
-
{
|
|
4883
|
-
key,
|
|
4884
|
-
size: "small",
|
|
4885
|
-
variant: "contained",
|
|
4886
|
-
component: Link2,
|
|
4887
|
-
to: `${moduleHref}?generate=1`
|
|
4888
|
-
},
|
|
4889
|
-
label
|
|
4890
|
-
);
|
|
5064
|
+
return onCreateKey ? /* @__PURE__ */ React16.createElement(GenerateKeyButton, { key, size: "small", label, onClick: onCreateKey }) : /* @__PURE__ */ React16.createElement(GenerateKeyButton, { key, size: "small", label, to: `${moduleHref}?generate=1` });
|
|
4891
5065
|
case "module":
|
|
4892
|
-
return /* @__PURE__ */
|
|
5066
|
+
return /* @__PURE__ */ React16.createElement(Button6, { key, size: "small", variant: "outlined", component: Link3, to: moduleHref }, label);
|
|
4893
5067
|
case "all-limits":
|
|
4894
|
-
return /* @__PURE__ */
|
|
5068
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4895
5069
|
Button6,
|
|
4896
5070
|
{
|
|
4897
5071
|
key,
|
|
4898
5072
|
size: "small",
|
|
4899
5073
|
variant: "text",
|
|
4900
5074
|
onClick: toggleExpanded,
|
|
4901
|
-
endIcon: /* @__PURE__ */
|
|
5075
|
+
endIcon: /* @__PURE__ */ React16.createElement(
|
|
4902
5076
|
ExpandMore4,
|
|
4903
5077
|
{
|
|
4904
5078
|
sx: {
|
|
@@ -4916,14 +5090,22 @@ var LiteLLMBudgetGauges = ({
|
|
|
4916
5090
|
}
|
|
4917
5091
|
};
|
|
4918
5092
|
const hasFooter = resolvedCtas.length > 0 || !!action;
|
|
4919
|
-
return /* @__PURE__ */
|
|
5093
|
+
return /* @__PURE__ */ React16.createElement(Paper10, { sx: { p: 2 } }, /* @__PURE__ */ React16.createElement(Box14, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1.5, minWidth: 0 } }, /* @__PURE__ */ React16.createElement(Typography14, { variant: "h6", lineHeight: 1.2, sx: { minWidth: 0, flex: 1 } }, title), !loading && !error && /* @__PURE__ */ React16.createElement(
|
|
4920
5094
|
StatusPill,
|
|
4921
5095
|
{
|
|
4922
5096
|
label: summaryText,
|
|
4923
5097
|
tone: budgetTone2(headline.closest?.pct ?? 0),
|
|
4924
5098
|
dot: headline.count > 0
|
|
4925
5099
|
}
|
|
4926
|
-
)), loading && /* @__PURE__ */
|
|
5100
|
+
)), loading && /* @__PURE__ */ React16.createElement(Box14, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 110 }, /* @__PURE__ */ React16.createElement(CircularProgress8, { size: 28 })), !loading && error && /* @__PURE__ */ React16.createElement(Alert8, { severity: "error", sx: { mt: 0.5 } }, error), !loading && !error && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { sx: { display: "flex", alignItems: "flex-start", gap: 1 } }, gauges.map((gauge) => /* @__PURE__ */ React16.createElement(LevelGauge, { key: gauge.kind, gauge, size, keysHref: keysTabHref })), /* @__PURE__ */ React16.createElement(
|
|
5101
|
+
UsageMiniChart,
|
|
5102
|
+
{
|
|
5103
|
+
data: dailyTokens,
|
|
5104
|
+
totalTokens: mtdTokens,
|
|
5105
|
+
loading: usageLoading,
|
|
5106
|
+
height: size
|
|
5107
|
+
}
|
|
5108
|
+
)), /* @__PURE__ */ React16.createElement(Collapse4, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ React16.createElement(
|
|
4927
5109
|
Box14,
|
|
4928
5110
|
{
|
|
4929
5111
|
sx: {
|
|
@@ -4934,7 +5116,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4934
5116
|
overflowY: "auto"
|
|
4935
5117
|
}
|
|
4936
5118
|
},
|
|
4937
|
-
/* @__PURE__ */
|
|
5119
|
+
/* @__PURE__ */ React16.createElement(LimitListPanel, null, /* @__PURE__ */ React16.createElement(
|
|
4938
5120
|
BudgetLimitList,
|
|
4939
5121
|
{
|
|
4940
5122
|
limits: expandedLimits,
|
|
@@ -4942,7 +5124,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4942
5124
|
keysHref: keysTabHref
|
|
4943
5125
|
}
|
|
4944
5126
|
))
|
|
4945
|
-
))), hasFooter && /* @__PURE__ */
|
|
5127
|
+
))), hasFooter && /* @__PURE__ */ React16.createElement(
|
|
4946
5128
|
Box14,
|
|
4947
5129
|
{
|
|
4948
5130
|
sx: (theme) => ({
|
|
@@ -4962,11 +5144,12 @@ var LiteLLMBudgetGauges = ({
|
|
|
4962
5144
|
|
|
4963
5145
|
// src/index.ts
|
|
4964
5146
|
init_KeyFormDialog();
|
|
5147
|
+
init_GenerateKeyButton();
|
|
4965
5148
|
|
|
4966
5149
|
// src/components/GenerateKeyDialog.tsx
|
|
4967
5150
|
init_KeyFormDialog();
|
|
4968
|
-
import
|
|
4969
|
-
var GenerateKeyDialog = (props) => /* @__PURE__ */
|
|
5151
|
+
import React17 from "react";
|
|
5152
|
+
var GenerateKeyDialog = (props) => /* @__PURE__ */ React17.createElement(
|
|
4970
5153
|
KeyFormDialog,
|
|
4971
5154
|
{
|
|
4972
5155
|
open: props.open,
|
|
@@ -4990,6 +5173,7 @@ init_api();
|
|
|
4990
5173
|
init_permissions();
|
|
4991
5174
|
export {
|
|
4992
5175
|
DashboardHeader,
|
|
5176
|
+
GenerateKeyButton,
|
|
4993
5177
|
GenerateKeyDialog,
|
|
4994
5178
|
KeyFormDialog,
|
|
4995
5179
|
KeysTable,
|