@acarmisc/backstage-plugin-litellm 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/GenerateKeyButton.d.ts +18 -0
- package/dist/components/LiteLLMBudgetGauges.d.ts +23 -10
- package/dist/index.cjs.js +695 -516
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +561 -382
- package/dist/index.esm.js.map +4 -4
- package/package.json +1 -1
- package/dist/SearchInput.d.ts +0 -1
- package/dist/components/SearchInput.d.ts +0 -9
package/dist/index.esm.js
CHANGED
|
@@ -836,16 +836,74 @@ 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
|
+
}) => {
|
|
854
|
+
if (onClick) {
|
|
855
|
+
return /* @__PURE__ */ React2.createElement(
|
|
856
|
+
Button,
|
|
857
|
+
{
|
|
858
|
+
variant: "contained",
|
|
859
|
+
color: "primary",
|
|
860
|
+
disableElevation: true,
|
|
861
|
+
size,
|
|
862
|
+
startIcon: /* @__PURE__ */ React2.createElement(Add, null),
|
|
863
|
+
onClick
|
|
864
|
+
},
|
|
865
|
+
label
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
if (to) {
|
|
869
|
+
return /* @__PURE__ */ React2.createElement(
|
|
870
|
+
Button,
|
|
871
|
+
{
|
|
872
|
+
variant: "contained",
|
|
873
|
+
color: "primary",
|
|
874
|
+
disableElevation: true,
|
|
875
|
+
size,
|
|
876
|
+
startIcon: /* @__PURE__ */ React2.createElement(Add, null),
|
|
877
|
+
component: Link,
|
|
878
|
+
to
|
|
879
|
+
},
|
|
880
|
+
label
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
return /* @__PURE__ */ React2.createElement(
|
|
884
|
+
Button,
|
|
885
|
+
{
|
|
886
|
+
variant: "contained",
|
|
887
|
+
color: "primary",
|
|
888
|
+
disableElevation: true,
|
|
889
|
+
size,
|
|
890
|
+
startIcon: /* @__PURE__ */ React2.createElement(Add, null),
|
|
891
|
+
disabled: true
|
|
892
|
+
},
|
|
893
|
+
label
|
|
894
|
+
);
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
|
|
839
899
|
// src/components/DashboardHeader.tsx
|
|
840
|
-
import
|
|
900
|
+
import React3, { useMemo } from "react";
|
|
841
901
|
import Box2 from "@mui/material/Box";
|
|
842
902
|
import Typography2 from "@mui/material/Typography";
|
|
843
903
|
import Skeleton from "@mui/material/Skeleton";
|
|
844
904
|
import Paper2 from "@mui/material/Paper";
|
|
845
|
-
import Button from "@mui/material/Button";
|
|
846
905
|
import Divider from "@mui/material/Divider";
|
|
847
906
|
import { alpha as alpha2 } from "@mui/material/styles";
|
|
848
|
-
import { Add } from "@mui/icons-material";
|
|
849
907
|
function initials(name) {
|
|
850
908
|
const local = name.split("@")[0] ?? name;
|
|
851
909
|
const parts = local.split(/[.\-_\s]+/).filter(Boolean);
|
|
@@ -858,9 +916,10 @@ var init_DashboardHeader = __esm({
|
|
|
858
916
|
"use strict";
|
|
859
917
|
init_api();
|
|
860
918
|
init_ui();
|
|
919
|
+
init_GenerateKeyButton();
|
|
861
920
|
CountStat = ({ value, label, tone }) => {
|
|
862
921
|
const active = value > 0;
|
|
863
|
-
return /* @__PURE__ */
|
|
922
|
+
return /* @__PURE__ */ React3.createElement(Box2, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ React3.createElement(
|
|
864
923
|
Box2,
|
|
865
924
|
{
|
|
866
925
|
sx: (theme) => ({
|
|
@@ -871,14 +930,14 @@ var init_DashboardHeader = __esm({
|
|
|
871
930
|
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
931
|
})
|
|
873
932
|
}
|
|
874
|
-
), /* @__PURE__ */
|
|
933
|
+
), /* @__PURE__ */ React3.createElement(
|
|
875
934
|
Typography2,
|
|
876
935
|
{
|
|
877
936
|
component: "span",
|
|
878
937
|
sx: { fontSize: 15, fontWeight: 700, lineHeight: 1, fontVariantNumeric: "tabular-nums" }
|
|
879
938
|
},
|
|
880
939
|
value
|
|
881
|
-
), /* @__PURE__ */
|
|
940
|
+
), /* @__PURE__ */ React3.createElement(
|
|
882
941
|
Typography2,
|
|
883
942
|
{
|
|
884
943
|
component: "span",
|
|
@@ -902,7 +961,7 @@ var init_DashboardHeader = __esm({
|
|
|
902
961
|
return { total: keys.length, expired, expiringSoon };
|
|
903
962
|
}, [keys]);
|
|
904
963
|
const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;
|
|
905
|
-
return /* @__PURE__ */
|
|
964
|
+
return /* @__PURE__ */ React3.createElement(Paper2, { sx: { borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ React3.createElement(
|
|
906
965
|
Box2,
|
|
907
966
|
{
|
|
908
967
|
sx: {
|
|
@@ -915,7 +974,7 @@ var init_DashboardHeader = __esm({
|
|
|
915
974
|
pb: 2
|
|
916
975
|
}
|
|
917
976
|
},
|
|
918
|
-
/* @__PURE__ */
|
|
977
|
+
/* @__PURE__ */ React3.createElement(
|
|
919
978
|
Box2,
|
|
920
979
|
{
|
|
921
980
|
sx: (theme) => ({
|
|
@@ -935,7 +994,7 @@ var init_DashboardHeader = __esm({
|
|
|
935
994
|
},
|
|
936
995
|
initials(displayName)
|
|
937
996
|
),
|
|
938
|
-
/* @__PURE__ */
|
|
997
|
+
/* @__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
998
|
Typography2,
|
|
940
999
|
{
|
|
941
1000
|
variant: "caption",
|
|
@@ -943,7 +1002,7 @@ var init_DashboardHeader = __esm({
|
|
|
943
1002
|
sx: { display: "block", fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
944
1003
|
},
|
|
945
1004
|
userInfo.user_id
|
|
946
|
-
), /* @__PURE__ */
|
|
1005
|
+
), /* @__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
1006
|
TagChip,
|
|
948
1007
|
{
|
|
949
1008
|
key: team.team_id,
|
|
@@ -951,18 +1010,8 @@ var init_DashboardHeader = __esm({
|
|
|
951
1010
|
title: team.team_id
|
|
952
1011
|
}
|
|
953
1012
|
)))),
|
|
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(
|
|
1013
|
+
/* @__PURE__ */ React3.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick })
|
|
1014
|
+
), /* @__PURE__ */ React3.createElement(Divider, null), /* @__PURE__ */ React3.createElement(
|
|
966
1015
|
Box2,
|
|
967
1016
|
{
|
|
968
1017
|
sx: {
|
|
@@ -974,18 +1023,18 @@ var init_DashboardHeader = __esm({
|
|
|
974
1023
|
py: 1.5
|
|
975
1024
|
}
|
|
976
1025
|
},
|
|
977
|
-
/* @__PURE__ */
|
|
978
|
-
/* @__PURE__ */
|
|
979
|
-
/* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
982
|
-
), tabs && /* @__PURE__ */
|
|
1026
|
+
/* @__PURE__ */ React3.createElement(CountStat, { value: counts.total, label: "keys", tone: "accent" }),
|
|
1027
|
+
/* @__PURE__ */ React3.createElement(Divider, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1028
|
+
/* @__PURE__ */ React3.createElement(CountStat, { value: counts.expired, label: "expired", tone: "danger" }),
|
|
1029
|
+
/* @__PURE__ */ React3.createElement(Divider, { orientation: "vertical", flexItem: true, sx: { my: 0.25 } }),
|
|
1030
|
+
/* @__PURE__ */ React3.createElement(CountStat, { value: counts.expiringSoon, label: "expiring soon", tone: "warning" })
|
|
1031
|
+
), tabs && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(Divider, null), /* @__PURE__ */ React3.createElement(Box2, { sx: { px: 1 } }, tabs)));
|
|
983
1032
|
};
|
|
984
1033
|
}
|
|
985
1034
|
});
|
|
986
1035
|
|
|
987
1036
|
// src/components/KeysTable.tsx
|
|
988
|
-
import
|
|
1037
|
+
import React4, { useState, useMemo as useMemo2 } from "react";
|
|
989
1038
|
import Table from "@mui/material/Table";
|
|
990
1039
|
import TableBody from "@mui/material/TableBody";
|
|
991
1040
|
import TableCell from "@mui/material/TableCell";
|
|
@@ -1006,7 +1055,7 @@ import CircularProgress from "@mui/material/CircularProgress";
|
|
|
1006
1055
|
import Skeleton2 from "@mui/material/Skeleton";
|
|
1007
1056
|
import InputAdornment from "@mui/material/InputAdornment";
|
|
1008
1057
|
import { alpha as alpha3 } from "@mui/material/styles";
|
|
1009
|
-
import { ContentCopy, Delete,
|
|
1058
|
+
import { ContentCopy, Delete, Edit, Autorenew, Search, Lock, LockOpen } from "@mui/icons-material";
|
|
1010
1059
|
function expiryChipLabel(status, expiresAt) {
|
|
1011
1060
|
if (status === "expired") return "Expired";
|
|
1012
1061
|
if (status === "soon") return `${Math.ceil((new Date(expiresAt).getTime() - Date.now()) / 864e5)}d left`;
|
|
@@ -1020,12 +1069,12 @@ function expiryTone(status) {
|
|
|
1020
1069
|
function ExpiryCell({ expiresAt }) {
|
|
1021
1070
|
const status = expiryStatus(expiresAt);
|
|
1022
1071
|
if (!status) {
|
|
1023
|
-
return /* @__PURE__ */
|
|
1072
|
+
return /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary" }, "Never");
|
|
1024
1073
|
}
|
|
1025
1074
|
if (status === "ok") {
|
|
1026
|
-
return /* @__PURE__ */
|
|
1075
|
+
return /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary" }, formatDate(expiresAt));
|
|
1027
1076
|
}
|
|
1028
|
-
return /* @__PURE__ */
|
|
1077
|
+
return /* @__PURE__ */ React4.createElement(
|
|
1029
1078
|
StatusPill,
|
|
1030
1079
|
{
|
|
1031
1080
|
label: expiryChipLabel(status, expiresAt),
|
|
@@ -1040,16 +1089,16 @@ function budgetTone(pct) {
|
|
|
1040
1089
|
return "accent";
|
|
1041
1090
|
}
|
|
1042
1091
|
function keyBlockIcon(isBlocking, blocked) {
|
|
1043
|
-
if (isBlocking) return /* @__PURE__ */
|
|
1044
|
-
if (blocked) return /* @__PURE__ */
|
|
1045
|
-
return /* @__PURE__ */
|
|
1092
|
+
if (isBlocking) return /* @__PURE__ */ React4.createElement(CircularProgress, { size: 18 });
|
|
1093
|
+
if (blocked) return /* @__PURE__ */ React4.createElement(LockOpen, { fontSize: "small" });
|
|
1094
|
+
return /* @__PURE__ */ React4.createElement(Lock, { fontSize: "small" });
|
|
1046
1095
|
}
|
|
1047
1096
|
function BudgetCell({ spend, maxBudget }) {
|
|
1048
1097
|
if (!maxBudget) {
|
|
1049
|
-
return /* @__PURE__ */
|
|
1098
|
+
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
1099
|
}
|
|
1051
1100
|
const pct = Math.min(100, spend / maxBudget * 100);
|
|
1052
|
-
return /* @__PURE__ */
|
|
1101
|
+
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
1102
|
}
|
|
1054
1103
|
function compareKeys(a, b, sortKey) {
|
|
1055
1104
|
switch (sortKey) {
|
|
@@ -1074,6 +1123,7 @@ var init_KeysTable = __esm({
|
|
|
1074
1123
|
"src/components/KeysTable.tsx"() {
|
|
1075
1124
|
"use strict";
|
|
1076
1125
|
init_api();
|
|
1126
|
+
init_GenerateKeyButton();
|
|
1077
1127
|
init_ui();
|
|
1078
1128
|
shortKeyId = (token) => {
|
|
1079
1129
|
if (!token) return "-";
|
|
@@ -1172,32 +1222,27 @@ var init_KeysTable = __esm({
|
|
|
1172
1222
|
};
|
|
1173
1223
|
const renderTableBody = () => {
|
|
1174
1224
|
if (loading) {
|
|
1175
|
-
return /* @__PURE__ */
|
|
1225
|
+
return /* @__PURE__ */ React4.createElement(TableRow, null, /* @__PURE__ */ React4.createElement(TableCell, { colSpan: 8, sx: { py: 2 } }, /* @__PURE__ */ React4.createElement(Skeleton2, { variant: "rounded", height: 140 })));
|
|
1176
1226
|
}
|
|
1177
1227
|
if (filteredKeys.length === 0) {
|
|
1178
|
-
return /* @__PURE__ */
|
|
1228
|
+
return /* @__PURE__ */ React4.createElement(TableRow, null, /* @__PURE__ */ React4.createElement(TableCell, { colSpan: 8 }, filterText ? /* @__PURE__ */ React4.createElement(
|
|
1179
1229
|
EmptyState,
|
|
1180
1230
|
{
|
|
1181
1231
|
message: "No keys match that filter",
|
|
1182
1232
|
hint: "Try a different alias or model name."
|
|
1183
1233
|
}
|
|
1184
|
-
) : /* @__PURE__ */
|
|
1234
|
+
) : /* @__PURE__ */ React4.createElement(
|
|
1185
1235
|
EmptyState,
|
|
1186
1236
|
{
|
|
1187
1237
|
message: "No keys yet",
|
|
1188
1238
|
hint: "Generate your first key to start calling models.",
|
|
1189
|
-
action: /* @__PURE__ */
|
|
1190
|
-
|
|
1239
|
+
action: /* @__PURE__ */ React4.createElement(
|
|
1240
|
+
GenerateKeyButton,
|
|
1191
1241
|
{
|
|
1192
|
-
variant: "contained",
|
|
1193
|
-
color: "primary",
|
|
1194
1242
|
size: "small",
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
sx: { mt: 1 }
|
|
1199
|
-
},
|
|
1200
|
-
"Generate Your First Key"
|
|
1243
|
+
label: "Generate Your First Key",
|
|
1244
|
+
onClick: onGenerateKeyClick
|
|
1245
|
+
}
|
|
1201
1246
|
)
|
|
1202
1247
|
}
|
|
1203
1248
|
)));
|
|
@@ -1206,7 +1251,7 @@ var init_KeysTable = __esm({
|
|
|
1206
1251
|
const keyId = key.token ?? key.key;
|
|
1207
1252
|
const isBlocking = blockingKeyId === keyId;
|
|
1208
1253
|
const keyModels = key.models ?? [];
|
|
1209
|
-
return /* @__PURE__ */
|
|
1254
|
+
return /* @__PURE__ */ React4.createElement(
|
|
1210
1255
|
TableRow,
|
|
1211
1256
|
{
|
|
1212
1257
|
key: keyId,
|
|
@@ -1217,8 +1262,8 @@ var init_KeysTable = __esm({
|
|
|
1217
1262
|
"& td": { color: theme.palette.text.secondary }
|
|
1218
1263
|
}) : void 0
|
|
1219
1264
|
},
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1265
|
+
/* @__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" }))),
|
|
1266
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(Box3, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React4.createElement(
|
|
1222
1267
|
Typography3,
|
|
1223
1268
|
{
|
|
1224
1269
|
variant: "body2",
|
|
@@ -1235,7 +1280,7 @@ var init_KeysTable = __esm({
|
|
|
1235
1280
|
})
|
|
1236
1281
|
},
|
|
1237
1282
|
shortKeyId(keyId)
|
|
1238
|
-
), /* @__PURE__ */
|
|
1283
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1239
1284
|
IconButton,
|
|
1240
1285
|
{
|
|
1241
1286
|
size: "small",
|
|
@@ -1243,13 +1288,13 @@ var init_KeysTable = __esm({
|
|
|
1243
1288
|
title: "Copy Key ID",
|
|
1244
1289
|
sx: quietIconButtonSx("accent")
|
|
1245
1290
|
},
|
|
1246
|
-
/* @__PURE__ */
|
|
1291
|
+
/* @__PURE__ */ React4.createElement(ContentCopy, { sx: { fontSize: 15 } })
|
|
1247
1292
|
))),
|
|
1248
|
-
/* @__PURE__ */
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1293
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2", color: "text.secondary" }, formatDate(key.created_at))),
|
|
1294
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(ExpiryCell, { expiresAt: key.expires_at })),
|
|
1295
|
+
/* @__PURE__ */ React4.createElement(TableCell, null, /* @__PURE__ */ React4.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })),
|
|
1296
|
+
/* @__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")),
|
|
1297
|
+
/* @__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
1298
|
TagChip,
|
|
1254
1299
|
{
|
|
1255
1300
|
label: `+${keyModels.length - 2}`,
|
|
@@ -1257,7 +1302,7 @@ var init_KeysTable = __esm({
|
|
|
1257
1302
|
title: keyModels.slice(2).join(", ")
|
|
1258
1303
|
}
|
|
1259
1304
|
))),
|
|
1260
|
-
/* @__PURE__ */
|
|
1305
|
+
/* @__PURE__ */ React4.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React4.createElement(Box3, { display: "flex", justifyContent: "flex-end", gap: 0.25 }, /* @__PURE__ */ React4.createElement(
|
|
1261
1306
|
IconButton,
|
|
1262
1307
|
{
|
|
1263
1308
|
size: "small",
|
|
@@ -1265,8 +1310,8 @@ var init_KeysTable = __esm({
|
|
|
1265
1310
|
title: "Edit key",
|
|
1266
1311
|
sx: quietIconButtonSx("accent")
|
|
1267
1312
|
},
|
|
1268
|
-
/* @__PURE__ */
|
|
1269
|
-
), /* @__PURE__ */
|
|
1313
|
+
/* @__PURE__ */ React4.createElement(Edit, { fontSize: "small" })
|
|
1314
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1270
1315
|
IconButton,
|
|
1271
1316
|
{
|
|
1272
1317
|
size: "small",
|
|
@@ -1276,7 +1321,7 @@ var init_KeysTable = __esm({
|
|
|
1276
1321
|
sx: quietIconButtonSx("warning")
|
|
1277
1322
|
},
|
|
1278
1323
|
keyBlockIcon(isBlocking, key.blocked)
|
|
1279
|
-
), /* @__PURE__ */
|
|
1324
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1280
1325
|
IconButton,
|
|
1281
1326
|
{
|
|
1282
1327
|
size: "small",
|
|
@@ -1284,18 +1329,18 @@ var init_KeysTable = __esm({
|
|
|
1284
1329
|
title: "Revoke key",
|
|
1285
1330
|
sx: quietIconButtonSx("danger")
|
|
1286
1331
|
},
|
|
1287
|
-
/* @__PURE__ */
|
|
1332
|
+
/* @__PURE__ */ React4.createElement(Delete, { fontSize: "small" })
|
|
1288
1333
|
)))
|
|
1289
1334
|
);
|
|
1290
1335
|
});
|
|
1291
1336
|
};
|
|
1292
|
-
return /* @__PURE__ */
|
|
1337
|
+
return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
|
|
1293
1338
|
SectionCard,
|
|
1294
1339
|
{
|
|
1295
1340
|
title: "Virtual Keys",
|
|
1296
1341
|
subtitle: loading ? "Loading\u2026" : `${filteredKeys.length}${filterText ? ` of ${keys.length}` : ""} key${filteredKeys.length === 1 ? "" : "s"}`,
|
|
1297
1342
|
flush: true,
|
|
1298
|
-
actions: /* @__PURE__ */
|
|
1343
|
+
actions: /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
|
|
1299
1344
|
TextField,
|
|
1300
1345
|
{
|
|
1301
1346
|
size: "small",
|
|
@@ -1305,15 +1350,15 @@ var init_KeysTable = __esm({
|
|
|
1305
1350
|
onChange: (e) => setFilterText(e.target.value),
|
|
1306
1351
|
sx: { minWidth: 240 },
|
|
1307
1352
|
InputProps: {
|
|
1308
|
-
startAdornment: /* @__PURE__ */
|
|
1353
|
+
startAdornment: /* @__PURE__ */ React4.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React4.createElement(Search, { fontSize: "small", color: "disabled" }))
|
|
1309
1354
|
}
|
|
1310
1355
|
}
|
|
1311
|
-
), expiredKeys.length > 0 && /* @__PURE__ */
|
|
1356
|
+
), expiredKeys.length > 0 && /* @__PURE__ */ React4.createElement(
|
|
1312
1357
|
Button2,
|
|
1313
1358
|
{
|
|
1314
1359
|
variant: "outlined",
|
|
1315
1360
|
color: "inherit",
|
|
1316
|
-
startIcon: /* @__PURE__ */
|
|
1361
|
+
startIcon: /* @__PURE__ */ React4.createElement(Autorenew, null),
|
|
1317
1362
|
onClick: () => setPruneConfirmCount(expiredKeys.length),
|
|
1318
1363
|
sx: (theme) => ({
|
|
1319
1364
|
color: theme.palette.text.secondary,
|
|
@@ -1328,20 +1373,10 @@ var init_KeysTable = __esm({
|
|
|
1328
1373
|
"Prune expired (",
|
|
1329
1374
|
expiredKeys.length,
|
|
1330
1375
|
")"
|
|
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
|
-
))
|
|
1376
|
+
), /* @__PURE__ */ React4.createElement(GenerateKeyButton, { onClick: onGenerateKeyClick }))
|
|
1342
1377
|
},
|
|
1343
|
-
/* @__PURE__ */
|
|
1344
|
-
), /* @__PURE__ */
|
|
1378
|
+
/* @__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())))
|
|
1379
|
+
), /* @__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
1380
|
Button2,
|
|
1346
1381
|
{
|
|
1347
1382
|
onClick: handleConfirmDelete,
|
|
@@ -1349,8 +1384,8 @@ var init_KeysTable = __esm({
|
|
|
1349
1384
|
color: "error",
|
|
1350
1385
|
disabled: deleteSubmitting
|
|
1351
1386
|
},
|
|
1352
|
-
deleteSubmitting ? /* @__PURE__ */
|
|
1353
|
-
))), /* @__PURE__ */
|
|
1387
|
+
deleteSubmitting ? /* @__PURE__ */ React4.createElement(CircularProgress, { size: 20 }) : "Revoke"
|
|
1388
|
+
))), /* @__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
1389
|
Button2,
|
|
1355
1390
|
{
|
|
1356
1391
|
onClick: handlePruneExpired,
|
|
@@ -1358,7 +1393,7 @@ var init_KeysTable = __esm({
|
|
|
1358
1393
|
color: "error",
|
|
1359
1394
|
disabled: pruneSubmitting
|
|
1360
1395
|
},
|
|
1361
|
-
pruneSubmitting ? /* @__PURE__ */
|
|
1396
|
+
pruneSubmitting ? /* @__PURE__ */ React4.createElement(CircularProgress, { size: 20 }) : "Prune"
|
|
1362
1397
|
))));
|
|
1363
1398
|
};
|
|
1364
1399
|
}
|
|
@@ -1379,7 +1414,7 @@ var init_format = __esm({
|
|
|
1379
1414
|
});
|
|
1380
1415
|
|
|
1381
1416
|
// src/components/KeyFormDialog.tsx
|
|
1382
|
-
import
|
|
1417
|
+
import React5, { useState as useState2, useEffect, useMemo as useMemo3 } from "react";
|
|
1383
1418
|
import Box4 from "@mui/material/Box";
|
|
1384
1419
|
import Typography4 from "@mui/material/Typography";
|
|
1385
1420
|
import Dialog2 from "@mui/material/Dialog";
|
|
@@ -1556,14 +1591,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1556
1591
|
const snippetKey = tab === "claude-code" ? "claudeCode" : tab;
|
|
1557
1592
|
const code = snippets[snippetKey];
|
|
1558
1593
|
const fileHint = SNIPPET_FILE_HINTS[tab];
|
|
1559
|
-
return /* @__PURE__ */
|
|
1594
|
+
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
1595
|
Box4,
|
|
1561
1596
|
{
|
|
1562
1597
|
position: "relative",
|
|
1563
1598
|
p: 1.5,
|
|
1564
1599
|
sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
|
|
1565
1600
|
},
|
|
1566
|
-
/* @__PURE__ */
|
|
1601
|
+
/* @__PURE__ */ React5.createElement(
|
|
1567
1602
|
IconButton2,
|
|
1568
1603
|
{
|
|
1569
1604
|
size: "small",
|
|
@@ -1571,9 +1606,9 @@ var init_KeyFormDialog = __esm({
|
|
|
1571
1606
|
title: "Copy snippet",
|
|
1572
1607
|
sx: { position: "absolute", top: 4, right: 4 }
|
|
1573
1608
|
},
|
|
1574
|
-
/* @__PURE__ */
|
|
1609
|
+
/* @__PURE__ */ React5.createElement(ContentCopy2, { fontSize: "small" })
|
|
1575
1610
|
),
|
|
1576
|
-
/* @__PURE__ */
|
|
1611
|
+
/* @__PURE__ */ React5.createElement(
|
|
1577
1612
|
Typography4,
|
|
1578
1613
|
{
|
|
1579
1614
|
component: "pre",
|
|
@@ -1588,7 +1623,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1588
1623
|
},
|
|
1589
1624
|
code
|
|
1590
1625
|
),
|
|
1591
|
-
model && /* @__PURE__ */
|
|
1626
|
+
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
1627
|
));
|
|
1593
1628
|
};
|
|
1594
1629
|
KeyFormDialog = ({
|
|
@@ -1743,11 +1778,11 @@ var init_KeyFormDialog = __esm({
|
|
|
1743
1778
|
const inCost = fmtCost(m.input_cost_per_token);
|
|
1744
1779
|
const outCost = fmtCost(m.output_cost_per_token);
|
|
1745
1780
|
const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
|
|
1746
|
-
return /* @__PURE__ */
|
|
1781
|
+
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
1782
|
};
|
|
1748
1783
|
const renderTeamField = () => {
|
|
1749
1784
|
if (teams.length > 0) {
|
|
1750
|
-
return /* @__PURE__ */
|
|
1785
|
+
return /* @__PURE__ */ React5.createElement(
|
|
1751
1786
|
Autocomplete,
|
|
1752
1787
|
{
|
|
1753
1788
|
options: teams,
|
|
@@ -1761,7 +1796,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1761
1796
|
setFormData({ team_id: team?.team_id, models: restrictedModels });
|
|
1762
1797
|
},
|
|
1763
1798
|
disabled: !isCreate,
|
|
1764
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1799
|
+
renderInput: (params) => /* @__PURE__ */ React5.createElement(
|
|
1765
1800
|
TextField2,
|
|
1766
1801
|
{
|
|
1767
1802
|
...params,
|
|
@@ -1776,14 +1811,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1776
1811
|
);
|
|
1777
1812
|
}
|
|
1778
1813
|
if (teamRequired && isCreate) {
|
|
1779
|
-
return /* @__PURE__ */
|
|
1814
|
+
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
1815
|
}
|
|
1781
1816
|
return null;
|
|
1782
1817
|
};
|
|
1783
1818
|
const editKeyId = keyToEdit?.token ?? keyToEdit?.key ?? "";
|
|
1784
1819
|
const renderDangerZone = () => {
|
|
1785
1820
|
if (isCreate || !onResetKeySpend) return null;
|
|
1786
|
-
return /* @__PURE__ */
|
|
1821
|
+
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
1822
|
Button3,
|
|
1788
1823
|
{
|
|
1789
1824
|
size: "small",
|
|
@@ -1792,7 +1827,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1792
1827
|
onClick: () => setResetSpendConfirm(true)
|
|
1793
1828
|
},
|
|
1794
1829
|
"Reset Spend to $0"
|
|
1795
|
-
) : /* @__PURE__ */
|
|
1830
|
+
) : /* @__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
1831
|
Button3,
|
|
1797
1832
|
{
|
|
1798
1833
|
size: "small",
|
|
@@ -1801,8 +1836,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1801
1836
|
disabled: resetSpendSubmitting,
|
|
1802
1837
|
onClick: handleResetSpend
|
|
1803
1838
|
},
|
|
1804
|
-
resetSpendSubmitting ? /* @__PURE__ */
|
|
1805
|
-
), /* @__PURE__ */
|
|
1839
|
+
resetSpendSubmitting ? /* @__PURE__ */ React5.createElement(CircularProgress2, { size: 16 }) : "Confirm"
|
|
1840
|
+
), /* @__PURE__ */ React5.createElement(Button3, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel")));
|
|
1806
1841
|
};
|
|
1807
1842
|
const maskKey = (key) => {
|
|
1808
1843
|
if (key.length <= 8) return "***";
|
|
@@ -1812,7 +1847,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1812
1847
|
if (newKeyValue) dialogTitle = "Key Generated";
|
|
1813
1848
|
else if (isCreate) dialogTitle = "Generate New Key";
|
|
1814
1849
|
const submitLabel = isCreate ? "Generate" : "Save";
|
|
1815
|
-
return /* @__PURE__ */
|
|
1850
|
+
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
1851
|
Box4,
|
|
1817
1852
|
{
|
|
1818
1853
|
display: "flex",
|
|
@@ -1827,7 +1862,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1827
1862
|
borderRadius: 1
|
|
1828
1863
|
}
|
|
1829
1864
|
},
|
|
1830
|
-
/* @__PURE__ */
|
|
1865
|
+
/* @__PURE__ */ React5.createElement(
|
|
1831
1866
|
Typography4,
|
|
1832
1867
|
{
|
|
1833
1868
|
component: "code",
|
|
@@ -1836,8 +1871,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1836
1871
|
},
|
|
1837
1872
|
newKeyValue
|
|
1838
1873
|
),
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
), newKeySnippets && /* @__PURE__ */
|
|
1874
|
+
/* @__PURE__ */ React5.createElement(IconButton2, { onClick: () => copyToClipboard(newKeyValue) }, /* @__PURE__ */ React5.createElement(ContentCopy2, null))
|
|
1875
|
+
), 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
1876
|
Box4,
|
|
1842
1877
|
{
|
|
1843
1878
|
display: "flex",
|
|
@@ -1851,7 +1886,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1851
1886
|
borderRadius: 1
|
|
1852
1887
|
}
|
|
1853
1888
|
},
|
|
1854
|
-
/* @__PURE__ */
|
|
1889
|
+
/* @__PURE__ */ React5.createElement(
|
|
1855
1890
|
Typography4,
|
|
1856
1891
|
{
|
|
1857
1892
|
component: "code",
|
|
@@ -1860,8 +1895,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1860
1895
|
},
|
|
1861
1896
|
newKeySnippets.publicEndpoint
|
|
1862
1897
|
),
|
|
1863
|
-
/* @__PURE__ */
|
|
1864
|
-
)), newKeySnippets && /* @__PURE__ */
|
|
1898
|
+
/* @__PURE__ */ React5.createElement(IconButton2, { size: "small", onClick: () => copyToClipboard(newKeySnippets.publicEndpoint) }, /* @__PURE__ */ React5.createElement(ContentCopy2, { fontSize: "small" }))
|
|
1899
|
+
)), 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
1900
|
TextField2,
|
|
1866
1901
|
{
|
|
1867
1902
|
label: "Alias",
|
|
@@ -1877,7 +1912,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1877
1912
|
required: true,
|
|
1878
1913
|
fullWidth: true
|
|
1879
1914
|
}
|
|
1880
|
-
), !isCreate ? null : /* @__PURE__ */
|
|
1915
|
+
), !isCreate ? null : /* @__PURE__ */ React5.createElement(
|
|
1881
1916
|
TextField2,
|
|
1882
1917
|
{
|
|
1883
1918
|
select: true,
|
|
@@ -1886,12 +1921,12 @@ var init_KeyFormDialog = __esm({
|
|
|
1886
1921
|
onChange: (e) => setFormData({ duration: e.target.value }),
|
|
1887
1922
|
fullWidth: true
|
|
1888
1923
|
},
|
|
1889
|
-
/* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
/* @__PURE__ */
|
|
1892
|
-
/* @__PURE__ */
|
|
1893
|
-
/* @__PURE__ */
|
|
1894
|
-
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */
|
|
1924
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "1d" }, "1 Day"),
|
|
1925
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "7d" }, "7 Days"),
|
|
1926
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "30d" }, "30 Days"),
|
|
1927
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "90d" }, "90 Days"),
|
|
1928
|
+
/* @__PURE__ */ React5.createElement(MenuItem, { value: "1y" }, "1 Year")
|
|
1929
|
+
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ React5.createElement(
|
|
1895
1930
|
Autocomplete,
|
|
1896
1931
|
{
|
|
1897
1932
|
multiple: true,
|
|
@@ -1900,8 +1935,8 @@ var init_KeyFormDialog = __esm({
|
|
|
1900
1935
|
getOptionLabel: (m) => m.model_name,
|
|
1901
1936
|
value: selectedModels,
|
|
1902
1937
|
onChange: (_e, selected) => setFormData({ models: selected.map((m) => m.model_name) }),
|
|
1903
|
-
renderOption: (props, m) => /* @__PURE__ */
|
|
1904
|
-
renderInput: (params) => /* @__PURE__ */
|
|
1938
|
+
renderOption: (props, m) => /* @__PURE__ */ React5.createElement("li", { ...props }, modelOption(m)),
|
|
1939
|
+
renderInput: (params) => /* @__PURE__ */ React5.createElement(
|
|
1905
1940
|
TextField2,
|
|
1906
1941
|
{
|
|
1907
1942
|
...params,
|
|
@@ -1911,10 +1946,10 @@ var init_KeyFormDialog = __esm({
|
|
|
1911
1946
|
}
|
|
1912
1947
|
)
|
|
1913
1948
|
}
|
|
1914
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
1949
|
+
), allowUnlimitedBudget && /* @__PURE__ */ React5.createElement(
|
|
1915
1950
|
FormControlLabel,
|
|
1916
1951
|
{
|
|
1917
|
-
control: /* @__PURE__ */
|
|
1952
|
+
control: /* @__PURE__ */ React5.createElement(
|
|
1918
1953
|
Checkbox,
|
|
1919
1954
|
{
|
|
1920
1955
|
checked: unlimitedBudget,
|
|
@@ -1930,7 +1965,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1930
1965
|
),
|
|
1931
1966
|
label: "Unlimited budget"
|
|
1932
1967
|
}
|
|
1933
|
-
), /* @__PURE__ */
|
|
1968
|
+
), /* @__PURE__ */ React5.createElement(
|
|
1934
1969
|
TextField2,
|
|
1935
1970
|
{
|
|
1936
1971
|
label: "Max Budget (USD)",
|
|
@@ -1943,7 +1978,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1943
1978
|
required: true,
|
|
1944
1979
|
fullWidth: true
|
|
1945
1980
|
}
|
|
1946
|
-
), /* @__PURE__ */
|
|
1981
|
+
), /* @__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
1982
|
TextField2,
|
|
1948
1983
|
{
|
|
1949
1984
|
label: "TPM Limit",
|
|
@@ -1953,7 +1988,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1953
1988
|
helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
|
|
1954
1989
|
fullWidth: true
|
|
1955
1990
|
}
|
|
1956
|
-
), /* @__PURE__ */
|
|
1991
|
+
), /* @__PURE__ */ React5.createElement(
|
|
1957
1992
|
TextField2,
|
|
1958
1993
|
{
|
|
1959
1994
|
label: "RPM Limit",
|
|
@@ -1963,7 +1998,7 @@ var init_KeyFormDialog = __esm({
|
|
|
1963
1998
|
helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
|
|
1964
1999
|
fullWidth: true
|
|
1965
2000
|
}
|
|
1966
|
-
))), renderDangerZone())), /* @__PURE__ */
|
|
2001
|
+
))), 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
2002
|
Button3,
|
|
1968
2003
|
{
|
|
1969
2004
|
onClick: isCreate ? handleGenerate : handleUpdate,
|
|
@@ -1971,14 +2006,14 @@ var init_KeyFormDialog = __esm({
|
|
|
1971
2006
|
color: "primary",
|
|
1972
2007
|
disabled: !canSubmit
|
|
1973
2008
|
},
|
|
1974
|
-
submitting ? /* @__PURE__ */
|
|
2009
|
+
submitting ? /* @__PURE__ */ React5.createElement(CircularProgress2, { size: 24 }) : submitLabel
|
|
1975
2010
|
))));
|
|
1976
2011
|
};
|
|
1977
2012
|
}
|
|
1978
2013
|
});
|
|
1979
2014
|
|
|
1980
2015
|
// src/components/ManageTeamDialog.tsx
|
|
1981
|
-
import
|
|
2016
|
+
import React6, { useState as useState3, useEffect as useEffect2, useMemo as useMemo4 } from "react";
|
|
1982
2017
|
import Dialog3 from "@mui/material/Dialog";
|
|
1983
2018
|
import DialogTitle3 from "@mui/material/DialogTitle";
|
|
1984
2019
|
import DialogContent3 from "@mui/material/DialogContent";
|
|
@@ -2247,7 +2282,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2247
2282
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
2248
2283
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
2249
2284
|
const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
|
|
2250
|
-
return /* @__PURE__ */
|
|
2285
|
+
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
2286
|
TextField3,
|
|
2252
2287
|
{
|
|
2253
2288
|
label: "Team Alias",
|
|
@@ -2256,7 +2291,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2256
2291
|
disabled: submitting,
|
|
2257
2292
|
fullWidth: true
|
|
2258
2293
|
}
|
|
2259
|
-
), /* @__PURE__ */
|
|
2294
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2260
2295
|
Autocomplete2,
|
|
2261
2296
|
{
|
|
2262
2297
|
multiple: true,
|
|
@@ -2264,7 +2299,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2264
2299
|
value: models,
|
|
2265
2300
|
onChange: (_, newValue) => setModels(newValue),
|
|
2266
2301
|
disabled: submitting,
|
|
2267
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2302
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(
|
|
2268
2303
|
TextField3,
|
|
2269
2304
|
{
|
|
2270
2305
|
...params,
|
|
@@ -2274,7 +2309,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2274
2309
|
}
|
|
2275
2310
|
)
|
|
2276
2311
|
}
|
|
2277
|
-
), /* @__PURE__ */
|
|
2312
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2278
2313
|
TextField3,
|
|
2279
2314
|
{
|
|
2280
2315
|
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
@@ -2286,13 +2321,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
2286
2321
|
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
2287
2322
|
fullWidth: true
|
|
2288
2323
|
}
|
|
2289
|
-
), allowUnlimitedBudget && /* @__PURE__ */
|
|
2324
|
+
), allowUnlimitedBudget && /* @__PURE__ */ React6.createElement(
|
|
2290
2325
|
FormControlLabel2,
|
|
2291
2326
|
{
|
|
2292
|
-
control: /* @__PURE__ */
|
|
2327
|
+
control: /* @__PURE__ */ React6.createElement(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
2293
2328
|
label: "Unlimited Budget"
|
|
2294
2329
|
}
|
|
2295
|
-
), /* @__PURE__ */
|
|
2330
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2296
2331
|
TextField3,
|
|
2297
2332
|
{
|
|
2298
2333
|
select: true,
|
|
@@ -2303,8 +2338,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2303
2338
|
helperText: "Spend-reset period for the team budget",
|
|
2304
2339
|
fullWidth: true
|
|
2305
2340
|
},
|
|
2306
|
-
durationOptions.map((o) => /* @__PURE__ */
|
|
2307
|
-
), showMembers && /* @__PURE__ */
|
|
2341
|
+
durationOptions.map((o) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: o.value, value: o.value }, o.label))
|
|
2342
|
+
), 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
2343
|
Typography5,
|
|
2309
2344
|
{
|
|
2310
2345
|
variant: "caption",
|
|
@@ -2312,14 +2347,14 @@ var init_ManageTeamDialog = __esm({
|
|
|
2312
2347
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
2313
2348
|
},
|
|
2314
2349
|
m.user_id
|
|
2315
|
-
)) : /* @__PURE__ */
|
|
2350
|
+
)) : /* @__PURE__ */ React6.createElement(
|
|
2316
2351
|
Typography5,
|
|
2317
2352
|
{
|
|
2318
2353
|
variant: "body2",
|
|
2319
2354
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
2320
2355
|
},
|
|
2321
2356
|
m.user_id
|
|
2322
|
-
)), /* @__PURE__ */
|
|
2357
|
+
)), /* @__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
2358
|
IconButton3,
|
|
2324
2359
|
{
|
|
2325
2360
|
edge: "end",
|
|
@@ -2328,8 +2363,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2328
2363
|
disabled: memberBusy,
|
|
2329
2364
|
onClick: () => handleRemoveMember(m.user_id)
|
|
2330
2365
|
},
|
|
2331
|
-
/* @__PURE__ */
|
|
2332
|
-
))))))), /* @__PURE__ */
|
|
2366
|
+
/* @__PURE__ */ React6.createElement(DeleteIcon, { fontSize: "small" })
|
|
2367
|
+
))))))), /* @__PURE__ */ React6.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ React6.createElement(
|
|
2333
2368
|
Autocomplete2,
|
|
2334
2369
|
{
|
|
2335
2370
|
freeSolo: true,
|
|
@@ -2353,7 +2388,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2353
2388
|
isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
|
|
2354
2389
|
disabled: memberBusy,
|
|
2355
2390
|
sx: { flex: 1, minWidth: 260 },
|
|
2356
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2391
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(
|
|
2357
2392
|
TextField3,
|
|
2358
2393
|
{
|
|
2359
2394
|
...params,
|
|
@@ -2363,12 +2398,12 @@ var init_ManageTeamDialog = __esm({
|
|
|
2363
2398
|
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
2364
2399
|
InputProps: {
|
|
2365
2400
|
...params.InputProps,
|
|
2366
|
-
endAdornment: /* @__PURE__ */
|
|
2401
|
+
endAdornment: /* @__PURE__ */ React6.createElement(React6.Fragment, null, usersLoading ? /* @__PURE__ */ React6.createElement(CircularProgress3, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
|
|
2367
2402
|
}
|
|
2368
2403
|
}
|
|
2369
2404
|
)
|
|
2370
2405
|
}
|
|
2371
|
-
), /* @__PURE__ */
|
|
2406
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2372
2407
|
TextField3,
|
|
2373
2408
|
{
|
|
2374
2409
|
label: "Max budget in team ($)",
|
|
@@ -2379,7 +2414,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2379
2414
|
size: "small",
|
|
2380
2415
|
sx: { width: 160 }
|
|
2381
2416
|
}
|
|
2382
|
-
), /* @__PURE__ */
|
|
2417
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2383
2418
|
Button4,
|
|
2384
2419
|
{
|
|
2385
2420
|
onClick: handleAddMember,
|
|
@@ -2388,7 +2423,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2388
2423
|
sx: { mt: 0.5 }
|
|
2389
2424
|
},
|
|
2390
2425
|
"Add"
|
|
2391
|
-
))), showKnowledgeBases && /* @__PURE__ */
|
|
2426
|
+
))), 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
2427
|
Autocomplete2,
|
|
2393
2428
|
{
|
|
2394
2429
|
multiple: true,
|
|
@@ -2397,9 +2432,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2397
2432
|
getOptionLabel: kbLabel,
|
|
2398
2433
|
onChange: (_, v) => setKbIds(v),
|
|
2399
2434
|
disabled: kbBusy,
|
|
2400
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2435
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(TextField3, { ...params, label: "Attached knowledge bases" })
|
|
2401
2436
|
}
|
|
2402
|
-
), /* @__PURE__ */
|
|
2437
|
+
), /* @__PURE__ */ React6.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React6.createElement(
|
|
2403
2438
|
Button4,
|
|
2404
2439
|
{
|
|
2405
2440
|
onClick: handleSaveKnowledgeBases,
|
|
@@ -2407,7 +2442,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2407
2442
|
variant: "outlined"
|
|
2408
2443
|
},
|
|
2409
2444
|
kbBusy ? "Saving\u2026" : "Save knowledge bases"
|
|
2410
|
-
))), showMcpServers && /* @__PURE__ */
|
|
2445
|
+
))), 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
2446
|
Autocomplete2,
|
|
2412
2447
|
{
|
|
2413
2448
|
multiple: true,
|
|
@@ -2416,9 +2451,9 @@ var init_ManageTeamDialog = __esm({
|
|
|
2416
2451
|
getOptionLabel: mcpLabel,
|
|
2417
2452
|
onChange: (_, v) => setMcpIds(v),
|
|
2418
2453
|
disabled: mcpBusy,
|
|
2419
|
-
renderInput: (params) => /* @__PURE__ */
|
|
2454
|
+
renderInput: (params) => /* @__PURE__ */ React6.createElement(TextField3, { ...params, label: "Attached MCP servers" })
|
|
2420
2455
|
}
|
|
2421
|
-
), /* @__PURE__ */
|
|
2456
|
+
), /* @__PURE__ */ React6.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React6.createElement(
|
|
2422
2457
|
Button4,
|
|
2423
2458
|
{
|
|
2424
2459
|
onClick: handleSaveMcpServers,
|
|
@@ -2426,13 +2461,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
2426
2461
|
variant: "outlined"
|
|
2427
2462
|
},
|
|
2428
2463
|
mcpBusy ? "Saving\u2026" : "Save MCP servers"
|
|
2429
|
-
)))), /* @__PURE__ */
|
|
2464
|
+
)))), /* @__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
2465
|
};
|
|
2431
2466
|
}
|
|
2432
2467
|
});
|
|
2433
2468
|
|
|
2434
2469
|
// src/components/UsageStats.tsx
|
|
2435
|
-
import
|
|
2470
|
+
import React7, { useMemo as useMemo5, useState as useState4 } from "react";
|
|
2436
2471
|
import Paper4 from "@mui/material/Paper";
|
|
2437
2472
|
import Box6 from "@mui/material/Box";
|
|
2438
2473
|
import Typography6 from "@mui/material/Typography";
|
|
@@ -2482,16 +2517,16 @@ var init_UsageStats = __esm({
|
|
|
2482
2517
|
"30d": "Last 30 days"
|
|
2483
2518
|
};
|
|
2484
2519
|
fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
|
|
2485
|
-
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */
|
|
2520
|
+
ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ React7.createElement(Skeleton3, { variant: "rounded", height });
|
|
2486
2521
|
ChartOrFallback = ({ loading, empty, height = 240, children }) => {
|
|
2487
|
-
if (loading) return /* @__PURE__ */
|
|
2488
|
-
if (empty) return /* @__PURE__ */
|
|
2489
|
-
return /* @__PURE__ */
|
|
2522
|
+
if (loading) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { height });
|
|
2523
|
+
if (empty) return /* @__PURE__ */ React7.createElement(EmptyState, { message: "No data for this period", height });
|
|
2524
|
+
return /* @__PURE__ */ React7.createElement(React7.Fragment, null, children);
|
|
2490
2525
|
};
|
|
2491
2526
|
SuccessRateCell = ({ rate, requests }) => {
|
|
2492
|
-
if (requests === 0) return /* @__PURE__ */
|
|
2527
|
+
if (requests === 0) return /* @__PURE__ */ React7.createElement(Typography6, { variant: "body2", color: "text.secondary" }, "\u2014");
|
|
2493
2528
|
const tone = rateTone(rate);
|
|
2494
|
-
return /* @__PURE__ */
|
|
2529
|
+
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
2530
|
Typography6,
|
|
2496
2531
|
{
|
|
2497
2532
|
variant: "caption",
|
|
@@ -2640,23 +2675,23 @@ var init_UsageStats = __esm({
|
|
|
2640
2675
|
];
|
|
2641
2676
|
const renderKeyRows = () => {
|
|
2642
2677
|
if (loading) {
|
|
2643
|
-
return /* @__PURE__ */
|
|
2678
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React7.createElement(Skeleton3, { variant: "rounded", height: 80 })));
|
|
2644
2679
|
}
|
|
2645
2680
|
if (keyRows.length === 0) {
|
|
2646
|
-
return /* @__PURE__ */
|
|
2681
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 7 }, /* @__PURE__ */ React7.createElement(EmptyState, { message: "No key activity in this period" })));
|
|
2647
2682
|
}
|
|
2648
|
-
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */
|
|
2683
|
+
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
2684
|
};
|
|
2650
2685
|
const renderModelRows = () => {
|
|
2651
2686
|
if (loading) {
|
|
2652
|
-
return /* @__PURE__ */
|
|
2687
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React7.createElement(Skeleton3, { variant: "rounded", height: 80 })));
|
|
2653
2688
|
}
|
|
2654
2689
|
if (modelRows.length === 0) {
|
|
2655
|
-
return /* @__PURE__ */
|
|
2690
|
+
return /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, { colSpan: 8 }, /* @__PURE__ */ React7.createElement(EmptyState, { message: "No model activity in this period" })));
|
|
2656
2691
|
}
|
|
2657
|
-
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */
|
|
2692
|
+
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
2693
|
};
|
|
2659
|
-
const periodSelect = /* @__PURE__ */
|
|
2694
|
+
const periodSelect = /* @__PURE__ */ React7.createElement(
|
|
2660
2695
|
TextField4,
|
|
2661
2696
|
{
|
|
2662
2697
|
select: true,
|
|
@@ -2666,17 +2701,17 @@ var init_UsageStats = __esm({
|
|
|
2666
2701
|
onChange: (e) => handlePresetChange(e.target.value),
|
|
2667
2702
|
sx: { minWidth: 160 }
|
|
2668
2703
|
},
|
|
2669
|
-
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */
|
|
2704
|
+
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React7.createElement(MenuItem3, { key: p, value: p }, PRESET_LABELS[p]))
|
|
2670
2705
|
);
|
|
2671
|
-
return /* @__PURE__ */
|
|
2706
|
+
return /* @__PURE__ */ React7.createElement(
|
|
2672
2707
|
SectionCard,
|
|
2673
2708
|
{
|
|
2674
2709
|
title: "Usage Analytics",
|
|
2675
2710
|
subtitle: `${PRESET_LABELS[currentPreset]} \xB7 ${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
|
|
2676
2711
|
actions: periodSelect
|
|
2677
2712
|
},
|
|
2678
|
-
/* @__PURE__ */
|
|
2679
|
-
/* @__PURE__ */
|
|
2713
|
+
/* @__PURE__ */ React7.createElement(MetricStrip, { metrics }),
|
|
2714
|
+
/* @__PURE__ */ React7.createElement(
|
|
2680
2715
|
Box6,
|
|
2681
2716
|
{
|
|
2682
2717
|
sx: {
|
|
@@ -2689,7 +2724,7 @@ var init_UsageStats = __esm({
|
|
|
2689
2724
|
mb: 2
|
|
2690
2725
|
}
|
|
2691
2726
|
},
|
|
2692
|
-
/* @__PURE__ */
|
|
2727
|
+
/* @__PURE__ */ React7.createElement(
|
|
2693
2728
|
SegmentedControl,
|
|
2694
2729
|
{
|
|
2695
2730
|
value: tab,
|
|
@@ -2701,7 +2736,7 @@ var init_UsageStats = __esm({
|
|
|
2701
2736
|
]
|
|
2702
2737
|
}
|
|
2703
2738
|
),
|
|
2704
|
-
tab === "models" && /* @__PURE__ */
|
|
2739
|
+
tab === "models" && /* @__PURE__ */ React7.createElement(
|
|
2705
2740
|
TextField4,
|
|
2706
2741
|
{
|
|
2707
2742
|
select: true,
|
|
@@ -2711,17 +2746,17 @@ var init_UsageStats = __esm({
|
|
|
2711
2746
|
onChange: (e) => setSelectedModel(e.target.value),
|
|
2712
2747
|
sx: { minWidth: 220 }
|
|
2713
2748
|
},
|
|
2714
|
-
/* @__PURE__ */
|
|
2715
|
-
models.map((m) => /* @__PURE__ */
|
|
2749
|
+
/* @__PURE__ */ React7.createElement(MenuItem3, { value: "all" }, "All models"),
|
|
2750
|
+
models.map((m) => /* @__PURE__ */ React7.createElement(MenuItem3, { key: m.model_name, value: m.model_name }, m.model_name))
|
|
2716
2751
|
)
|
|
2717
2752
|
),
|
|
2718
|
-
tab === "costs" && /* @__PURE__ */
|
|
2753
|
+
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
2754
|
Tooltip,
|
|
2720
2755
|
{
|
|
2721
2756
|
cursor: chart.cursor,
|
|
2722
|
-
content: /* @__PURE__ */
|
|
2757
|
+
content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
|
|
2723
2758
|
}
|
|
2724
|
-
), topSpendModels.map((m) => /* @__PURE__ */
|
|
2759
|
+
), topSpendModels.map((m) => /* @__PURE__ */ React7.createElement(
|
|
2725
2760
|
Area,
|
|
2726
2761
|
{
|
|
2727
2762
|
key: m,
|
|
@@ -2733,13 +2768,13 @@ var init_UsageStats = __esm({
|
|
|
2733
2768
|
fill: chartColor(m),
|
|
2734
2769
|
fillOpacity: 0.28
|
|
2735
2770
|
}
|
|
2736
|
-
))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */
|
|
2771
|
+
))))), !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
2772
|
Tooltip,
|
|
2738
2773
|
{
|
|
2739
2774
|
cursor: chart.cursor,
|
|
2740
|
-
content: /* @__PURE__ */
|
|
2775
|
+
content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
|
|
2741
2776
|
}
|
|
2742
|
-
), /* @__PURE__ */
|
|
2777
|
+
), /* @__PURE__ */ React7.createElement(ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ React7.createElement(
|
|
2743
2778
|
Line,
|
|
2744
2779
|
{
|
|
2745
2780
|
type: "monotone",
|
|
@@ -2750,13 +2785,13 @@ var init_UsageStats = __esm({
|
|
|
2750
2785
|
dot: { r: 2.5, strokeWidth: 0, fill: SERIES.input },
|
|
2751
2786
|
activeDot: { r: 4 }
|
|
2752
2787
|
}
|
|
2753
|
-
)))))), /* @__PURE__ */
|
|
2788
|
+
)))))), /* @__PURE__ */ React7.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React7.createElement(
|
|
2754
2789
|
ChartCard,
|
|
2755
2790
|
{
|
|
2756
2791
|
title: maxBudget > 0 ? "Cumulative spend vs budget" : "Cumulative spend",
|
|
2757
2792
|
meta: maxBudget > 0 ? `${fmtUsd(totalCumSpend)} used \xB7 ${fmtUsd(Math.max(0, maxBudget - totalCumSpend))} left` : void 0
|
|
2758
2793
|
},
|
|
2759
|
-
/* @__PURE__ */
|
|
2794
|
+
/* @__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
2795
|
YAxis,
|
|
2761
2796
|
{
|
|
2762
2797
|
tickFormatter: fmtUsdCompact,
|
|
@@ -2764,13 +2799,13 @@ var init_UsageStats = __esm({
|
|
|
2764
2799
|
domain: cumulativeDomain,
|
|
2765
2800
|
...chart.axis
|
|
2766
2801
|
}
|
|
2767
|
-
), /* @__PURE__ */
|
|
2802
|
+
), /* @__PURE__ */ React7.createElement(
|
|
2768
2803
|
Tooltip,
|
|
2769
2804
|
{
|
|
2770
2805
|
cursor: chart.cursor,
|
|
2771
|
-
content: /* @__PURE__ */
|
|
2806
|
+
content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: fmtUsd })
|
|
2772
2807
|
}
|
|
2773
|
-
), maxBudget > 0 && /* @__PURE__ */
|
|
2808
|
+
), maxBudget > 0 && /* @__PURE__ */ React7.createElement(
|
|
2774
2809
|
ReferenceLine,
|
|
2775
2810
|
{
|
|
2776
2811
|
y: maxBudget,
|
|
@@ -2778,7 +2813,7 @@ var init_UsageStats = __esm({
|
|
|
2778
2813
|
strokeDasharray: "5 4",
|
|
2779
2814
|
label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 10, fill: SERIES.budget }
|
|
2780
2815
|
}
|
|
2781
|
-
), /* @__PURE__ */
|
|
2816
|
+
), /* @__PURE__ */ React7.createElement(
|
|
2782
2817
|
Area,
|
|
2783
2818
|
{
|
|
2784
2819
|
type: "monotone",
|
|
@@ -2791,32 +2826,32 @@ var init_UsageStats = __esm({
|
|
|
2791
2826
|
}
|
|
2792
2827
|
))))
|
|
2793
2828
|
))),
|
|
2794
|
-
tab === "models" && /* @__PURE__ */
|
|
2829
|
+
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
2830
|
ChartOrFallback,
|
|
2796
2831
|
{
|
|
2797
2832
|
loading,
|
|
2798
2833
|
empty: topModelSpendBars.length === 0,
|
|
2799
2834
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2800
2835
|
},
|
|
2801
|
-
/* @__PURE__ */
|
|
2802
|
-
))), /* @__PURE__ */
|
|
2836
|
+
/* @__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) })))))
|
|
2837
|
+
))), /* @__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
2838
|
ChartOrFallback,
|
|
2804
2839
|
{
|
|
2805
2840
|
loading,
|
|
2806
2841
|
empty: modelRows.length === 0,
|
|
2807
2842
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2808
2843
|
},
|
|
2809
|
-
/* @__PURE__ */
|
|
2810
|
-
))), /* @__PURE__ */
|
|
2811
|
-
tab === "keys" && /* @__PURE__ */
|
|
2844
|
+
/* @__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] })))
|
|
2845
|
+
))), /* @__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()))))),
|
|
2846
|
+
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
2847
|
ChartOrFallback,
|
|
2813
2848
|
{
|
|
2814
2849
|
loading,
|
|
2815
2850
|
empty: topKeySpendBars.length === 0,
|
|
2816
2851
|
height: Math.max(160, topKeySpendBars.length * 32)
|
|
2817
2852
|
},
|
|
2818
|
-
/* @__PURE__ */
|
|
2819
|
-
))), /* @__PURE__ */
|
|
2853
|
+
/* @__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 })))
|
|
2854
|
+
))), /* @__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
2855
|
);
|
|
2821
2856
|
};
|
|
2822
2857
|
}
|
|
@@ -2944,12 +2979,12 @@ var init_budget = __esm({
|
|
|
2944
2979
|
});
|
|
2945
2980
|
|
|
2946
2981
|
// src/components/BudgetLimitList.tsx
|
|
2947
|
-
import
|
|
2982
|
+
import React8 from "react";
|
|
2948
2983
|
import Box7 from "@mui/material/Box";
|
|
2949
2984
|
import Paper5 from "@mui/material/Paper";
|
|
2950
2985
|
import Typography7 from "@mui/material/Typography";
|
|
2951
2986
|
import { alpha as alpha4, useTheme as useTheme2 } from "@mui/material/styles";
|
|
2952
|
-
import { Link } from "@backstage/core-components";
|
|
2987
|
+
import { Link as Link2 } from "@backstage/core-components";
|
|
2953
2988
|
function levelToneColor(tone, theme) {
|
|
2954
2989
|
switch (tone) {
|
|
2955
2990
|
case "success":
|
|
@@ -2975,7 +3010,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2975
3010
|
const tone = budgetTone2(limit.pct);
|
|
2976
3011
|
const pct = Math.round(limit.pct);
|
|
2977
3012
|
const closeTo = limit.softLimit !== void 0 && limit.spend >= limit.softLimit;
|
|
2978
|
-
return /* @__PURE__ */
|
|
3013
|
+
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
3014
|
Typography7,
|
|
2980
3015
|
{
|
|
2981
3016
|
variant: "body2",
|
|
@@ -2983,7 +3018,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2983
3018
|
title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label
|
|
2984
3019
|
},
|
|
2985
3020
|
limit.label
|
|
2986
|
-
), limit.sublabel && /* @__PURE__ */
|
|
3021
|
+
), limit.sublabel && /* @__PURE__ */ React8.createElement(
|
|
2987
3022
|
Typography7,
|
|
2988
3023
|
{
|
|
2989
3024
|
variant: "caption",
|
|
@@ -2998,7 +3033,7 @@ var init_BudgetLimitList = __esm({
|
|
|
2998
3033
|
}
|
|
2999
3034
|
},
|
|
3000
3035
|
limit.sublabel
|
|
3001
|
-
)), limit.hidden ? /* @__PURE__ */
|
|
3036
|
+
)), 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
3037
|
Box7,
|
|
3003
3038
|
{
|
|
3004
3039
|
sx: {
|
|
@@ -3009,14 +3044,14 @@ var init_BudgetLimitList = __esm({
|
|
|
3009
3044
|
mt: 0.75
|
|
3010
3045
|
}
|
|
3011
3046
|
},
|
|
3012
|
-
/* @__PURE__ */
|
|
3013
|
-
/* @__PURE__ */
|
|
3047
|
+
/* @__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`)),
|
|
3048
|
+
/* @__PURE__ */ React8.createElement(Typography7, { variant: "caption", color: "text.secondary" }, fmtBudgetDuration(limit.budgetDuration) ? `resets ${fmtBudgetDuration(limit.budgetDuration)}` : "never resets")
|
|
3014
3049
|
));
|
|
3015
3050
|
};
|
|
3016
3051
|
TaggedLimit = ({ limit }) => {
|
|
3017
3052
|
const theme = useTheme2();
|
|
3018
3053
|
const tone = budgetTone2(limit.pct);
|
|
3019
|
-
return /* @__PURE__ */
|
|
3054
|
+
return /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(
|
|
3020
3055
|
Typography7,
|
|
3021
3056
|
{
|
|
3022
3057
|
sx: {
|
|
@@ -3029,7 +3064,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3029
3064
|
}
|
|
3030
3065
|
},
|
|
3031
3066
|
limit.kind
|
|
3032
|
-
), /* @__PURE__ */
|
|
3067
|
+
), /* @__PURE__ */ React8.createElement(LimitCard, { limit }));
|
|
3033
3068
|
};
|
|
3034
3069
|
MoreKeysNote = ({
|
|
3035
3070
|
count,
|
|
@@ -3038,12 +3073,12 @@ var init_BudgetLimitList = __esm({
|
|
|
3038
3073
|
}) => {
|
|
3039
3074
|
const text = label ?? `+${count} more budgeted key${count > 1 ? "s" : ""} further from the cap`;
|
|
3040
3075
|
if (!href) {
|
|
3041
|
-
return /* @__PURE__ */
|
|
3076
|
+
return /* @__PURE__ */ React8.createElement(Typography7, { variant: "caption", color: "text.secondary" }, text);
|
|
3042
3077
|
}
|
|
3043
|
-
return /* @__PURE__ */
|
|
3078
|
+
return /* @__PURE__ */ React8.createElement(Link2, { to: href, variant: "caption" }, text, " \u2192");
|
|
3044
3079
|
};
|
|
3045
|
-
BudgetLimitList = ({ limits, hiddenKeyCount = 0, keysHref }) => /* @__PURE__ */
|
|
3046
|
-
LimitListPanel = ({ children }) => /* @__PURE__ */
|
|
3080
|
+
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 }));
|
|
3081
|
+
LimitListPanel = ({ children }) => /* @__PURE__ */ React8.createElement(
|
|
3047
3082
|
Box7,
|
|
3048
3083
|
{
|
|
3049
3084
|
sx: (theme) => ({
|
|
@@ -3059,7 +3094,7 @@ var init_BudgetLimitList = __esm({
|
|
|
3059
3094
|
});
|
|
3060
3095
|
|
|
3061
3096
|
// src/components/LiteLLMBudgetWidget.tsx
|
|
3062
|
-
import
|
|
3097
|
+
import React9, { useEffect as useEffect3, useMemo as useMemo6, useState as useState5 } from "react";
|
|
3063
3098
|
import Paper6 from "@mui/material/Paper";
|
|
3064
3099
|
import Box8 from "@mui/material/Box";
|
|
3065
3100
|
import Typography8 from "@mui/material/Typography";
|
|
@@ -3086,7 +3121,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3086
3121
|
tone,
|
|
3087
3122
|
isLast,
|
|
3088
3123
|
children
|
|
3089
|
-
}) => /* @__PURE__ */
|
|
3124
|
+
}) => /* @__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
3125
|
Box8,
|
|
3091
3126
|
{
|
|
3092
3127
|
sx: (theme) => ({
|
|
@@ -3104,7 +3139,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3104
3139
|
})
|
|
3105
3140
|
},
|
|
3106
3141
|
rank
|
|
3107
|
-
), /* @__PURE__ */
|
|
3142
|
+
), /* @__PURE__ */ React9.createElement(
|
|
3108
3143
|
Box8,
|
|
3109
3144
|
{
|
|
3110
3145
|
sx: (theme) => ({
|
|
@@ -3114,7 +3149,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3114
3149
|
bgcolor: isLast ? "transparent" : alpha5(theme.palette.text.primary, 0.12)
|
|
3115
3150
|
})
|
|
3116
3151
|
}
|
|
3117
|
-
)), /* @__PURE__ */
|
|
3152
|
+
)), /* @__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
3153
|
Typography8,
|
|
3119
3154
|
{
|
|
3120
3155
|
sx: (theme) => ({
|
|
@@ -3126,8 +3161,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3126
3161
|
})
|
|
3127
3162
|
},
|
|
3128
3163
|
name
|
|
3129
|
-
), /* @__PURE__ */
|
|
3130
|
-
EmptyLimitCard = ({ children }) => /* @__PURE__ */
|
|
3164
|
+
), /* @__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)));
|
|
3165
|
+
EmptyLimitCard = ({ children }) => /* @__PURE__ */ React9.createElement(
|
|
3131
3166
|
Paper6,
|
|
3132
3167
|
{
|
|
3133
3168
|
variant: "outlined",
|
|
@@ -3138,7 +3173,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3138
3173
|
bgcolor: alpha5(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.03 : 0.015)
|
|
3139
3174
|
})
|
|
3140
3175
|
},
|
|
3141
|
-
/* @__PURE__ */
|
|
3176
|
+
/* @__PURE__ */ React9.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, children)
|
|
3142
3177
|
);
|
|
3143
3178
|
CLOSEST_LEVEL_LABEL = {
|
|
3144
3179
|
key: "key",
|
|
@@ -3146,7 +3181,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3146
3181
|
team: "team"
|
|
3147
3182
|
};
|
|
3148
3183
|
KEYS_PATH = "/litellm?tab=keys";
|
|
3149
|
-
HierarchyNote = ({ withReset }) => /* @__PURE__ */
|
|
3184
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ React9.createElement(
|
|
3150
3185
|
Box8,
|
|
3151
3186
|
{
|
|
3152
3187
|
sx: (theme) => ({
|
|
@@ -3157,11 +3192,11 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3157
3192
|
borderColor: alpha5(theme.palette.text.primary, 0.18)
|
|
3158
3193
|
})
|
|
3159
3194
|
},
|
|
3160
|
-
/* @__PURE__ */
|
|
3195
|
+
/* @__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
3196
|
);
|
|
3162
3197
|
FullBody = ({ summary }) => {
|
|
3163
3198
|
const hasBudgetedKeys = summary.keys.length + summary.hiddenBudgetedKeys > 0;
|
|
3164
|
-
return /* @__PURE__ */
|
|
3199
|
+
return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
|
|
3165
3200
|
LevelFrame,
|
|
3166
3201
|
{
|
|
3167
3202
|
rank: 1,
|
|
@@ -3169,10 +3204,10 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3169
3204
|
tagline: "per-key cap \xB7 highest priority",
|
|
3170
3205
|
note: hasBudgetedKeys ? void 0 : "no key budgets set"
|
|
3171
3206
|
},
|
|
3172
|
-
!hasBudgetedKeys && /* @__PURE__ */
|
|
3173
|
-
summary.keys.map((k) => /* @__PURE__ */
|
|
3174
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */
|
|
3175
|
-
), /* @__PURE__ */
|
|
3207
|
+
!hasBudgetedKeys && /* @__PURE__ */ React9.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3208
|
+
summary.keys.map((k) => /* @__PURE__ */ React9.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3209
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React9.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys, href: KEYS_PATH })
|
|
3210
|
+
), /* @__PURE__ */ React9.createElement(
|
|
3176
3211
|
LevelFrame,
|
|
3177
3212
|
{
|
|
3178
3213
|
rank: 2,
|
|
@@ -3180,15 +3215,15 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3180
3215
|
tagline: "cap on everything you spend with your own keys",
|
|
3181
3216
|
note: "skipped for team-bound keys"
|
|
3182
3217
|
},
|
|
3183
|
-
summary.user ? /* @__PURE__ */
|
|
3184
|
-
), /* @__PURE__ */
|
|
3218
|
+
summary.user ? /* @__PURE__ */ React9.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React9.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
|
|
3219
|
+
), /* @__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
3220
|
};
|
|
3186
3221
|
CompactBody = ({ summary }) => {
|
|
3187
3222
|
const { count } = budgetHeadline(summary);
|
|
3188
3223
|
if (count === 0) {
|
|
3189
|
-
return /* @__PURE__ */
|
|
3224
|
+
return /* @__PURE__ */ React9.createElement(Typography8, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
|
|
3190
3225
|
}
|
|
3191
|
-
return /* @__PURE__ */
|
|
3226
|
+
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
3227
|
};
|
|
3193
3228
|
LiteLLMBudgetWidget = ({
|
|
3194
3229
|
title = "Budget Policy",
|
|
@@ -3239,8 +3274,8 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3239
3274
|
headline.closest.pct
|
|
3240
3275
|
)}%`;
|
|
3241
3276
|
const summaryText = headline.count === 0 ? "no limits apply" : `${limitsWord}${closestWord}`;
|
|
3242
|
-
const body = /* @__PURE__ */
|
|
3243
|
-
return /* @__PURE__ */
|
|
3277
|
+
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 })));
|
|
3278
|
+
return /* @__PURE__ */ React9.createElement(Paper6, { sx: { p: 2 } }, collapsible ? /* @__PURE__ */ React9.createElement(
|
|
3244
3279
|
Box8,
|
|
3245
3280
|
{
|
|
3246
3281
|
onClick: () => setExpanded((e) => !e),
|
|
@@ -3253,14 +3288,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3253
3288
|
mb: expanded ? 1.5 : 0
|
|
3254
3289
|
}
|
|
3255
3290
|
},
|
|
3256
|
-
/* @__PURE__ */
|
|
3291
|
+
/* @__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
3292
|
StatusPill,
|
|
3258
3293
|
{
|
|
3259
3294
|
label: summaryText,
|
|
3260
3295
|
tone: budgetTone2(headline.closest?.pct ?? 0)
|
|
3261
3296
|
}
|
|
3262
3297
|
))),
|
|
3263
|
-
/* @__PURE__ */
|
|
3298
|
+
/* @__PURE__ */ React9.createElement(
|
|
3264
3299
|
IconButton4,
|
|
3265
3300
|
{
|
|
3266
3301
|
size: "small",
|
|
@@ -3276,9 +3311,9 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3276
3311
|
transition: theme.transitions.create("transform")
|
|
3277
3312
|
})
|
|
3278
3313
|
},
|
|
3279
|
-
/* @__PURE__ */
|
|
3314
|
+
/* @__PURE__ */ React9.createElement(ExpandMore2, null)
|
|
3280
3315
|
)
|
|
3281
|
-
) : /* @__PURE__ */
|
|
3316
|
+
) : /* @__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
3317
|
Box8,
|
|
3283
3318
|
{
|
|
3284
3319
|
sx: (theme) => ({
|
|
@@ -3294,7 +3329,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3294
3329
|
});
|
|
3295
3330
|
|
|
3296
3331
|
// src/components/TeamUsage.tsx
|
|
3297
|
-
import
|
|
3332
|
+
import React10, { useState as useState6 } from "react";
|
|
3298
3333
|
import Paper7 from "@mui/material/Paper";
|
|
3299
3334
|
import Box9 from "@mui/material/Box";
|
|
3300
3335
|
import Typography9 from "@mui/material/Typography";
|
|
@@ -3311,7 +3346,7 @@ import IconButton5 from "@mui/material/IconButton";
|
|
|
3311
3346
|
import Button5 from "@mui/material/Button";
|
|
3312
3347
|
import CircularProgress5 from "@mui/material/CircularProgress";
|
|
3313
3348
|
import { alpha as alpha6 } from "@mui/material/styles";
|
|
3314
|
-
import { ExpandMore as ExpandMore3, Group, Add as
|
|
3349
|
+
import { ExpandMore as ExpandMore3, Group, Add as Add2 } from "@mui/icons-material";
|
|
3315
3350
|
import {
|
|
3316
3351
|
AreaChart as AreaChart2,
|
|
3317
3352
|
Area as Area2,
|
|
@@ -3354,10 +3389,10 @@ var init_TeamUsage = __esm({
|
|
|
3354
3389
|
const renderDailySpendSection = () => {
|
|
3355
3390
|
if (hidden) return null;
|
|
3356
3391
|
if (usageLoading) {
|
|
3357
|
-
return /* @__PURE__ */
|
|
3392
|
+
return /* @__PURE__ */ React10.createElement(Box9, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React10.createElement(CircularProgress5, { size: 22 }));
|
|
3358
3393
|
}
|
|
3359
3394
|
if (dailyData.length === 0) return null;
|
|
3360
|
-
return /* @__PURE__ */
|
|
3395
|
+
return /* @__PURE__ */ React10.createElement(Box9, { mb: 2.5 }, /* @__PURE__ */ React10.createElement(
|
|
3361
3396
|
Typography9,
|
|
3362
3397
|
{
|
|
3363
3398
|
variant: "caption",
|
|
@@ -3365,13 +3400,13 @@ var init_TeamUsage = __esm({
|
|
|
3365
3400
|
sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
|
|
3366
3401
|
},
|
|
3367
3402
|
"Daily spend"
|
|
3368
|
-
), /* @__PURE__ */
|
|
3403
|
+
), /* @__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
3404
|
Tooltip2,
|
|
3370
3405
|
{
|
|
3371
3406
|
cursor: chart.cursor,
|
|
3372
|
-
content: /* @__PURE__ */
|
|
3407
|
+
content: /* @__PURE__ */ React10.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
3373
3408
|
}
|
|
3374
|
-
), /* @__PURE__ */
|
|
3409
|
+
), /* @__PURE__ */ React10.createElement(
|
|
3375
3410
|
Area2,
|
|
3376
3411
|
{
|
|
3377
3412
|
type: "monotone",
|
|
@@ -3384,7 +3419,7 @@ var init_TeamUsage = __esm({
|
|
|
3384
3419
|
}
|
|
3385
3420
|
)))));
|
|
3386
3421
|
};
|
|
3387
|
-
const sectionLabel = (label) => /* @__PURE__ */
|
|
3422
|
+
const sectionLabel = (label) => /* @__PURE__ */ React10.createElement(
|
|
3388
3423
|
Typography9,
|
|
3389
3424
|
{
|
|
3390
3425
|
variant: "caption",
|
|
@@ -3393,7 +3428,7 @@ var init_TeamUsage = __esm({
|
|
|
3393
3428
|
},
|
|
3394
3429
|
label
|
|
3395
3430
|
);
|
|
3396
|
-
return /* @__PURE__ */
|
|
3431
|
+
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
3432
|
Box9,
|
|
3398
3433
|
{
|
|
3399
3434
|
sx: (theme) => ({
|
|
@@ -3408,8 +3443,8 @@ var init_TeamUsage = __esm({
|
|
|
3408
3443
|
color: theme.palette.primary.main
|
|
3409
3444
|
})
|
|
3410
3445
|
},
|
|
3411
|
-
/* @__PURE__ */
|
|
3412
|
-
), /* @__PURE__ */
|
|
3446
|
+
/* @__PURE__ */ React10.createElement(Group, { fontSize: "small" })
|
|
3447
|
+
), /* @__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
3448
|
Typography9,
|
|
3414
3449
|
{
|
|
3415
3450
|
variant: "caption",
|
|
@@ -3417,7 +3452,7 @@ var init_TeamUsage = __esm({
|
|
|
3417
3452
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3418
3453
|
},
|
|
3419
3454
|
team.team_id
|
|
3420
|
-
)), isOver && /* @__PURE__ */
|
|
3455
|
+
)), 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
3456
|
Button5,
|
|
3422
3457
|
{
|
|
3423
3458
|
size: "small",
|
|
@@ -3426,7 +3461,7 @@ var init_TeamUsage = __esm({
|
|
|
3426
3461
|
sx: { textTransform: "none" }
|
|
3427
3462
|
},
|
|
3428
3463
|
"Edit"
|
|
3429
|
-
), /* @__PURE__ */
|
|
3464
|
+
), /* @__PURE__ */ React10.createElement(
|
|
3430
3465
|
IconButton5,
|
|
3431
3466
|
{
|
|
3432
3467
|
size: "small",
|
|
@@ -3439,8 +3474,8 @@ var init_TeamUsage = __esm({
|
|
|
3439
3474
|
transition: theme.transitions.create("transform")
|
|
3440
3475
|
})
|
|
3441
3476
|
},
|
|
3442
|
-
/* @__PURE__ */
|
|
3443
|
-
)), /* @__PURE__ */
|
|
3477
|
+
/* @__PURE__ */ React10.createElement(ExpandMore3, null)
|
|
3478
|
+
)), /* @__PURE__ */ React10.createElement(
|
|
3444
3479
|
Box9,
|
|
3445
3480
|
{
|
|
3446
3481
|
sx: {
|
|
@@ -3451,20 +3486,20 @@ var init_TeamUsage = __esm({
|
|
|
3451
3486
|
maxWidth: 640
|
|
3452
3487
|
}
|
|
3453
3488
|
},
|
|
3454
|
-
/* @__PURE__ */
|
|
3455
|
-
/* @__PURE__ */
|
|
3456
|
-
/* @__PURE__ */
|
|
3457
|
-
/* @__PURE__ */
|
|
3458
|
-
/* @__PURE__ */
|
|
3459
|
-
/* @__PURE__ */
|
|
3460
|
-
), hidden && /* @__PURE__ */
|
|
3489
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
|
|
3490
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
|
|
3491
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Budget", value: budgetStatValue(hidden, budget) }),
|
|
3492
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "Spend", value: hidden ? "\u2014" : fmtUsd2(spend) }),
|
|
3493
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
3494
|
+
/* @__PURE__ */ React10.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
3495
|
+
), 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
3496
|
TableContainer4,
|
|
3462
3497
|
{
|
|
3463
3498
|
component: Paper7,
|
|
3464
3499
|
variant: "outlined",
|
|
3465
3500
|
sx: { borderRadius: 1.5 }
|
|
3466
3501
|
},
|
|
3467
|
-
/* @__PURE__ */
|
|
3502
|
+
/* @__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
3503
|
Typography9,
|
|
3469
3504
|
{
|
|
3470
3505
|
variant: "caption",
|
|
@@ -3472,14 +3507,14 @@ var init_TeamUsage = __esm({
|
|
|
3472
3507
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
3473
3508
|
},
|
|
3474
3509
|
m.user_id
|
|
3475
|
-
)) : /* @__PURE__ */
|
|
3510
|
+
)) : /* @__PURE__ */ React10.createElement(
|
|
3476
3511
|
Typography9,
|
|
3477
3512
|
{
|
|
3478
3513
|
variant: "body2",
|
|
3479
3514
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
3480
3515
|
},
|
|
3481
3516
|
m.user_id
|
|
3482
|
-
)), /* @__PURE__ */
|
|
3517
|
+
)), /* @__PURE__ */ React10.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React10.createElement(
|
|
3483
3518
|
StatusPill,
|
|
3484
3519
|
{
|
|
3485
3520
|
label: m.role,
|
|
@@ -3487,7 +3522,7 @@ var init_TeamUsage = __esm({
|
|
|
3487
3522
|
dot: false
|
|
3488
3523
|
}
|
|
3489
3524
|
))))))
|
|
3490
|
-
) : /* @__PURE__ */
|
|
3525
|
+
) : /* @__PURE__ */ React10.createElement(Typography9, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
|
|
3491
3526
|
};
|
|
3492
3527
|
TeamUsage = ({
|
|
3493
3528
|
teams,
|
|
@@ -3499,22 +3534,22 @@ var init_TeamUsage = __esm({
|
|
|
3499
3534
|
canCreate,
|
|
3500
3535
|
onCreateTeam
|
|
3501
3536
|
}) => {
|
|
3502
|
-
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */
|
|
3537
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React10.createElement(
|
|
3503
3538
|
Button5,
|
|
3504
3539
|
{
|
|
3505
3540
|
variant: "contained",
|
|
3506
3541
|
color: "primary",
|
|
3507
3542
|
disableElevation: true,
|
|
3508
|
-
startIcon: /* @__PURE__ */
|
|
3543
|
+
startIcon: /* @__PURE__ */ React10.createElement(Add2, null),
|
|
3509
3544
|
onClick: onCreateTeam
|
|
3510
3545
|
},
|
|
3511
3546
|
"Create Team"
|
|
3512
3547
|
) : void 0;
|
|
3513
3548
|
if (loading) {
|
|
3514
|
-
return /* @__PURE__ */
|
|
3549
|
+
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
3550
|
}
|
|
3516
3551
|
if (!teams.length) {
|
|
3517
|
-
return /* @__PURE__ */
|
|
3552
|
+
return /* @__PURE__ */ React10.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React10.createElement(
|
|
3518
3553
|
EmptyState,
|
|
3519
3554
|
{
|
|
3520
3555
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -3522,14 +3557,14 @@ var init_TeamUsage = __esm({
|
|
|
3522
3557
|
}
|
|
3523
3558
|
));
|
|
3524
3559
|
}
|
|
3525
|
-
return /* @__PURE__ */
|
|
3560
|
+
return /* @__PURE__ */ React10.createElement(
|
|
3526
3561
|
SectionCard,
|
|
3527
3562
|
{
|
|
3528
3563
|
title: "Teams",
|
|
3529
3564
|
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
3530
3565
|
actions: createAction
|
|
3531
3566
|
},
|
|
3532
|
-
/* @__PURE__ */
|
|
3567
|
+
/* @__PURE__ */ React10.createElement(Box9, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React10.createElement(
|
|
3533
3568
|
TeamCard,
|
|
3534
3569
|
{
|
|
3535
3570
|
key: team.team_id,
|
|
@@ -3546,7 +3581,7 @@ var init_TeamUsage = __esm({
|
|
|
3546
3581
|
});
|
|
3547
3582
|
|
|
3548
3583
|
// src/components/ModelsTable.tsx
|
|
3549
|
-
import
|
|
3584
|
+
import React11, { useState as useState7, useMemo as useMemo7, useCallback } from "react";
|
|
3550
3585
|
import Box10 from "@mui/material/Box";
|
|
3551
3586
|
import Table5 from "@mui/material/Table";
|
|
3552
3587
|
import TableBody5 from "@mui/material/TableBody";
|
|
@@ -3678,13 +3713,13 @@ var init_ModelsTable = __esm({
|
|
|
3678
3713
|
return theme.palette.info.main;
|
|
3679
3714
|
}
|
|
3680
3715
|
};
|
|
3681
|
-
return /* @__PURE__ */
|
|
3716
|
+
return /* @__PURE__ */ React11.createElement(
|
|
3682
3717
|
SectionCard,
|
|
3683
3718
|
{
|
|
3684
3719
|
title: "Available Models",
|
|
3685
3720
|
subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
|
|
3686
3721
|
},
|
|
3687
|
-
/* @__PURE__ */
|
|
3722
|
+
/* @__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
3723
|
Select,
|
|
3689
3724
|
{
|
|
3690
3725
|
labelId: "team-select-label",
|
|
@@ -3692,8 +3727,8 @@ var init_ModelsTable = __esm({
|
|
|
3692
3727
|
label: "Team",
|
|
3693
3728
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
3694
3729
|
},
|
|
3695
|
-
teams.map((t) => /* @__PURE__ */
|
|
3696
|
-
)), /* @__PURE__ */
|
|
3730
|
+
teams.map((t) => /* @__PURE__ */ React11.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
|
|
3731
|
+
)), /* @__PURE__ */ React11.createElement(
|
|
3697
3732
|
TextField5,
|
|
3698
3733
|
{
|
|
3699
3734
|
label: "Filter Models",
|
|
@@ -3703,33 +3738,33 @@ var init_ModelsTable = __esm({
|
|
|
3703
3738
|
disabled: !selectedTeamId,
|
|
3704
3739
|
sx: { minWidth: 240, mb: 2 },
|
|
3705
3740
|
InputProps: {
|
|
3706
|
-
startAdornment: /* @__PURE__ */
|
|
3741
|
+
startAdornment: /* @__PURE__ */ React11.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React11.createElement(Search2, { fontSize: "small" }))
|
|
3707
3742
|
}
|
|
3708
3743
|
}
|
|
3709
3744
|
)),
|
|
3710
|
-
loading && /* @__PURE__ */
|
|
3711
|
-
!loading && !selectedTeamId && /* @__PURE__ */
|
|
3745
|
+
loading && /* @__PURE__ */ React11.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
3746
|
+
!loading && !selectedTeamId && /* @__PURE__ */ React11.createElement(
|
|
3712
3747
|
EmptyState,
|
|
3713
3748
|
{
|
|
3714
3749
|
message: "Select a team to see available models",
|
|
3715
3750
|
hint: "Each team may have a different set of models assigned"
|
|
3716
3751
|
}
|
|
3717
3752
|
),
|
|
3718
|
-
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */
|
|
3753
|
+
!loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React11.createElement(
|
|
3719
3754
|
EmptyState,
|
|
3720
3755
|
{
|
|
3721
3756
|
message: "No models available for this team",
|
|
3722
3757
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
3723
3758
|
}
|
|
3724
3759
|
),
|
|
3725
|
-
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */
|
|
3760
|
+
!loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React11.createElement(
|
|
3726
3761
|
EmptyState,
|
|
3727
3762
|
{
|
|
3728
3763
|
message: "No models match your search",
|
|
3729
3764
|
hint: "Try a different search term"
|
|
3730
3765
|
}
|
|
3731
3766
|
),
|
|
3732
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */
|
|
3767
|
+
!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
3768
|
"span",
|
|
3734
3769
|
{
|
|
3735
3770
|
role: "button",
|
|
@@ -3739,8 +3774,8 @@ var init_ModelsTable = __esm({
|
|
|
3739
3774
|
onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
|
|
3740
3775
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3741
3776
|
},
|
|
3742
|
-
/* @__PURE__ */
|
|
3743
|
-
)), /* @__PURE__ */
|
|
3777
|
+
/* @__PURE__ */ React11.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
3778
|
+
)), /* @__PURE__ */ React11.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React11.createElement(
|
|
3744
3779
|
Box10,
|
|
3745
3780
|
{
|
|
3746
3781
|
component: "span",
|
|
@@ -3751,8 +3786,8 @@ var init_ModelsTable = __esm({
|
|
|
3751
3786
|
sx: quietIconButtonSx("accent"),
|
|
3752
3787
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
3753
3788
|
},
|
|
3754
|
-
/* @__PURE__ */
|
|
3755
|
-
)))), /* @__PURE__ */
|
|
3789
|
+
/* @__PURE__ */ React11.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
|
|
3790
|
+
)))), /* @__PURE__ */ React11.createElement(TableCell5, null, /* @__PURE__ */ React11.createElement(
|
|
3756
3791
|
Box10,
|
|
3757
3792
|
{
|
|
3758
3793
|
component: "span",
|
|
@@ -3769,8 +3804,8 @@ var init_ModelsTable = __esm({
|
|
|
3769
3804
|
}
|
|
3770
3805
|
},
|
|
3771
3806
|
m.mode
|
|
3772
|
-
)), /* @__PURE__ */
|
|
3773
|
-
/* @__PURE__ */
|
|
3807
|
+
)), /* @__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" })))))))),
|
|
3808
|
+
/* @__PURE__ */ React11.createElement(
|
|
3774
3809
|
Snackbar,
|
|
3775
3810
|
{
|
|
3776
3811
|
open: copiedSnackbar,
|
|
@@ -3778,7 +3813,7 @@ var init_ModelsTable = __esm({
|
|
|
3778
3813
|
onClose: () => setCopiedSnackbar(false),
|
|
3779
3814
|
anchorOrigin: { vertical: "bottom", horizontal: "center" }
|
|
3780
3815
|
},
|
|
3781
|
-
/* @__PURE__ */
|
|
3816
|
+
/* @__PURE__ */ React11.createElement(Alert4, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
|
|
3782
3817
|
)
|
|
3783
3818
|
);
|
|
3784
3819
|
};
|
|
@@ -3786,7 +3821,7 @@ var init_ModelsTable = __esm({
|
|
|
3786
3821
|
});
|
|
3787
3822
|
|
|
3788
3823
|
// src/components/AuditLog.tsx
|
|
3789
|
-
import
|
|
3824
|
+
import React12, { useState as useState8, useMemo as useMemo8 } from "react";
|
|
3790
3825
|
import Box11 from "@mui/material/Box";
|
|
3791
3826
|
import Typography11 from "@mui/material/Typography";
|
|
3792
3827
|
import Table6 from "@mui/material/Table";
|
|
@@ -3825,10 +3860,10 @@ function formatDateTime(iso) {
|
|
|
3825
3860
|
}
|
|
3826
3861
|
function renderAuditLogBody(loading, entries) {
|
|
3827
3862
|
if (loading) {
|
|
3828
|
-
return /* @__PURE__ */
|
|
3863
|
+
return /* @__PURE__ */ React12.createElement(TableRow6, null, /* @__PURE__ */ React12.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React12.createElement(Skeleton5, { variant: "rounded", height: 160 })));
|
|
3829
3864
|
}
|
|
3830
3865
|
if (entries.length === 0) {
|
|
3831
|
-
return /* @__PURE__ */
|
|
3866
|
+
return /* @__PURE__ */ React12.createElement(TableRow6, null, /* @__PURE__ */ React12.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React12.createElement(
|
|
3832
3867
|
EmptyState,
|
|
3833
3868
|
{
|
|
3834
3869
|
message: "No audit events found",
|
|
@@ -3836,7 +3871,7 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3836
3871
|
}
|
|
3837
3872
|
)));
|
|
3838
3873
|
}
|
|
3839
|
-
return entries.map((entry) => /* @__PURE__ */
|
|
3874
|
+
return entries.map((entry) => /* @__PURE__ */ React12.createElement(DetailRow, { key: entry.id, entry }));
|
|
3840
3875
|
}
|
|
3841
3876
|
var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
3842
3877
|
var init_AuditLog = __esm({
|
|
@@ -3860,7 +3895,7 @@ var init_AuditLog = __esm({
|
|
|
3860
3895
|
DetailRow = ({ entry }) => {
|
|
3861
3896
|
const [open, setOpen] = useState8(false);
|
|
3862
3897
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3863
|
-
return /* @__PURE__ */
|
|
3898
|
+
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
3899
|
IconButton6,
|
|
3865
3900
|
{
|
|
3866
3901
|
size: "small",
|
|
@@ -3873,8 +3908,8 @@ var init_AuditLog = __esm({
|
|
|
3873
3908
|
transition: theme.transitions.create("transform")
|
|
3874
3909
|
})
|
|
3875
3910
|
},
|
|
3876
|
-
/* @__PURE__ */
|
|
3877
|
-
)), /* @__PURE__ */
|
|
3911
|
+
/* @__PURE__ */ React12.createElement(KeyboardArrowDown, { fontSize: "small" })
|
|
3912
|
+
)), /* @__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
3913
|
Typography11,
|
|
3879
3914
|
{
|
|
3880
3915
|
variant: "body2",
|
|
@@ -3884,7 +3919,7 @@ var init_AuditLog = __esm({
|
|
|
3884
3919
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3885
3920
|
},
|
|
3886
3921
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3887
|
-
)), /* @__PURE__ */
|
|
3922
|
+
)), /* @__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
3923
|
Box11,
|
|
3889
3924
|
{
|
|
3890
3925
|
display: "flex",
|
|
@@ -3897,7 +3932,7 @@ var init_AuditLog = __esm({
|
|
|
3897
3932
|
bgcolor: alpha8(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
|
|
3898
3933
|
})
|
|
3899
3934
|
},
|
|
3900
|
-
entry.before_value && /* @__PURE__ */
|
|
3935
|
+
entry.before_value && /* @__PURE__ */ React12.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React12.createElement(
|
|
3901
3936
|
Typography11,
|
|
3902
3937
|
{
|
|
3903
3938
|
variant: "caption",
|
|
@@ -3907,8 +3942,8 @@ var init_AuditLog = __esm({
|
|
|
3907
3942
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3908
3943
|
},
|
|
3909
3944
|
"Before"
|
|
3910
|
-
), /* @__PURE__ */
|
|
3911
|
-
entry.updated_values && /* @__PURE__ */
|
|
3945
|
+
), /* @__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))),
|
|
3946
|
+
entry.updated_values && /* @__PURE__ */ React12.createElement(Box11, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React12.createElement(
|
|
3912
3947
|
Typography11,
|
|
3913
3948
|
{
|
|
3914
3949
|
variant: "caption",
|
|
@@ -3918,7 +3953,7 @@ var init_AuditLog = __esm({
|
|
|
3918
3953
|
sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
|
|
3919
3954
|
},
|
|
3920
3955
|
"After"
|
|
3921
|
-
), /* @__PURE__ */
|
|
3956
|
+
), /* @__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
3957
|
)))));
|
|
3923
3958
|
};
|
|
3924
3959
|
AuditLog = ({ api }) => {
|
|
@@ -3935,13 +3970,13 @@ var init_AuditLog = __esm({
|
|
|
3935
3970
|
);
|
|
3936
3971
|
const entries = value?.audit_logs ?? [];
|
|
3937
3972
|
const total = value?.total ?? 0;
|
|
3938
|
-
return /* @__PURE__ */
|
|
3973
|
+
return /* @__PURE__ */ React12.createElement(
|
|
3939
3974
|
SectionCard,
|
|
3940
3975
|
{
|
|
3941
3976
|
title: "Audit Log",
|
|
3942
3977
|
subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
|
|
3943
3978
|
flush: true,
|
|
3944
|
-
actions: /* @__PURE__ */
|
|
3979
|
+
actions: /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(
|
|
3945
3980
|
TextField6,
|
|
3946
3981
|
{
|
|
3947
3982
|
size: "small",
|
|
@@ -3954,12 +3989,12 @@ var init_AuditLog = __esm({
|
|
|
3954
3989
|
},
|
|
3955
3990
|
sx: { minWidth: 150 }
|
|
3956
3991
|
},
|
|
3957
|
-
/* @__PURE__ */
|
|
3958
|
-
/* @__PURE__ */
|
|
3959
|
-
/* @__PURE__ */
|
|
3960
|
-
/* @__PURE__ */
|
|
3961
|
-
/* @__PURE__ */
|
|
3962
|
-
), /* @__PURE__ */
|
|
3992
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "" }, "All actions"),
|
|
3993
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "created" }, "Created"),
|
|
3994
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "updated" }, "Updated"),
|
|
3995
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
|
|
3996
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "blocked" }, "Blocked")
|
|
3997
|
+
), /* @__PURE__ */ React12.createElement(
|
|
3963
3998
|
TextField6,
|
|
3964
3999
|
{
|
|
3965
4000
|
size: "small",
|
|
@@ -3972,13 +4007,13 @@ var init_AuditLog = __esm({
|
|
|
3972
4007
|
},
|
|
3973
4008
|
sx: { minWidth: 150 }
|
|
3974
4009
|
},
|
|
3975
|
-
/* @__PURE__ */
|
|
3976
|
-
/* @__PURE__ */
|
|
3977
|
-
/* @__PURE__ */
|
|
3978
|
-
/* @__PURE__ */
|
|
3979
|
-
/* @__PURE__ */
|
|
3980
|
-
/* @__PURE__ */
|
|
3981
|
-
), /* @__PURE__ */
|
|
4010
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "" }, "All tables"),
|
|
4011
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
4012
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
4013
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
|
|
4014
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
|
|
4015
|
+
/* @__PURE__ */ React12.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
|
|
4016
|
+
), /* @__PURE__ */ React12.createElement(
|
|
3982
4017
|
TextField6,
|
|
3983
4018
|
{
|
|
3984
4019
|
size: "small",
|
|
@@ -3992,9 +4027,9 @@ var init_AuditLog = __esm({
|
|
|
3992
4027
|
}
|
|
3993
4028
|
))
|
|
3994
4029
|
},
|
|
3995
|
-
error && /* @__PURE__ */
|
|
3996
|
-
/* @__PURE__ */
|
|
3997
|
-
/* @__PURE__ */
|
|
4030
|
+
error && /* @__PURE__ */ React12.createElement(Box11, { px: 2.5, pb: 2 }, /* @__PURE__ */ React12.createElement(Alert5, { severity: "error" }, error.message)),
|
|
4031
|
+
/* @__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)))),
|
|
4032
|
+
/* @__PURE__ */ React12.createElement(
|
|
3998
4033
|
TablePagination,
|
|
3999
4034
|
{
|
|
4000
4035
|
component: "div",
|
|
@@ -4049,7 +4084,7 @@ var LiteLLMPage_exports = {};
|
|
|
4049
4084
|
__export(LiteLLMPage_exports, {
|
|
4050
4085
|
LiteLLMPage: () => LiteLLMPage
|
|
4051
4086
|
});
|
|
4052
|
-
import
|
|
4087
|
+
import React13, { useState as useState9, useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo9 } from "react";
|
|
4053
4088
|
import Box12 from "@mui/material/Box";
|
|
4054
4089
|
import Snackbar2 from "@mui/material/Snackbar";
|
|
4055
4090
|
import Alert6 from "@mui/material/Alert";
|
|
@@ -4335,14 +4370,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4335
4370
|
);
|
|
4336
4371
|
const isInitialLoading = userLoading && !userInfo;
|
|
4337
4372
|
if (isInitialLoading) {
|
|
4338
|
-
return /* @__PURE__ */
|
|
4373
|
+
return /* @__PURE__ */ React13.createElement(Box12, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React13.createElement(CircularProgress6, null));
|
|
4339
4374
|
}
|
|
4340
4375
|
if (userError || !userInfo) {
|
|
4341
4376
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
4342
4377
|
const hint = userError?.body?.hint;
|
|
4343
|
-
return /* @__PURE__ */
|
|
4378
|
+
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
4379
|
}
|
|
4345
|
-
const pageTabs = /* @__PURE__ */
|
|
4380
|
+
const pageTabs = /* @__PURE__ */ React13.createElement(
|
|
4346
4381
|
Tabs2,
|
|
4347
4382
|
{
|
|
4348
4383
|
value: activeTab,
|
|
@@ -4355,13 +4390,13 @@ var init_LiteLLMPage = __esm({
|
|
|
4355
4390
|
'& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
|
|
4356
4391
|
}
|
|
4357
4392
|
},
|
|
4358
|
-
/* @__PURE__ */
|
|
4359
|
-
/* @__PURE__ */
|
|
4360
|
-
/* @__PURE__ */
|
|
4361
|
-
/* @__PURE__ */
|
|
4362
|
-
userInfo.can_view_audit && /* @__PURE__ */
|
|
4393
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Overview", value: "overview" }),
|
|
4394
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Keys", value: "keys" }),
|
|
4395
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Teams", value: "teams" }),
|
|
4396
|
+
/* @__PURE__ */ React13.createElement(Tab2, { label: "Models", value: "models" }),
|
|
4397
|
+
userInfo.can_view_audit && /* @__PURE__ */ React13.createElement(Tab2, { label: "Audit Log", value: "audit" })
|
|
4363
4398
|
);
|
|
4364
|
-
return /* @__PURE__ */
|
|
4399
|
+
return /* @__PURE__ */ React13.createElement(Box12, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React13.createElement(
|
|
4365
4400
|
DashboardHeader,
|
|
4366
4401
|
{
|
|
4367
4402
|
userInfo,
|
|
@@ -4371,7 +4406,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4371
4406
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4372
4407
|
tabs: pageTabs
|
|
4373
4408
|
}
|
|
4374
|
-
), activeTab === "overview" && /* @__PURE__ */
|
|
4409
|
+
), 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
4410
|
UsageStats,
|
|
4376
4411
|
{
|
|
4377
4412
|
usage: usage ?? null,
|
|
@@ -4382,7 +4417,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4382
4417
|
loading: usageLoading,
|
|
4383
4418
|
userInfo
|
|
4384
4419
|
}
|
|
4385
|
-
)), /* @__PURE__ */
|
|
4420
|
+
)), /* @__PURE__ */ React13.createElement(Grid2, { item: true, xs: 12, lg: 4 }, /* @__PURE__ */ React13.createElement(LiteLLMBudgetWidget, { compact: true, collapsible: true }))), activeTab === "keys" && /* @__PURE__ */ React13.createElement(
|
|
4386
4421
|
KeysTable,
|
|
4387
4422
|
{
|
|
4388
4423
|
keys: keys ?? [],
|
|
@@ -4399,7 +4434,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4399
4434
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
4400
4435
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
4401
4436
|
const visibleTeams = Array.from(teamsById.values());
|
|
4402
|
-
return /* @__PURE__ */
|
|
4437
|
+
return /* @__PURE__ */ React13.createElement(
|
|
4403
4438
|
TeamUsage,
|
|
4404
4439
|
{
|
|
4405
4440
|
teams: visibleTeams,
|
|
@@ -4415,14 +4450,14 @@ var init_LiteLLMPage = __esm({
|
|
|
4415
4450
|
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
4416
4451
|
}
|
|
4417
4452
|
);
|
|
4418
|
-
})(), activeTab === "models" && /* @__PURE__ */
|
|
4453
|
+
})(), activeTab === "models" && /* @__PURE__ */ React13.createElement(
|
|
4419
4454
|
ModelsTable,
|
|
4420
4455
|
{
|
|
4421
4456
|
allModels: allModels ?? [],
|
|
4422
4457
|
teams: teams ?? [],
|
|
4423
4458
|
loading: modelsLoading
|
|
4424
4459
|
}
|
|
4425
|
-
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */
|
|
4460
|
+
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React13.createElement(AuditLog, { api }), /* @__PURE__ */ React13.createElement(
|
|
4426
4461
|
KeyFormDialog,
|
|
4427
4462
|
{
|
|
4428
4463
|
open: keyFormOpen,
|
|
@@ -4442,7 +4477,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4442
4477
|
onResetKeySpend: handleResetKeySpend,
|
|
4443
4478
|
onGetConfig: () => api.getConfig()
|
|
4444
4479
|
}
|
|
4445
|
-
), /* @__PURE__ */
|
|
4480
|
+
), /* @__PURE__ */ React13.createElement(
|
|
4446
4481
|
ManageTeamDialog,
|
|
4447
4482
|
{
|
|
4448
4483
|
open: !!manageTeam,
|
|
@@ -4504,7 +4539,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4504
4539
|
refreshTeams();
|
|
4505
4540
|
}
|
|
4506
4541
|
}
|
|
4507
|
-
), /* @__PURE__ */
|
|
4542
|
+
), /* @__PURE__ */ React13.createElement(
|
|
4508
4543
|
Snackbar2,
|
|
4509
4544
|
{
|
|
4510
4545
|
open: !!snackbar,
|
|
@@ -4512,7 +4547,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4512
4547
|
onClose: () => setSnackbar(null),
|
|
4513
4548
|
anchorOrigin: { vertical: "bottom", horizontal: "right" }
|
|
4514
4549
|
},
|
|
4515
|
-
snackbar ? /* @__PURE__ */
|
|
4550
|
+
snackbar ? /* @__PURE__ */ React13.createElement(Alert6, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
|
|
4516
4551
|
));
|
|
4517
4552
|
};
|
|
4518
4553
|
}
|
|
@@ -4520,7 +4555,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4520
4555
|
|
|
4521
4556
|
// src/plugin.tsx
|
|
4522
4557
|
init_api();
|
|
4523
|
-
import
|
|
4558
|
+
import React14 from "react";
|
|
4524
4559
|
import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
|
|
4525
4560
|
import {
|
|
4526
4561
|
createFrontendPlugin,
|
|
@@ -4539,10 +4574,10 @@ var liteLlmPage = PageBlueprint.make({
|
|
|
4539
4574
|
params: {
|
|
4540
4575
|
path: "/litellm",
|
|
4541
4576
|
title: "LiteLLM",
|
|
4542
|
-
icon: /* @__PURE__ */
|
|
4577
|
+
icon: /* @__PURE__ */ React14.createElement(TrendingUpIcon, null),
|
|
4543
4578
|
loader: async () => {
|
|
4544
4579
|
const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
|
|
4545
|
-
return /* @__PURE__ */
|
|
4580
|
+
return /* @__PURE__ */ React14.createElement(LiteLLMPage2, null);
|
|
4546
4581
|
}
|
|
4547
4582
|
}
|
|
4548
4583
|
});
|
|
@@ -4561,7 +4596,7 @@ init_TeamUsage();
|
|
|
4561
4596
|
// src/components/LiteLLMHomeWidget.tsx
|
|
4562
4597
|
init_api();
|
|
4563
4598
|
init_format();
|
|
4564
|
-
import
|
|
4599
|
+
import React15, { useState as useState10, useEffect as useEffect5 } from "react";
|
|
4565
4600
|
import Paper9 from "@mui/material/Paper";
|
|
4566
4601
|
import Box13 from "@mui/material/Box";
|
|
4567
4602
|
import Typography13 from "@mui/material/Typography";
|
|
@@ -4585,7 +4620,7 @@ function presetToDateRange(preset) {
|
|
|
4585
4620
|
}
|
|
4586
4621
|
return { start, end };
|
|
4587
4622
|
}
|
|
4588
|
-
var Kpi = ({ label, value }) => /* @__PURE__ */
|
|
4623
|
+
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
4624
|
var LiteLLMHomeWidget = ({
|
|
4590
4625
|
defaultPeriod = "7d",
|
|
4591
4626
|
title = "LiteLLM Usage"
|
|
@@ -4634,17 +4669,17 @@ var LiteLLMHomeWidget = ({
|
|
|
4634
4669
|
spend: d.spend
|
|
4635
4670
|
}));
|
|
4636
4671
|
const hasSparkline = dailyData.length > 0;
|
|
4637
|
-
return /* @__PURE__ */
|
|
4672
|
+
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
4673
|
Select2,
|
|
4639
4674
|
{
|
|
4640
4675
|
value: period,
|
|
4641
4676
|
onChange: (e) => setPeriod(e.target.value),
|
|
4642
4677
|
displayEmpty: true
|
|
4643
4678
|
},
|
|
4644
|
-
/* @__PURE__ */
|
|
4645
|
-
/* @__PURE__ */
|
|
4646
|
-
/* @__PURE__ */
|
|
4647
|
-
))), loading && /* @__PURE__ */
|
|
4679
|
+
/* @__PURE__ */ React15.createElement(MenuItem6, { value: "today" }, "Today"),
|
|
4680
|
+
/* @__PURE__ */ React15.createElement(MenuItem6, { value: "7d" }, "7d"),
|
|
4681
|
+
/* @__PURE__ */ React15.createElement(MenuItem6, { value: "30d" }, "30d")
|
|
4682
|
+
))), 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
4683
|
Area3,
|
|
4649
4684
|
{
|
|
4650
4685
|
type: "monotone",
|
|
@@ -4665,9 +4700,10 @@ init_LiteLLMBudgetWidget();
|
|
|
4665
4700
|
init_api();
|
|
4666
4701
|
init_format();
|
|
4667
4702
|
init_ui();
|
|
4703
|
+
init_GenerateKeyButton();
|
|
4668
4704
|
init_BudgetLimitList();
|
|
4669
4705
|
init_budget();
|
|
4670
|
-
import
|
|
4706
|
+
import React16, { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo10, useState as useState11 } from "react";
|
|
4671
4707
|
import Paper10 from "@mui/material/Paper";
|
|
4672
4708
|
import Box14 from "@mui/material/Box";
|
|
4673
4709
|
import Button6 from "@mui/material/Button";
|
|
@@ -4676,8 +4712,9 @@ import CircularProgress8 from "@mui/material/CircularProgress";
|
|
|
4676
4712
|
import Alert8 from "@mui/material/Alert";
|
|
4677
4713
|
import Collapse4 from "@mui/material/Collapse";
|
|
4678
4714
|
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
4715
|
+
import { AreaChart as AreaChart4, Area as Area4, ResponsiveContainer as ResponsiveContainer4, Tooltip as Tooltip4 } from "recharts";
|
|
4679
4716
|
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
4680
|
-
import { Link as
|
|
4717
|
+
import { Link as Link3 } from "@backstage/core-components";
|
|
4681
4718
|
var MODULE_PATH = "/litellm";
|
|
4682
4719
|
var LEVEL = {
|
|
4683
4720
|
key: { name: "Key", none: "No key budget" },
|
|
@@ -4686,7 +4723,7 @@ var LEVEL = {
|
|
|
4686
4723
|
};
|
|
4687
4724
|
var DEFAULT_CTAS = ["new-key", "module", "all-limits"];
|
|
4688
4725
|
var CTA_LABELS = {
|
|
4689
|
-
"new-key": "New
|
|
4726
|
+
"new-key": "Generate New Key",
|
|
4690
4727
|
module: "Open module",
|
|
4691
4728
|
"all-limits": "All limits"
|
|
4692
4729
|
};
|
|
@@ -4712,7 +4749,7 @@ var LevelGauge = ({
|
|
|
4712
4749
|
const limit = gauge.limit;
|
|
4713
4750
|
const tone = limit ? budgetTone2(limit.pct) : "neutral";
|
|
4714
4751
|
const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
|
|
4715
|
-
return /* @__PURE__ */
|
|
4752
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4716
4753
|
Box14,
|
|
4717
4754
|
{
|
|
4718
4755
|
sx: {
|
|
@@ -4723,7 +4760,7 @@ var LevelGauge = ({
|
|
|
4723
4760
|
flex: 1
|
|
4724
4761
|
}
|
|
4725
4762
|
},
|
|
4726
|
-
/* @__PURE__ */
|
|
4763
|
+
/* @__PURE__ */ React16.createElement(
|
|
4727
4764
|
Gauge,
|
|
4728
4765
|
{
|
|
4729
4766
|
value: limit?.pct ?? 0,
|
|
@@ -4732,7 +4769,7 @@ var LevelGauge = ({
|
|
|
4732
4769
|
label: limit ? `${Math.round(limit.pct)}%` : "\u2014"
|
|
4733
4770
|
}
|
|
4734
4771
|
),
|
|
4735
|
-
/* @__PURE__ */
|
|
4772
|
+
/* @__PURE__ */ React16.createElement(
|
|
4736
4773
|
Typography14,
|
|
4737
4774
|
{
|
|
4738
4775
|
sx: (theme) => ({
|
|
@@ -4746,7 +4783,7 @@ var LevelGauge = ({
|
|
|
4746
4783
|
},
|
|
4747
4784
|
name
|
|
4748
4785
|
),
|
|
4749
|
-
limit ? /* @__PURE__ */
|
|
4786
|
+
limit ? /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(
|
|
4750
4787
|
Typography14,
|
|
4751
4788
|
{
|
|
4752
4789
|
variant: "caption",
|
|
@@ -4762,7 +4799,7 @@ var LevelGauge = ({
|
|
|
4762
4799
|
}
|
|
4763
4800
|
},
|
|
4764
4801
|
limit.label
|
|
4765
|
-
), /* @__PURE__ */
|
|
4802
|
+
), /* @__PURE__ */ React16.createElement(
|
|
4766
4803
|
Typography14,
|
|
4767
4804
|
{
|
|
4768
4805
|
variant: "caption",
|
|
@@ -4774,10 +4811,10 @@ var LevelGauge = ({
|
|
|
4774
4811
|
}
|
|
4775
4812
|
},
|
|
4776
4813
|
spendCaption(limit)
|
|
4777
|
-
), extra > 0 ? /* @__PURE__ */
|
|
4814
|
+
), extra > 0 ? /* @__PURE__ */ React16.createElement(
|
|
4778
4815
|
Typography14,
|
|
4779
4816
|
{
|
|
4780
|
-
component:
|
|
4817
|
+
component: Link3,
|
|
4781
4818
|
to: keysHref,
|
|
4782
4819
|
variant: "caption",
|
|
4783
4820
|
sx: {
|
|
@@ -4790,7 +4827,7 @@ var LevelGauge = ({
|
|
|
4790
4827
|
"+",
|
|
4791
4828
|
extra,
|
|
4792
4829
|
" more"
|
|
4793
|
-
) : /* @__PURE__ */
|
|
4830
|
+
) : /* @__PURE__ */ React16.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, resetLabel(limit))) : /* @__PURE__ */ React16.createElement(
|
|
4794
4831
|
Typography14,
|
|
4795
4832
|
{
|
|
4796
4833
|
variant: "caption",
|
|
@@ -4801,6 +4838,125 @@ var LevelGauge = ({
|
|
|
4801
4838
|
)
|
|
4802
4839
|
);
|
|
4803
4840
|
};
|
|
4841
|
+
function monthToDateRange(now = /* @__PURE__ */ new Date()) {
|
|
4842
|
+
const toDay = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
|
|
4843
|
+
d.getDate()
|
|
4844
|
+
).padStart(2, "0")}`;
|
|
4845
|
+
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
4846
|
+
return { startDate: toDay(start), endDate: toDay(now) };
|
|
4847
|
+
}
|
|
4848
|
+
var UsageMiniChart = ({ data, totalTokens, loading, height }) => {
|
|
4849
|
+
const renderPlot = () => {
|
|
4850
|
+
if (loading) {
|
|
4851
|
+
return /* @__PURE__ */ React16.createElement(Box14, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%" }, /* @__PURE__ */ React16.createElement(CircularProgress8, { size: 20 }));
|
|
4852
|
+
}
|
|
4853
|
+
if (data.length === 0) {
|
|
4854
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4855
|
+
Box14,
|
|
4856
|
+
{
|
|
4857
|
+
display: "flex",
|
|
4858
|
+
justifyContent: "center",
|
|
4859
|
+
alignItems: "center",
|
|
4860
|
+
height: "100%",
|
|
4861
|
+
sx: (theme) => ({ color: theme.palette.text.secondary, fontSize: 18 })
|
|
4862
|
+
},
|
|
4863
|
+
"\u2014"
|
|
4864
|
+
);
|
|
4865
|
+
}
|
|
4866
|
+
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(
|
|
4867
|
+
Area4,
|
|
4868
|
+
{
|
|
4869
|
+
type: "monotone",
|
|
4870
|
+
dataKey: "input",
|
|
4871
|
+
name: "Input",
|
|
4872
|
+
stackId: "tok",
|
|
4873
|
+
stroke: SERIES.input,
|
|
4874
|
+
strokeWidth: 1.5,
|
|
4875
|
+
fill: SERIES.input,
|
|
4876
|
+
fillOpacity: 0.25,
|
|
4877
|
+
dot: false,
|
|
4878
|
+
isAnimationActive: false
|
|
4879
|
+
}
|
|
4880
|
+
), /* @__PURE__ */ React16.createElement(
|
|
4881
|
+
Area4,
|
|
4882
|
+
{
|
|
4883
|
+
type: "monotone",
|
|
4884
|
+
dataKey: "output",
|
|
4885
|
+
name: "Output",
|
|
4886
|
+
stackId: "tok",
|
|
4887
|
+
stroke: SERIES.output,
|
|
4888
|
+
strokeWidth: 1.5,
|
|
4889
|
+
fill: SERIES.output,
|
|
4890
|
+
fillOpacity: 0.25,
|
|
4891
|
+
dot: false,
|
|
4892
|
+
isAnimationActive: false
|
|
4893
|
+
}
|
|
4894
|
+
)));
|
|
4895
|
+
};
|
|
4896
|
+
const renderCaption = () => {
|
|
4897
|
+
if (loading) {
|
|
4898
|
+
return /* @__PURE__ */ React16.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { mt: 0.25, fontSize: 11 } }, "\u2026");
|
|
4899
|
+
}
|
|
4900
|
+
if (data.length === 0) {
|
|
4901
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4902
|
+
Typography14,
|
|
4903
|
+
{
|
|
4904
|
+
variant: "caption",
|
|
4905
|
+
color: "text.secondary",
|
|
4906
|
+
sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" }
|
|
4907
|
+
},
|
|
4908
|
+
"No usage this month"
|
|
4909
|
+
);
|
|
4910
|
+
}
|
|
4911
|
+
return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(
|
|
4912
|
+
Typography14,
|
|
4913
|
+
{
|
|
4914
|
+
variant: "caption",
|
|
4915
|
+
title: `${fmtInt(totalTokens)} tokens month to date`,
|
|
4916
|
+
sx: {
|
|
4917
|
+
mt: 0.25,
|
|
4918
|
+
fontSize: 11,
|
|
4919
|
+
fontWeight: 600,
|
|
4920
|
+
maxWidth: "100%",
|
|
4921
|
+
overflow: "hidden",
|
|
4922
|
+
textOverflow: "ellipsis",
|
|
4923
|
+
whiteSpace: "nowrap",
|
|
4924
|
+
fontVariantNumeric: "tabular-nums"
|
|
4925
|
+
}
|
|
4926
|
+
},
|
|
4927
|
+
fmtCompact(totalTokens),
|
|
4928
|
+
" tokens"
|
|
4929
|
+
), /* @__PURE__ */ React16.createElement(Typography14, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 } }, "month to date"));
|
|
4930
|
+
};
|
|
4931
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4932
|
+
Box14,
|
|
4933
|
+
{
|
|
4934
|
+
sx: {
|
|
4935
|
+
display: "flex",
|
|
4936
|
+
flexDirection: "column",
|
|
4937
|
+
alignItems: "center",
|
|
4938
|
+
minWidth: 0,
|
|
4939
|
+
flex: 1
|
|
4940
|
+
}
|
|
4941
|
+
},
|
|
4942
|
+
/* @__PURE__ */ React16.createElement(Box14, { sx: { height, width: "100%" } }, renderPlot()),
|
|
4943
|
+
/* @__PURE__ */ React16.createElement(
|
|
4944
|
+
Typography14,
|
|
4945
|
+
{
|
|
4946
|
+
sx: (theme) => ({
|
|
4947
|
+
mt: 0.75,
|
|
4948
|
+
fontSize: 10.5,
|
|
4949
|
+
fontWeight: 700,
|
|
4950
|
+
letterSpacing: "0.08em",
|
|
4951
|
+
textTransform: "uppercase",
|
|
4952
|
+
color: theme.palette.text.secondary
|
|
4953
|
+
})
|
|
4954
|
+
},
|
|
4955
|
+
"Usage"
|
|
4956
|
+
),
|
|
4957
|
+
renderCaption()
|
|
4958
|
+
);
|
|
4959
|
+
};
|
|
4804
4960
|
var LiteLLMBudgetGauges = ({
|
|
4805
4961
|
title = "Budget",
|
|
4806
4962
|
size = 72,
|
|
@@ -4820,6 +4976,9 @@ var LiteLLMBudgetGauges = ({
|
|
|
4820
4976
|
const [user, setUser] = useState11(null);
|
|
4821
4977
|
const [teams, setTeams] = useState11([]);
|
|
4822
4978
|
const [keys, setKeys] = useState11([]);
|
|
4979
|
+
const [usageLoading, setUsageLoading] = useState11(true);
|
|
4980
|
+
const [dailyTokens, setDailyTokens] = useState11([]);
|
|
4981
|
+
const [mtdTokens, setMtdTokens] = useState11(0);
|
|
4823
4982
|
const isControlled = expanded !== void 0;
|
|
4824
4983
|
const [uncontrolledExpanded, setUncontrolledExpanded] = useState11(defaultExpanded);
|
|
4825
4984
|
const isExpanded = isControlled ? expanded : uncontrolledExpanded;
|
|
@@ -4832,23 +4991,43 @@ var LiteLLMBudgetGauges = ({
|
|
|
4832
4991
|
useEffect6(() => {
|
|
4833
4992
|
let cancelled = false;
|
|
4834
4993
|
setLoading(true);
|
|
4994
|
+
setUsageLoading(true);
|
|
4835
4995
|
setError(null);
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4996
|
+
const { startDate, endDate } = monthToDateRange();
|
|
4997
|
+
Promise.allSettled([
|
|
4998
|
+
api.getUserInfo(),
|
|
4999
|
+
api.getTeams(),
|
|
5000
|
+
api.listKeys(),
|
|
5001
|
+
api.getUsage(startDate, endDate)
|
|
5002
|
+
]).then(([userResult, teamsResult, keysResult, usageResult]) => {
|
|
5003
|
+
if (cancelled) return;
|
|
5004
|
+
if (userResult.status === "fulfilled") {
|
|
5005
|
+
setUser(userResult.value);
|
|
5006
|
+
} else {
|
|
5007
|
+
setUser(null);
|
|
5008
|
+
setError(
|
|
5009
|
+
userResult.reason?.message ?? "Failed to load your LiteLLM profile"
|
|
5010
|
+
);
|
|
4850
5011
|
}
|
|
4851
|
-
|
|
5012
|
+
setTeams(teamsResult.status === "fulfilled" ? teamsResult.value : []);
|
|
5013
|
+
setKeys(keysResult.status === "fulfilled" ? keysResult.value : []);
|
|
5014
|
+
setLoading(false);
|
|
5015
|
+
if (usageResult.status === "fulfilled") {
|
|
5016
|
+
const daily = usageResult.value.daily_usage ?? [];
|
|
5017
|
+
setDailyTokens(
|
|
5018
|
+
daily.map((d) => ({
|
|
5019
|
+
date: d.date,
|
|
5020
|
+
input: d.prompt_tokens,
|
|
5021
|
+
output: d.completion_tokens
|
|
5022
|
+
}))
|
|
5023
|
+
);
|
|
5024
|
+
setMtdTokens(usageResult.value.total_tokens ?? 0);
|
|
5025
|
+
} else {
|
|
5026
|
+
setDailyTokens([]);
|
|
5027
|
+
setMtdTokens(0);
|
|
5028
|
+
}
|
|
5029
|
+
setUsageLoading(false);
|
|
5030
|
+
});
|
|
4852
5031
|
return () => {
|
|
4853
5032
|
cancelled = true;
|
|
4854
5033
|
};
|
|
@@ -4877,28 +5056,18 @@ var LiteLLMBudgetGauges = ({
|
|
|
4877
5056
|
const key = `${cta.kind}-${index}`;
|
|
4878
5057
|
switch (cta.kind) {
|
|
4879
5058
|
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
|
-
);
|
|
5059
|
+
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
5060
|
case "module":
|
|
4892
|
-
return /* @__PURE__ */
|
|
5061
|
+
return /* @__PURE__ */ React16.createElement(Button6, { key, size: "small", variant: "outlined", component: Link3, to: moduleHref }, label);
|
|
4893
5062
|
case "all-limits":
|
|
4894
|
-
return /* @__PURE__ */
|
|
5063
|
+
return /* @__PURE__ */ React16.createElement(
|
|
4895
5064
|
Button6,
|
|
4896
5065
|
{
|
|
4897
5066
|
key,
|
|
4898
5067
|
size: "small",
|
|
4899
5068
|
variant: "text",
|
|
4900
5069
|
onClick: toggleExpanded,
|
|
4901
|
-
endIcon: /* @__PURE__ */
|
|
5070
|
+
endIcon: /* @__PURE__ */ React16.createElement(
|
|
4902
5071
|
ExpandMore4,
|
|
4903
5072
|
{
|
|
4904
5073
|
sx: {
|
|
@@ -4916,14 +5085,22 @@ var LiteLLMBudgetGauges = ({
|
|
|
4916
5085
|
}
|
|
4917
5086
|
};
|
|
4918
5087
|
const hasFooter = resolvedCtas.length > 0 || !!action;
|
|
4919
|
-
return /* @__PURE__ */
|
|
5088
|
+
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
5089
|
StatusPill,
|
|
4921
5090
|
{
|
|
4922
5091
|
label: summaryText,
|
|
4923
5092
|
tone: budgetTone2(headline.closest?.pct ?? 0),
|
|
4924
5093
|
dot: headline.count > 0
|
|
4925
5094
|
}
|
|
4926
|
-
)), loading && /* @__PURE__ */
|
|
5095
|
+
)), 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(
|
|
5096
|
+
UsageMiniChart,
|
|
5097
|
+
{
|
|
5098
|
+
data: dailyTokens,
|
|
5099
|
+
totalTokens: mtdTokens,
|
|
5100
|
+
loading: usageLoading,
|
|
5101
|
+
height: size
|
|
5102
|
+
}
|
|
5103
|
+
)), /* @__PURE__ */ React16.createElement(Collapse4, { in: isExpanded, unmountOnExit: true }, /* @__PURE__ */ React16.createElement(
|
|
4927
5104
|
Box14,
|
|
4928
5105
|
{
|
|
4929
5106
|
sx: {
|
|
@@ -4934,7 +5111,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4934
5111
|
overflowY: "auto"
|
|
4935
5112
|
}
|
|
4936
5113
|
},
|
|
4937
|
-
/* @__PURE__ */
|
|
5114
|
+
/* @__PURE__ */ React16.createElement(LimitListPanel, null, /* @__PURE__ */ React16.createElement(
|
|
4938
5115
|
BudgetLimitList,
|
|
4939
5116
|
{
|
|
4940
5117
|
limits: expandedLimits,
|
|
@@ -4942,7 +5119,7 @@ var LiteLLMBudgetGauges = ({
|
|
|
4942
5119
|
keysHref: keysTabHref
|
|
4943
5120
|
}
|
|
4944
5121
|
))
|
|
4945
|
-
))), hasFooter && /* @__PURE__ */
|
|
5122
|
+
))), hasFooter && /* @__PURE__ */ React16.createElement(
|
|
4946
5123
|
Box14,
|
|
4947
5124
|
{
|
|
4948
5125
|
sx: (theme) => ({
|
|
@@ -4962,11 +5139,12 @@ var LiteLLMBudgetGauges = ({
|
|
|
4962
5139
|
|
|
4963
5140
|
// src/index.ts
|
|
4964
5141
|
init_KeyFormDialog();
|
|
5142
|
+
init_GenerateKeyButton();
|
|
4965
5143
|
|
|
4966
5144
|
// src/components/GenerateKeyDialog.tsx
|
|
4967
5145
|
init_KeyFormDialog();
|
|
4968
|
-
import
|
|
4969
|
-
var GenerateKeyDialog = (props) => /* @__PURE__ */
|
|
5146
|
+
import React17 from "react";
|
|
5147
|
+
var GenerateKeyDialog = (props) => /* @__PURE__ */ React17.createElement(
|
|
4970
5148
|
KeyFormDialog,
|
|
4971
5149
|
{
|
|
4972
5150
|
open: props.open,
|
|
@@ -4990,6 +5168,7 @@ init_api();
|
|
|
4990
5168
|
init_permissions();
|
|
4991
5169
|
export {
|
|
4992
5170
|
DashboardHeader,
|
|
5171
|
+
GenerateKeyButton,
|
|
4993
5172
|
GenerateKeyDialog,
|
|
4994
5173
|
KeyFormDialog,
|
|
4995
5174
|
KeysTable,
|