@acarmisc/backstage-plugin-litellm 0.25.0 → 0.26.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/README.md +3 -2
- package/dist/SearchInput.d.ts +1 -0
- package/dist/components/GenerateKeyDialog.d.ts +8 -7
- package/dist/components/KeyFormDialog.d.ts +42 -0
- package/dist/components/KeysTable.d.ts +4 -5
- package/dist/components/SearchInput.d.ts +9 -0
- package/dist/index.cjs.js +210 -217
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +209 -216
- package/dist/index.esm.js.map +4 -4
- package/dist/types.d.ts +4 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1003,7 +1003,6 @@ import DialogContent from "@mui/material/DialogContent";
|
|
|
1003
1003
|
import DialogActions from "@mui/material/DialogActions";
|
|
1004
1004
|
import TextField from "@mui/material/TextField";
|
|
1005
1005
|
import CircularProgress from "@mui/material/CircularProgress";
|
|
1006
|
-
import Autocomplete from "@mui/material/Autocomplete";
|
|
1007
1006
|
import Skeleton2 from "@mui/material/Skeleton";
|
|
1008
1007
|
import InputAdornment from "@mui/material/InputAdornment";
|
|
1009
1008
|
import { alpha as alpha3 } from "@mui/material/styles";
|
|
@@ -1052,26 +1051,6 @@ function BudgetCell({ spend, maxBudget }) {
|
|
|
1052
1051
|
const pct = Math.min(100, spend / maxBudget * 100);
|
|
1053
1052
|
return /* @__PURE__ */ React3.createElement(Box3, { minWidth: 104 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums", mb: 0.5 } }, "$", spend.toFixed(2), /* @__PURE__ */ React3.createElement(Typography3, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 0.5 } }, "/ $", maxBudget)), /* @__PURE__ */ React3.createElement(Meter, { value: pct, tone: budgetTone(pct), height: 4 }));
|
|
1054
1053
|
}
|
|
1055
|
-
function fmtCost(perToken) {
|
|
1056
|
-
if (!perToken) return null;
|
|
1057
|
-
const per1k = perToken * 1e3;
|
|
1058
|
-
return per1k < 0.01 ? `$${(perToken * 1e6).toFixed(2)}/M` : `$${per1k.toFixed(3)}/1K`;
|
|
1059
|
-
}
|
|
1060
|
-
function formatContextWindow(maxInput, maxOutput) {
|
|
1061
|
-
if (!maxInput && !maxOutput) return null;
|
|
1062
|
-
const fmt = (n) => {
|
|
1063
|
-
if (!n) return null;
|
|
1064
|
-
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
1065
|
-
if (n >= 1e3) return `${Math.round(n / 1e3)}K`;
|
|
1066
|
-
return String(n);
|
|
1067
|
-
};
|
|
1068
|
-
const inPart = fmt(maxInput);
|
|
1069
|
-
const outPart = fmt(maxOutput);
|
|
1070
|
-
if (inPart && outPart) return `ctx ${inPart} in / ${outPart} out`;
|
|
1071
|
-
if (inPart) return `ctx ${inPart}`;
|
|
1072
|
-
if (outPart) return `ctx ${outPart} out`;
|
|
1073
|
-
return null;
|
|
1074
|
-
}
|
|
1075
1054
|
function compareKeys(a, b, sortKey) {
|
|
1076
1055
|
switch (sortKey) {
|
|
1077
1056
|
case "alias":
|
|
@@ -1090,16 +1069,12 @@ function compareKeys(a, b, sortKey) {
|
|
|
1090
1069
|
return 0;
|
|
1091
1070
|
}
|
|
1092
1071
|
}
|
|
1093
|
-
var
|
|
1072
|
+
var shortKeyId, formatDate, KeysTable;
|
|
1094
1073
|
var init_KeysTable = __esm({
|
|
1095
1074
|
"src/components/KeysTable.tsx"() {
|
|
1096
1075
|
"use strict";
|
|
1097
1076
|
init_api();
|
|
1098
1077
|
init_ui();
|
|
1099
|
-
maskKey = (key) => {
|
|
1100
|
-
if (key.length <= 8) return "***";
|
|
1101
|
-
return `${key.slice(0, 4)}...${key.slice(-4)}`;
|
|
1102
|
-
};
|
|
1103
1078
|
shortKeyId = (token) => {
|
|
1104
1079
|
if (!token) return "-";
|
|
1105
1080
|
if (token.length <= 16) return token;
|
|
@@ -1112,33 +1087,19 @@ var init_KeysTable = __esm({
|
|
|
1112
1087
|
return dateStr;
|
|
1113
1088
|
}
|
|
1114
1089
|
};
|
|
1115
|
-
keyToEditForm = (k) => ({
|
|
1116
|
-
key_alias: k.key_alias ?? "",
|
|
1117
|
-
models: k.models ?? [],
|
|
1118
|
-
max_budget: k.max_budget,
|
|
1119
|
-
tpm_limit: k.tpm_limit,
|
|
1120
|
-
rpm_limit: k.rpm_limit
|
|
1121
|
-
});
|
|
1122
1090
|
KeysTable = ({
|
|
1123
1091
|
keys,
|
|
1124
|
-
models,
|
|
1125
1092
|
loading,
|
|
1126
1093
|
onGenerateKeyClick,
|
|
1127
|
-
|
|
1094
|
+
onEditKey,
|
|
1128
1095
|
onBlockKey,
|
|
1129
1096
|
onUnblockKey,
|
|
1130
|
-
onResetKeySpend,
|
|
1131
1097
|
onDeleteKey,
|
|
1132
1098
|
onPruneExpiredKeys
|
|
1133
1099
|
}) => {
|
|
1134
|
-
const [editingKey, setEditingKey] = useState(null);
|
|
1135
|
-
const [editForm, setEditForm] = useState({});
|
|
1136
|
-
const [editSubmitting, setEditSubmitting] = useState(false);
|
|
1137
1100
|
const [blockingKeyId, setBlockingKeyId] = useState(null);
|
|
1138
1101
|
const [deleteConfirmId, setDeleteConfirmId] = useState(null);
|
|
1139
1102
|
const [deleteSubmitting, setDeleteSubmitting] = useState(false);
|
|
1140
|
-
const [resetSpendConfirm, setResetSpendConfirm] = useState(false);
|
|
1141
|
-
const [resetSpendSubmitting, setResetSpendSubmitting] = useState(false);
|
|
1142
1103
|
const [filterText, setFilterText] = useState("");
|
|
1143
1104
|
const [sortKey, setSortKey] = useState("created");
|
|
1144
1105
|
const [sortDirection, setSortDirection] = useState("desc");
|
|
@@ -1163,27 +1124,8 @@ var init_KeysTable = __esm({
|
|
|
1163
1124
|
return sortDirection === "asc" ? comparison : -comparison;
|
|
1164
1125
|
});
|
|
1165
1126
|
}, [keys, filterText, sortKey, sortDirection]);
|
|
1166
|
-
const editSelectedModels = models.filter((m) => (editForm.models || []).includes(m.model_name));
|
|
1167
1127
|
const handleOpenEdit = (k) => {
|
|
1168
|
-
|
|
1169
|
-
setEditForm(keyToEditForm(k));
|
|
1170
|
-
};
|
|
1171
|
-
const handleCloseEdit = () => {
|
|
1172
|
-
setEditingKey(null);
|
|
1173
|
-
setEditForm({});
|
|
1174
|
-
setResetSpendConfirm(false);
|
|
1175
|
-
};
|
|
1176
|
-
const handleUpdate = async () => {
|
|
1177
|
-
if (!editingKey) return;
|
|
1178
|
-
setEditSubmitting(true);
|
|
1179
|
-
try {
|
|
1180
|
-
await onUpdateKey(editingKey.token ?? editingKey.key, editForm);
|
|
1181
|
-
handleCloseEdit();
|
|
1182
|
-
} catch (error) {
|
|
1183
|
-
console.error("Failed to update key:", error);
|
|
1184
|
-
} finally {
|
|
1185
|
-
setEditSubmitting(false);
|
|
1186
|
-
}
|
|
1128
|
+
onEditKey(k);
|
|
1187
1129
|
};
|
|
1188
1130
|
const handleToggleBlock = async (key) => {
|
|
1189
1131
|
const keyId = key.token ?? key.key;
|
|
@@ -1198,17 +1140,6 @@ var init_KeysTable = __esm({
|
|
|
1198
1140
|
setBlockingKeyId(null);
|
|
1199
1141
|
}
|
|
1200
1142
|
};
|
|
1201
|
-
const handleResetSpend = async () => {
|
|
1202
|
-
if (!editingKey) return;
|
|
1203
|
-
setResetSpendSubmitting(true);
|
|
1204
|
-
try {
|
|
1205
|
-
await onResetKeySpend(editingKey.token ?? editingKey.key);
|
|
1206
|
-
setResetSpendConfirm(false);
|
|
1207
|
-
handleCloseEdit();
|
|
1208
|
-
} finally {
|
|
1209
|
-
setResetSpendSubmitting(false);
|
|
1210
|
-
}
|
|
1211
|
-
};
|
|
1212
1143
|
const handleConfirmDelete = async () => {
|
|
1213
1144
|
if (!deleteConfirmId) return;
|
|
1214
1145
|
setDeleteSubmitting(true);
|
|
@@ -1239,12 +1170,6 @@ var init_KeysTable = __esm({
|
|
|
1239
1170
|
setPruneConfirmCount(null);
|
|
1240
1171
|
}
|
|
1241
1172
|
};
|
|
1242
|
-
const modelOption = (m) => {
|
|
1243
|
-
const inCost = fmtCost(m.input_cost_per_token);
|
|
1244
|
-
const outCost = fmtCost(m.output_cost_per_token);
|
|
1245
|
-
const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
|
|
1246
|
-
return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
|
|
1247
|
-
};
|
|
1248
1173
|
const renderTableBody = () => {
|
|
1249
1174
|
if (loading) {
|
|
1250
1175
|
return /* @__PURE__ */ React3.createElement(TableRow, null, /* @__PURE__ */ React3.createElement(TableCell, { colSpan: 8, sx: { py: 2 } }, /* @__PURE__ */ React3.createElement(Skeleton2, { variant: "rounded", height: 140 })));
|
|
@@ -1416,75 +1341,7 @@ var init_KeysTable = __esm({
|
|
|
1416
1341
|
))
|
|
1417
1342
|
},
|
|
1418
1343
|
/* @__PURE__ */ React3.createElement(TableContainer, null, /* @__PURE__ */ React3.createElement(Table, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React3.createElement(TableHead, null, /* @__PURE__ */ React3.createElement(TableRow, null, /* @__PURE__ */ React3.createElement(TableCell, { sortDirection: sortKey === "alias" ? sortDirection : false }, /* @__PURE__ */ React3.createElement(TableSortLabel, { active: sortKey === "alias", direction: sortKey === "alias" ? sortDirection : "asc", onClick: () => handleSort("alias") }, "Alias")), /* @__PURE__ */ React3.createElement(TableCell, null, "Key ID"), /* @__PURE__ */ React3.createElement(TableCell, { sortDirection: sortKey === "created" ? sortDirection : false }, /* @__PURE__ */ React3.createElement(TableSortLabel, { active: sortKey === "created", direction: sortKey === "created" ? sortDirection : "asc", onClick: () => handleSort("created") }, "Created")), /* @__PURE__ */ React3.createElement(TableCell, { sortDirection: sortKey === "expires" ? sortDirection : false }, /* @__PURE__ */ React3.createElement(TableSortLabel, { active: sortKey === "expires", direction: sortKey === "expires" ? sortDirection : "asc", onClick: () => handleSort("expires") }, "Expires")), /* @__PURE__ */ React3.createElement(TableCell, { sortDirection: sortKey === "budget" ? sortDirection : false }, /* @__PURE__ */ React3.createElement(TableSortLabel, { active: sortKey === "budget", direction: sortKey === "budget" ? sortDirection : "asc", onClick: () => handleSort("budget") }, "Budget")), /* @__PURE__ */ React3.createElement(TableCell, { sortDirection: sortKey === "limits" ? sortDirection : false }, /* @__PURE__ */ React3.createElement(TableSortLabel, { active: sortKey === "limits", direction: sortKey === "limits" ? sortDirection : "asc", onClick: () => handleSort("limits") }, "TPM / RPM")), /* @__PURE__ */ React3.createElement(TableCell, { sortDirection: sortKey === "models" ? sortDirection : false }, /* @__PURE__ */ React3.createElement(TableSortLabel, { active: sortKey === "models", direction: sortKey === "models" ? sortDirection : "asc", onClick: () => handleSort("models") }, "Models")), /* @__PURE__ */ React3.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React3.createElement(TableBody, null, renderTableBody())))
|
|
1419
|
-
), /* @__PURE__ */ React3.createElement(Dialog, { open: !!
|
|
1420
|
-
TextField,
|
|
1421
|
-
{
|
|
1422
|
-
label: "Alias",
|
|
1423
|
-
value: editForm.key_alias || "",
|
|
1424
|
-
onChange: (e) => setEditForm({ ...editForm, key_alias: e.target.value }),
|
|
1425
|
-
fullWidth: true
|
|
1426
|
-
}
|
|
1427
|
-
), models.length > 0 && /* @__PURE__ */ React3.createElement(
|
|
1428
|
-
Autocomplete,
|
|
1429
|
-
{
|
|
1430
|
-
multiple: true,
|
|
1431
|
-
options: models,
|
|
1432
|
-
groupBy: (m) => m.mode || "other",
|
|
1433
|
-
getOptionLabel: (m) => m.model_name,
|
|
1434
|
-
value: editSelectedModels,
|
|
1435
|
-
onChange: (_e, selected) => setEditForm({ ...editForm, models: selected.map((m) => m.model_name) }),
|
|
1436
|
-
renderOption: (props, m) => /* @__PURE__ */ React3.createElement("li", { ...props }, modelOption(m)),
|
|
1437
|
-
renderInput: (params) => /* @__PURE__ */ React3.createElement(TextField, { ...params, label: "Models", fullWidth: true })
|
|
1438
|
-
}
|
|
1439
|
-
), /* @__PURE__ */ React3.createElement(
|
|
1440
|
-
TextField,
|
|
1441
|
-
{
|
|
1442
|
-
label: "Max Budget (USD)",
|
|
1443
|
-
type: "number",
|
|
1444
|
-
value: editForm.max_budget ?? "",
|
|
1445
|
-
onChange: (e) => setEditForm({ ...editForm, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
|
|
1446
|
-
fullWidth: true
|
|
1447
|
-
}
|
|
1448
|
-
), /* @__PURE__ */ React3.createElement(
|
|
1449
|
-
TextField,
|
|
1450
|
-
{
|
|
1451
|
-
label: "TPM Limit",
|
|
1452
|
-
type: "number",
|
|
1453
|
-
value: editForm.tpm_limit ?? "",
|
|
1454
|
-
onChange: (e) => setEditForm({ ...editForm, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
1455
|
-
helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
|
|
1456
|
-
fullWidth: true
|
|
1457
|
-
}
|
|
1458
|
-
), /* @__PURE__ */ React3.createElement(
|
|
1459
|
-
TextField,
|
|
1460
|
-
{
|
|
1461
|
-
label: "RPM Limit",
|
|
1462
|
-
type: "number",
|
|
1463
|
-
value: editForm.rpm_limit ?? "",
|
|
1464
|
-
onChange: (e) => setEditForm({ ...editForm, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
1465
|
-
helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
|
|
1466
|
-
fullWidth: true
|
|
1467
|
-
}
|
|
1468
|
-
), /* @__PURE__ */ React3.createElement(Box3, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ React3.createElement(
|
|
1469
|
-
Button2,
|
|
1470
|
-
{
|
|
1471
|
-
size: "small",
|
|
1472
|
-
color: "warning",
|
|
1473
|
-
variant: "outlined",
|
|
1474
|
-
onClick: () => setResetSpendConfirm(true)
|
|
1475
|
-
},
|
|
1476
|
-
"Reset Spend to $0"
|
|
1477
|
-
) : /* @__PURE__ */ React3.createElement(Box3, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ React3.createElement(
|
|
1478
|
-
Button2,
|
|
1479
|
-
{
|
|
1480
|
-
size: "small",
|
|
1481
|
-
color: "warning",
|
|
1482
|
-
variant: "contained",
|
|
1483
|
-
disabled: resetSpendSubmitting,
|
|
1484
|
-
onClick: handleResetSpend
|
|
1485
|
-
},
|
|
1486
|
-
resetSpendSubmitting ? /* @__PURE__ */ React3.createElement(CircularProgress, { size: 16 }) : "Confirm"
|
|
1487
|
-
), /* @__PURE__ */ React3.createElement(Button2, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel"))))), /* @__PURE__ */ React3.createElement(DialogActions, null, /* @__PURE__ */ React3.createElement(Button2, { onClick: handleCloseEdit }, "Cancel"), /* @__PURE__ */ React3.createElement(Button2, { onClick: handleUpdate, variant: "contained", color: "primary", disabled: editSubmitting }, editSubmitting ? /* @__PURE__ */ React3.createElement(CircularProgress, { size: 24 }) : "Save"))), /* @__PURE__ */ React3.createElement(Dialog, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ React3.createElement(DialogTitle, null, "Revoke Key?"), /* @__PURE__ */ React3.createElement(DialogContent, null, /* @__PURE__ */ React3.createElement(Typography3, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ React3.createElement(DialogActions, null, /* @__PURE__ */ React3.createElement(Button2, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ React3.createElement(
|
|
1344
|
+
), /* @__PURE__ */ React3.createElement(Dialog, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ React3.createElement(DialogTitle, null, "Revoke Key?"), /* @__PURE__ */ React3.createElement(DialogContent, null, /* @__PURE__ */ React3.createElement(Typography3, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ React3.createElement(DialogActions, null, /* @__PURE__ */ React3.createElement(Button2, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ React3.createElement(
|
|
1488
1345
|
Button2,
|
|
1489
1346
|
{
|
|
1490
1347
|
onClick: handleConfirmDelete,
|
|
@@ -1521,7 +1378,7 @@ var init_format = __esm({
|
|
|
1521
1378
|
}
|
|
1522
1379
|
});
|
|
1523
1380
|
|
|
1524
|
-
// src/components/
|
|
1381
|
+
// src/components/KeyFormDialog.tsx
|
|
1525
1382
|
import React4, { useState as useState2, useEffect, useMemo as useMemo3 } from "react";
|
|
1526
1383
|
import Box4 from "@mui/material/Box";
|
|
1527
1384
|
import Typography4 from "@mui/material/Typography";
|
|
@@ -1534,7 +1391,7 @@ import MenuItem from "@mui/material/MenuItem";
|
|
|
1534
1391
|
import Button3 from "@mui/material/Button";
|
|
1535
1392
|
import IconButton2 from "@mui/material/IconButton";
|
|
1536
1393
|
import CircularProgress2 from "@mui/material/CircularProgress";
|
|
1537
|
-
import
|
|
1394
|
+
import Autocomplete from "@mui/material/Autocomplete";
|
|
1538
1395
|
import Checkbox from "@mui/material/Checkbox";
|
|
1539
1396
|
import FormControlLabel from "@mui/material/FormControlLabel";
|
|
1540
1397
|
import Alert from "@mui/material/Alert";
|
|
@@ -1636,17 +1493,17 @@ function teamHelperText(teamError, teamRequired) {
|
|
|
1636
1493
|
if (teamRequired) return "Bind this key to a team for scoped access";
|
|
1637
1494
|
return "Optional: bind this key to a specific team for scoped access";
|
|
1638
1495
|
}
|
|
1639
|
-
function budgetHelperText(budgetInvalid, budgetEstimate) {
|
|
1640
|
-
if (budgetInvalid) return 'Enter a positive budget or tick "Unlimited"';
|
|
1496
|
+
function budgetHelperText(budgetInvalid, budgetEstimate, unlimited) {
|
|
1497
|
+
if (budgetInvalid && !unlimited) return 'Enter a positive budget or tick "Unlimited"';
|
|
1641
1498
|
if (budgetEstimate !== null) return `\u2248 ${fmtInt(budgetEstimate)} tokens at the selected model's rate`;
|
|
1642
1499
|
return void 0;
|
|
1643
1500
|
}
|
|
1644
|
-
function
|
|
1501
|
+
function fmtCost(perToken) {
|
|
1645
1502
|
if (!perToken) return null;
|
|
1646
1503
|
const per1k = perToken * 1e3;
|
|
1647
1504
|
return per1k < 0.01 ? `$${(perToken * 1e6).toFixed(2)}/M` : `$${per1k.toFixed(3)}/1K`;
|
|
1648
1505
|
}
|
|
1649
|
-
function
|
|
1506
|
+
function formatContextWindow(maxInput, maxOutput) {
|
|
1650
1507
|
if (!maxInput && !maxOutput) return null;
|
|
1651
1508
|
const fmt = (n) => {
|
|
1652
1509
|
if (!n) return null;
|
|
@@ -1661,9 +1518,9 @@ function formatContextWindow2(maxInput, maxOutput) {
|
|
|
1661
1518
|
if (outPart) return `ctx ${outPart} out`;
|
|
1662
1519
|
return null;
|
|
1663
1520
|
}
|
|
1664
|
-
var generateDefaultAlias,
|
|
1665
|
-
var
|
|
1666
|
-
"src/components/
|
|
1521
|
+
var generateDefaultAlias, createForm, editForm, ALL_PROXY_MODELS, SNIPPET_FILE_HINTS, SnippetTabs, KeyFormDialog;
|
|
1522
|
+
var init_KeyFormDialog = __esm({
|
|
1523
|
+
"src/components/KeyFormDialog.tsx"() {
|
|
1667
1524
|
"use strict";
|
|
1668
1525
|
init_format();
|
|
1669
1526
|
generateDefaultAlias = (username) => {
|
|
@@ -1671,8 +1528,8 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1671
1528
|
const hash = Math.random().toString(36).slice(2, 8);
|
|
1672
1529
|
return `${base}-${hash}`;
|
|
1673
1530
|
};
|
|
1674
|
-
|
|
1675
|
-
alias:
|
|
1531
|
+
createForm = (username) => ({
|
|
1532
|
+
alias: generateDefaultAlias(username),
|
|
1676
1533
|
models: [],
|
|
1677
1534
|
duration: "30d",
|
|
1678
1535
|
max_budget: 100,
|
|
@@ -1681,6 +1538,13 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1681
1538
|
team_id: void 0,
|
|
1682
1539
|
key_type: "llm_api"
|
|
1683
1540
|
});
|
|
1541
|
+
editForm = (k) => ({
|
|
1542
|
+
key_alias: k.key_alias ?? "",
|
|
1543
|
+
models: k.models ?? [],
|
|
1544
|
+
max_budget: k.max_budget,
|
|
1545
|
+
tpm_limit: k.tpm_limit,
|
|
1546
|
+
rpm_limit: k.rpm_limit
|
|
1547
|
+
});
|
|
1684
1548
|
ALL_PROXY_MODELS = "all-proxy-models";
|
|
1685
1549
|
SNIPPET_FILE_HINTS = {
|
|
1686
1550
|
opencode: "Add to ~/.config/opencode/opencode.json",
|
|
@@ -1727,46 +1591,79 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1727
1591
|
model && /* @__PURE__ */ React4.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.")
|
|
1728
1592
|
));
|
|
1729
1593
|
};
|
|
1730
|
-
|
|
1594
|
+
KeyFormDialog = ({
|
|
1731
1595
|
open,
|
|
1732
1596
|
onClose,
|
|
1597
|
+
mode,
|
|
1598
|
+
keyToEdit = null,
|
|
1733
1599
|
keys,
|
|
1734
1600
|
models,
|
|
1735
1601
|
teams,
|
|
1736
1602
|
username,
|
|
1737
1603
|
keyGenerationSettings,
|
|
1738
|
-
|
|
1604
|
+
onCreateKey,
|
|
1605
|
+
onUpdateKey,
|
|
1606
|
+
onResetKeySpend,
|
|
1739
1607
|
onGetConfig
|
|
1740
1608
|
}) => {
|
|
1741
|
-
const
|
|
1609
|
+
const isCreate = mode === "create";
|
|
1610
|
+
const [generateForm, setGenerateForm] = useState2(createForm(username));
|
|
1611
|
+
const [editFormState, setEditFormState] = useState2({});
|
|
1742
1612
|
const [unlimitedBudget, setUnlimitedBudget] = useState2(false);
|
|
1743
1613
|
const [submitting, setSubmitting] = useState2(false);
|
|
1744
1614
|
const [newKeyValue, setNewKeyValue] = useState2(null);
|
|
1745
1615
|
const [newKeySnippets, setNewKeySnippets] = useState2(null);
|
|
1746
1616
|
const [newKeyModel, setNewKeyModel] = useState2("");
|
|
1747
1617
|
const [generateError, setGenerateError] = useState2(null);
|
|
1618
|
+
const [editError, setEditError] = useState2(null);
|
|
1619
|
+
const [resetSpendConfirm, setResetSpendConfirm] = useState2(false);
|
|
1620
|
+
const [resetSpendSubmitting, setResetSpendSubmitting] = useState2(false);
|
|
1748
1621
|
useEffect(() => {
|
|
1749
1622
|
if (open) {
|
|
1750
|
-
|
|
1751
|
-
|
|
1623
|
+
if (isCreate) {
|
|
1624
|
+
setGenerateForm(createForm(username));
|
|
1625
|
+
} else if (keyToEdit) {
|
|
1626
|
+
setEditFormState(editForm(keyToEdit));
|
|
1627
|
+
setUnlimitedBudget(
|
|
1628
|
+
keyGenerationSettings?.allowUnlimitedBudget === true && (keyToEdit.max_budget === null || keyToEdit.max_budget === void 0)
|
|
1629
|
+
);
|
|
1630
|
+
} else {
|
|
1631
|
+
setEditFormState({});
|
|
1632
|
+
setUnlimitedBudget(false);
|
|
1633
|
+
}
|
|
1752
1634
|
setNewKeyValue(null);
|
|
1753
1635
|
setNewKeySnippets(null);
|
|
1754
1636
|
setNewKeyModel("");
|
|
1755
1637
|
setGenerateError(null);
|
|
1638
|
+
setEditError(null);
|
|
1639
|
+
setResetSpendConfirm(false);
|
|
1756
1640
|
}
|
|
1757
|
-
}, [open, username]);
|
|
1641
|
+
}, [open, isCreate, keyToEdit, username, keyGenerationSettings?.allowUnlimitedBudget]);
|
|
1758
1642
|
const allowUnlimitedBudget = keyGenerationSettings?.allowUnlimitedBudget ?? false;
|
|
1759
1643
|
const teamRequired = keyGenerationSettings?.teamRequired ?? true;
|
|
1760
|
-
const
|
|
1644
|
+
const formData = isCreate ? generateForm : editFormState;
|
|
1645
|
+
const setFormData = (patch) => {
|
|
1646
|
+
if (isCreate) {
|
|
1647
|
+
setGenerateForm((prev) => ({ ...prev, ...patch }));
|
|
1648
|
+
} else {
|
|
1649
|
+
setEditFormState((prev) => ({ ...prev, ...patch }));
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
const currentAlias = (isCreate ? generateForm.alias : editFormState.key_alias) ?? "";
|
|
1653
|
+
const setAliasField = (value) => {
|
|
1654
|
+
if (isCreate) setFormData({ alias: value });
|
|
1655
|
+
else setFormData({ key_alias: value });
|
|
1656
|
+
};
|
|
1657
|
+
const selectedTeam = teams.find((t) => t.team_id === (isCreate ? generateForm.team_id : void 0)) ?? null;
|
|
1761
1658
|
const availableModels = useMemo3(() => {
|
|
1762
1659
|
return models.filter((m) => isModelAllowedByTeam(m, selectedTeam?.models));
|
|
1763
1660
|
}, [models, selectedTeam]);
|
|
1764
1661
|
const selectedModels = availableModels.filter((m) => (formData.models || []).includes(m.model_name));
|
|
1765
|
-
const aliasError = !
|
|
1766
|
-
const teamError = teamRequired && !
|
|
1662
|
+
const aliasError = !currentAlias.trim();
|
|
1663
|
+
const teamError = isCreate && teamRequired && !generateForm.team_id;
|
|
1767
1664
|
const budgetInvalid = !unlimitedBudget && (formData.max_budget === void 0 || formData.max_budget === null || formData.max_budget <= 0);
|
|
1768
|
-
const
|
|
1769
|
-
const aliasDuplicate = !!
|
|
1665
|
+
const canSubmit = !aliasError && !teamError && !budgetInvalid && !submitting;
|
|
1666
|
+
const aliasDuplicate = !!currentAlias && keys.some((k) => k.key_alias === currentAlias && k !== keyToEdit);
|
|
1770
1667
|
const budgetEstimate = useMemo3(() => {
|
|
1771
1668
|
if (unlimitedBudget || budgetInvalid) return null;
|
|
1772
1669
|
const inputCosts = selectedModels.map((m) => m.input_cost_per_token).filter((c) => typeof c === "number" && c > 0);
|
|
@@ -1779,11 +1676,11 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1779
1676
|
setGenerateError(null);
|
|
1780
1677
|
try {
|
|
1781
1678
|
const request = {
|
|
1782
|
-
...
|
|
1783
|
-
max_budget: unlimitedBudget ? null :
|
|
1679
|
+
...generateForm,
|
|
1680
|
+
max_budget: unlimitedBudget ? null : generateForm.max_budget
|
|
1784
1681
|
};
|
|
1785
|
-
const response = await
|
|
1786
|
-
const model =
|
|
1682
|
+
const response = await onCreateKey(request);
|
|
1683
|
+
const model = generateForm.models?.[0] ?? availableModels[0]?.model_name ?? "";
|
|
1787
1684
|
setNewKeyValue(response.key);
|
|
1788
1685
|
setNewKeyModel(model);
|
|
1789
1686
|
setNewKeySnippets(null);
|
|
@@ -1792,7 +1689,7 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1792
1689
|
setNewKeySnippets(buildSnippets(config.baseUrl, response.key, model));
|
|
1793
1690
|
} catch {
|
|
1794
1691
|
}
|
|
1795
|
-
|
|
1692
|
+
setGenerateForm(createForm(username));
|
|
1796
1693
|
setUnlimitedBudget(false);
|
|
1797
1694
|
} catch (error) {
|
|
1798
1695
|
setGenerateError(error?.message ?? "Failed to generate key");
|
|
@@ -1800,27 +1697,58 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1800
1697
|
setSubmitting(false);
|
|
1801
1698
|
}
|
|
1802
1699
|
};
|
|
1700
|
+
const handleUpdate = async () => {
|
|
1701
|
+
if (!keyToEdit) return;
|
|
1702
|
+
setSubmitting(true);
|
|
1703
|
+
setEditError(null);
|
|
1704
|
+
try {
|
|
1705
|
+
const request = {
|
|
1706
|
+
...editFormState,
|
|
1707
|
+
max_budget: unlimitedBudget ? null : editFormState.max_budget
|
|
1708
|
+
};
|
|
1709
|
+
await onUpdateKey(keyToEdit.token ?? keyToEdit.key, request);
|
|
1710
|
+
onClose();
|
|
1711
|
+
} catch (error) {
|
|
1712
|
+
setEditError(error?.message ?? "Failed to update key");
|
|
1713
|
+
} finally {
|
|
1714
|
+
setSubmitting(false);
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
const handleResetSpend = async () => {
|
|
1718
|
+
if (!keyToEdit || !onResetKeySpend) return;
|
|
1719
|
+
setResetSpendSubmitting(true);
|
|
1720
|
+
try {
|
|
1721
|
+
await onResetKeySpend(keyToEdit.token ?? keyToEdit.key);
|
|
1722
|
+
setResetSpendConfirm(false);
|
|
1723
|
+
onClose();
|
|
1724
|
+
} finally {
|
|
1725
|
+
setResetSpendSubmitting(false);
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1803
1728
|
const handleClose = () => {
|
|
1804
1729
|
onClose();
|
|
1805
1730
|
setGenerateError(null);
|
|
1731
|
+
setEditError(null);
|
|
1806
1732
|
setNewKeyValue(null);
|
|
1807
1733
|
setNewKeySnippets(null);
|
|
1808
|
-
|
|
1734
|
+
setGenerateForm(createForm(username));
|
|
1735
|
+
setEditFormState({});
|
|
1809
1736
|
setUnlimitedBudget(false);
|
|
1737
|
+
setResetSpendConfirm(false);
|
|
1810
1738
|
};
|
|
1811
1739
|
const copyToClipboard = (text) => {
|
|
1812
1740
|
navigator.clipboard.writeText(text);
|
|
1813
1741
|
};
|
|
1814
1742
|
const modelOption = (m) => {
|
|
1815
|
-
const inCost =
|
|
1816
|
-
const outCost =
|
|
1817
|
-
const ctx =
|
|
1743
|
+
const inCost = fmtCost(m.input_cost_per_token);
|
|
1744
|
+
const outCost = fmtCost(m.output_cost_per_token);
|
|
1745
|
+
const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
|
|
1818
1746
|
return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
|
|
1819
1747
|
};
|
|
1820
1748
|
const renderTeamField = () => {
|
|
1821
1749
|
if (teams.length > 0) {
|
|
1822
1750
|
return /* @__PURE__ */ React4.createElement(
|
|
1823
|
-
|
|
1751
|
+
Autocomplete,
|
|
1824
1752
|
{
|
|
1825
1753
|
options: teams,
|
|
1826
1754
|
getOptionLabel: (t) => t.team_alias || t.team_id,
|
|
@@ -1830,28 +1758,61 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1830
1758
|
const model = models.find((m) => m.model_name === name);
|
|
1831
1759
|
return model ? isModelAllowedByTeam(model, team?.models) : false;
|
|
1832
1760
|
});
|
|
1833
|
-
setFormData({
|
|
1761
|
+
setFormData({ team_id: team?.team_id, models: restrictedModels });
|
|
1834
1762
|
},
|
|
1763
|
+
disabled: !isCreate,
|
|
1835
1764
|
renderInput: (params) => /* @__PURE__ */ React4.createElement(
|
|
1836
1765
|
TextField2,
|
|
1837
1766
|
{
|
|
1838
1767
|
...params,
|
|
1839
1768
|
label: "Team",
|
|
1840
1769
|
error: teamError,
|
|
1841
|
-
helperText: teamHelperText(teamError, teamRequired),
|
|
1842
|
-
required: teamRequired,
|
|
1770
|
+
helperText: isCreate ? teamHelperText(teamError, teamRequired) : "Team binding is fixed after creation \u2014 delete and recreate the key to change it",
|
|
1771
|
+
required: isCreate && teamRequired,
|
|
1843
1772
|
fullWidth: true
|
|
1844
1773
|
}
|
|
1845
1774
|
)
|
|
1846
1775
|
}
|
|
1847
1776
|
);
|
|
1848
1777
|
}
|
|
1849
|
-
if (teamRequired) {
|
|
1778
|
+
if (teamRequired && isCreate) {
|
|
1850
1779
|
return /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.");
|
|
1851
1780
|
}
|
|
1852
1781
|
return null;
|
|
1853
1782
|
};
|
|
1854
|
-
|
|
1783
|
+
const editKeyId = keyToEdit?.token ?? keyToEdit?.key ?? "";
|
|
1784
|
+
const renderDangerZone = () => {
|
|
1785
|
+
if (isCreate || !onResetKeySpend) return null;
|
|
1786
|
+
return /* @__PURE__ */ React4.createElement(Box4, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ React4.createElement(
|
|
1787
|
+
Button3,
|
|
1788
|
+
{
|
|
1789
|
+
size: "small",
|
|
1790
|
+
color: "warning",
|
|
1791
|
+
variant: "outlined",
|
|
1792
|
+
onClick: () => setResetSpendConfirm(true)
|
|
1793
|
+
},
|
|
1794
|
+
"Reset Spend to $0"
|
|
1795
|
+
) : /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2", color: "warning.main" }, "Zero out spend counter?"), /* @__PURE__ */ React4.createElement(
|
|
1796
|
+
Button3,
|
|
1797
|
+
{
|
|
1798
|
+
size: "small",
|
|
1799
|
+
color: "warning",
|
|
1800
|
+
variant: "contained",
|
|
1801
|
+
disabled: resetSpendSubmitting,
|
|
1802
|
+
onClick: handleResetSpend
|
|
1803
|
+
},
|
|
1804
|
+
resetSpendSubmitting ? /* @__PURE__ */ React4.createElement(CircularProgress2, { size: 16 }) : "Confirm"
|
|
1805
|
+
), /* @__PURE__ */ React4.createElement(Button3, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel")));
|
|
1806
|
+
};
|
|
1807
|
+
const maskKey = (key) => {
|
|
1808
|
+
if (key.length <= 8) return "***";
|
|
1809
|
+
return `${key.slice(0, 4)}...${key.slice(-4)}`;
|
|
1810
|
+
};
|
|
1811
|
+
let dialogTitle = "Edit Key";
|
|
1812
|
+
if (newKeyValue) dialogTitle = "Key Generated";
|
|
1813
|
+
else if (isCreate) dialogTitle = "Generate New Key";
|
|
1814
|
+
const submitLabel = isCreate ? "Generate" : "Save";
|
|
1815
|
+
return /* @__PURE__ */ React4.createElement(Dialog2, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React4.createElement(DialogTitle2, null, dialogTitle), /* @__PURE__ */ React4.createElement(DialogContent2, null, newKeyValue ? /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ React4.createElement(
|
|
1855
1816
|
Box4,
|
|
1856
1817
|
{
|
|
1857
1818
|
display: "flex",
|
|
@@ -1900,14 +1861,15 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1900
1861
|
newKeySnippets.publicEndpoint
|
|
1901
1862
|
),
|
|
1902
1863
|
/* @__PURE__ */ React4.createElement(IconButton2, { size: "small", onClick: () => copyToClipboard(newKeySnippets.publicEndpoint) }, /* @__PURE__ */ React4.createElement(ContentCopy2, { fontSize: "small" }))
|
|
1903
|
-
)), newKeySnippets && /* @__PURE__ */ React4.createElement(Box4, { mt: 3 }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React4.createElement(Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ React4.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ React4.createElement(Box4, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, generateError && /* @__PURE__ */ React4.createElement(Alert, { severity: "error", onClose: () => setGenerateError(null) }, generateError), /* @__PURE__ */ React4.createElement(
|
|
1864
|
+
)), newKeySnippets && /* @__PURE__ */ React4.createElement(Box4, { mt: 3 }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React4.createElement(Code, { fontSize: "small", color: "action" }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2" }, "Start calling the proxy \u2014 paste and run")), /* @__PURE__ */ React4.createElement(SnippetTabs, { snippets: newKeySnippets, model: newKeyModel, onCopy: copyToClipboard }))) : /* @__PURE__ */ React4.createElement(Box4, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, !isCreate && /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ React4.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editKeyId))), generateError && /* @__PURE__ */ React4.createElement(Alert, { severity: "error", onClose: () => setGenerateError(null) }, generateError), editError && /* @__PURE__ */ React4.createElement(Alert, { severity: "error", onClose: () => setEditError(null) }, editError), /* @__PURE__ */ React4.createElement(
|
|
1904
1865
|
TextField2,
|
|
1905
1866
|
{
|
|
1906
1867
|
label: "Alias",
|
|
1907
|
-
value:
|
|
1868
|
+
value: currentAlias,
|
|
1908
1869
|
onChange: (e) => {
|
|
1909
|
-
|
|
1870
|
+
setAliasField(e.target.value);
|
|
1910
1871
|
setGenerateError(null);
|
|
1872
|
+
setEditError(null);
|
|
1911
1873
|
},
|
|
1912
1874
|
error: aliasError,
|
|
1913
1875
|
color: aliasDuplicate ? "warning" : void 0,
|
|
@@ -1915,13 +1877,13 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1915
1877
|
required: true,
|
|
1916
1878
|
fullWidth: true
|
|
1917
1879
|
}
|
|
1918
|
-
), /* @__PURE__ */ React4.createElement(
|
|
1880
|
+
), !isCreate ? null : /* @__PURE__ */ React4.createElement(
|
|
1919
1881
|
TextField2,
|
|
1920
1882
|
{
|
|
1921
1883
|
select: true,
|
|
1922
1884
|
label: "Duration",
|
|
1923
|
-
value:
|
|
1924
|
-
onChange: (e) => setFormData({
|
|
1885
|
+
value: generateForm.duration || "30d",
|
|
1886
|
+
onChange: (e) => setFormData({ duration: e.target.value }),
|
|
1925
1887
|
fullWidth: true
|
|
1926
1888
|
},
|
|
1927
1889
|
/* @__PURE__ */ React4.createElement(MenuItem, { value: "1d" }, "1 Day"),
|
|
@@ -1930,14 +1892,14 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1930
1892
|
/* @__PURE__ */ React4.createElement(MenuItem, { value: "90d" }, "90 Days"),
|
|
1931
1893
|
/* @__PURE__ */ React4.createElement(MenuItem, { value: "1y" }, "1 Year")
|
|
1932
1894
|
), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ React4.createElement(
|
|
1933
|
-
|
|
1895
|
+
Autocomplete,
|
|
1934
1896
|
{
|
|
1935
1897
|
multiple: true,
|
|
1936
1898
|
options: availableModels,
|
|
1937
1899
|
groupBy: (m) => m.mode || "other",
|
|
1938
1900
|
getOptionLabel: (m) => m.model_name,
|
|
1939
1901
|
value: selectedModels,
|
|
1940
|
-
onChange: (_e, selected) => setFormData({
|
|
1902
|
+
onChange: (_e, selected) => setFormData({ models: selected.map((m) => m.model_name) }),
|
|
1941
1903
|
renderOption: (props, m) => /* @__PURE__ */ React4.createElement("li", { ...props }, modelOption(m)),
|
|
1942
1904
|
renderInput: (params) => /* @__PURE__ */ React4.createElement(
|
|
1943
1905
|
TextField2,
|
|
@@ -1959,9 +1921,9 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1959
1921
|
onChange: (e) => {
|
|
1960
1922
|
setUnlimitedBudget(e.target.checked);
|
|
1961
1923
|
if (e.target.checked) {
|
|
1962
|
-
setFormData({
|
|
1924
|
+
setFormData({ max_budget: null });
|
|
1963
1925
|
} else {
|
|
1964
|
-
setFormData({
|
|
1926
|
+
setFormData({ max_budget: editFormState.max_budget ?? generateForm.max_budget ?? 100 });
|
|
1965
1927
|
}
|
|
1966
1928
|
}
|
|
1967
1929
|
}
|
|
@@ -1974,9 +1936,9 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1974
1936
|
label: "Max Budget (USD)",
|
|
1975
1937
|
type: "number",
|
|
1976
1938
|
value: unlimitedBudget ? "" : formData.max_budget ?? "",
|
|
1977
|
-
onChange: (e) => setFormData({
|
|
1939
|
+
onChange: (e) => setFormData({ max_budget: e.target.value ? Number(e.target.value) : void 0 }),
|
|
1978
1940
|
error: budgetInvalid,
|
|
1979
|
-
helperText: budgetHelperText(budgetInvalid, budgetEstimate),
|
|
1941
|
+
helperText: budgetHelperText(budgetInvalid, budgetEstimate, unlimitedBudget),
|
|
1980
1942
|
disabled: unlimitedBudget,
|
|
1981
1943
|
required: true,
|
|
1982
1944
|
fullWidth: true
|
|
@@ -1987,7 +1949,7 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1987
1949
|
label: "TPM Limit",
|
|
1988
1950
|
type: "number",
|
|
1989
1951
|
value: formData.tpm_limit ?? "",
|
|
1990
|
-
onChange: (e) => setFormData({
|
|
1952
|
+
onChange: (e) => setFormData({ tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
1991
1953
|
helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
|
|
1992
1954
|
fullWidth: true
|
|
1993
1955
|
}
|
|
@@ -1997,19 +1959,19 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1997
1959
|
label: "RPM Limit",
|
|
1998
1960
|
type: "number",
|
|
1999
1961
|
value: formData.rpm_limit ?? "",
|
|
2000
|
-
onChange: (e) => setFormData({
|
|
1962
|
+
onChange: (e) => setFormData({ rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
2001
1963
|
helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
|
|
2002
1964
|
fullWidth: true
|
|
2003
1965
|
}
|
|
2004
|
-
))))), /* @__PURE__ */ React4.createElement(DialogActions2, null, newKeyValue ? /* @__PURE__ */ React4.createElement(Button3, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Button3, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ React4.createElement(
|
|
1966
|
+
))), renderDangerZone())), /* @__PURE__ */ React4.createElement(DialogActions2, null, newKeyValue ? /* @__PURE__ */ React4.createElement(Button3, { onClick: handleClose, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Button3, { onClick: handleClose }, "Cancel"), /* @__PURE__ */ React4.createElement(
|
|
2005
1967
|
Button3,
|
|
2006
1968
|
{
|
|
2007
|
-
onClick: handleGenerate,
|
|
1969
|
+
onClick: isCreate ? handleGenerate : handleUpdate,
|
|
2008
1970
|
variant: "contained",
|
|
2009
1971
|
color: "primary",
|
|
2010
|
-
disabled: !
|
|
1972
|
+
disabled: !canSubmit
|
|
2011
1973
|
},
|
|
2012
|
-
submitting ? /* @__PURE__ */ React4.createElement(CircularProgress2, { size: 24 }) :
|
|
1974
|
+
submitting ? /* @__PURE__ */ React4.createElement(CircularProgress2, { size: 24 }) : submitLabel
|
|
2013
1975
|
))));
|
|
2014
1976
|
};
|
|
2015
1977
|
}
|
|
@@ -2026,7 +1988,7 @@ import Button4 from "@mui/material/Button";
|
|
|
2026
1988
|
import Alert2 from "@mui/material/Alert";
|
|
2027
1989
|
import Checkbox2 from "@mui/material/Checkbox";
|
|
2028
1990
|
import FormControlLabel2 from "@mui/material/FormControlLabel";
|
|
2029
|
-
import
|
|
1991
|
+
import Autocomplete2 from "@mui/material/Autocomplete";
|
|
2030
1992
|
import CircularProgress3 from "@mui/material/CircularProgress";
|
|
2031
1993
|
import MenuItem2 from "@mui/material/MenuItem";
|
|
2032
1994
|
import Box5 from "@mui/material/Box";
|
|
@@ -2295,7 +2257,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2295
2257
|
fullWidth: true
|
|
2296
2258
|
}
|
|
2297
2259
|
), /* @__PURE__ */ React5.createElement(
|
|
2298
|
-
|
|
2260
|
+
Autocomplete2,
|
|
2299
2261
|
{
|
|
2300
2262
|
multiple: true,
|
|
2301
2263
|
options: modelNames,
|
|
@@ -2368,7 +2330,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2368
2330
|
},
|
|
2369
2331
|
/* @__PURE__ */ React5.createElement(DeleteIcon, { fontSize: "small" })
|
|
2370
2332
|
))))))), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ React5.createElement(
|
|
2371
|
-
|
|
2333
|
+
Autocomplete2,
|
|
2372
2334
|
{
|
|
2373
2335
|
freeSolo: true,
|
|
2374
2336
|
autoHighlight: true,
|
|
@@ -2427,7 +2389,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2427
2389
|
},
|
|
2428
2390
|
"Add"
|
|
2429
2391
|
))), showKnowledgeBases && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Knowledge Bases"), /* @__PURE__ */ React5.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__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setKbError(null) }, kbError), /* @__PURE__ */ React5.createElement(
|
|
2430
|
-
|
|
2392
|
+
Autocomplete2,
|
|
2431
2393
|
{
|
|
2432
2394
|
multiple: true,
|
|
2433
2395
|
options: kbOptions,
|
|
@@ -2446,7 +2408,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2446
2408
|
},
|
|
2447
2409
|
kbBusy ? "Saving\u2026" : "Save knowledge bases"
|
|
2448
2410
|
))), showMcpServers && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "MCP Servers"), /* @__PURE__ */ React5.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__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMcpError(null) }, mcpError), /* @__PURE__ */ React5.createElement(
|
|
2449
|
-
|
|
2411
|
+
Autocomplete2,
|
|
2450
2412
|
{
|
|
2451
2413
|
multiple: true,
|
|
2452
2414
|
options: mcpOptions,
|
|
@@ -4121,7 +4083,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4121
4083
|
"use strict";
|
|
4122
4084
|
init_DashboardHeader();
|
|
4123
4085
|
init_KeysTable();
|
|
4124
|
-
|
|
4086
|
+
init_KeyFormDialog();
|
|
4125
4087
|
init_ManageTeamDialog();
|
|
4126
4088
|
init_UsageStats();
|
|
4127
4089
|
init_LiteLLMBudgetWidget();
|
|
@@ -4179,6 +4141,8 @@ var init_LiteLLMPage = __esm({
|
|
|
4179
4141
|
);
|
|
4180
4142
|
const [snackbar, setSnackbar] = useState9(null);
|
|
4181
4143
|
const [manageTeam, setManageTeam] = useState9(null);
|
|
4144
|
+
const [keyToEdit, setKeyToEdit] = useState9(null);
|
|
4145
|
+
const keyFormOpen = generateDialogOpen || !!keyToEdit;
|
|
4182
4146
|
const [teamUsageCache, setTeamUsageCache] = useState9({});
|
|
4183
4147
|
const [teamUsageLoading, setTeamUsageLoading] = useState9({});
|
|
4184
4148
|
const { value: userInfo, loading: userLoading, error: userError } = useAsync3(
|
|
@@ -4422,13 +4386,11 @@ var init_LiteLLMPage = __esm({
|
|
|
4422
4386
|
KeysTable,
|
|
4423
4387
|
{
|
|
4424
4388
|
keys: keys ?? [],
|
|
4425
|
-
models: allowedModels,
|
|
4426
4389
|
loading: keysLoading || modelsLoading,
|
|
4427
4390
|
onGenerateKeyClick: () => setGenerateDialogOpen(true),
|
|
4428
|
-
|
|
4391
|
+
onEditKey: setKeyToEdit,
|
|
4429
4392
|
onBlockKey: handleBlockKey,
|
|
4430
4393
|
onUnblockKey: handleUnblockKey,
|
|
4431
|
-
onResetKeySpend: handleResetKeySpend,
|
|
4432
4394
|
onDeleteKey: handleDeleteKey,
|
|
4433
4395
|
onPruneExpiredKeys: handlePruneExpiredKeys
|
|
4434
4396
|
}
|
|
@@ -4461,16 +4423,23 @@ var init_LiteLLMPage = __esm({
|
|
|
4461
4423
|
loading: modelsLoading
|
|
4462
4424
|
}
|
|
4463
4425
|
), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React12.createElement(AuditLog, { api }), /* @__PURE__ */ React12.createElement(
|
|
4464
|
-
|
|
4426
|
+
KeyFormDialog,
|
|
4465
4427
|
{
|
|
4466
|
-
open:
|
|
4467
|
-
onClose: () =>
|
|
4428
|
+
open: keyFormOpen,
|
|
4429
|
+
onClose: () => {
|
|
4430
|
+
setGenerateDialogOpen(false);
|
|
4431
|
+
setKeyToEdit(null);
|
|
4432
|
+
},
|
|
4433
|
+
mode: keyToEdit ? "edit" : "create",
|
|
4434
|
+
keyToEdit,
|
|
4468
4435
|
keys: keys ?? [],
|
|
4469
4436
|
models: allowedModels,
|
|
4470
4437
|
teams: teams ?? [],
|
|
4471
4438
|
username: userInfo.user_id,
|
|
4472
4439
|
keyGenerationSettings: liteLlmConfig?.keyGeneration,
|
|
4473
|
-
|
|
4440
|
+
onCreateKey: handleGenerateKey,
|
|
4441
|
+
onUpdateKey: handleUpdateKey,
|
|
4442
|
+
onResetKeySpend: handleResetKeySpend,
|
|
4474
4443
|
onGetConfig: () => api.getConfig()
|
|
4475
4444
|
}
|
|
4476
4445
|
), /* @__PURE__ */ React12.createElement(
|
|
@@ -4992,13 +4961,37 @@ var LiteLLMBudgetGauges = ({
|
|
|
4992
4961
|
};
|
|
4993
4962
|
|
|
4994
4963
|
// src/index.ts
|
|
4995
|
-
|
|
4964
|
+
init_KeyFormDialog();
|
|
4965
|
+
|
|
4966
|
+
// src/components/GenerateKeyDialog.tsx
|
|
4967
|
+
init_KeyFormDialog();
|
|
4968
|
+
import React16 from "react";
|
|
4969
|
+
var GenerateKeyDialog = (props) => /* @__PURE__ */ React16.createElement(
|
|
4970
|
+
KeyFormDialog,
|
|
4971
|
+
{
|
|
4972
|
+
open: props.open,
|
|
4973
|
+
onClose: props.onClose,
|
|
4974
|
+
mode: "create",
|
|
4975
|
+
keys: props.keys,
|
|
4976
|
+
models: props.models,
|
|
4977
|
+
teams: props.teams,
|
|
4978
|
+
username: props.username,
|
|
4979
|
+
keyGenerationSettings: props.keyGenerationSettings,
|
|
4980
|
+
onCreateKey: props.onGenerateKey,
|
|
4981
|
+
onUpdateKey: async () => {
|
|
4982
|
+
},
|
|
4983
|
+
onGetConfig: props.onGetConfig
|
|
4984
|
+
}
|
|
4985
|
+
);
|
|
4986
|
+
|
|
4987
|
+
// src/index.ts
|
|
4996
4988
|
init_ManageTeamDialog();
|
|
4997
4989
|
init_api();
|
|
4998
4990
|
init_permissions();
|
|
4999
4991
|
export {
|
|
5000
4992
|
DashboardHeader,
|
|
5001
4993
|
GenerateKeyDialog,
|
|
4994
|
+
KeyFormDialog,
|
|
5002
4995
|
KeysTable,
|
|
5003
4996
|
LiteLLMBudgetGauges,
|
|
5004
4997
|
LiteLLMBudgetWidget,
|