@acarmisc/backstage-plugin-litellm 0.10.0 → 0.10.2
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/api.d.ts +0 -2
- package/dist/components/KeysTable.d.ts +0 -1
- package/dist/index.cjs.js +24 -96
- package/dist/index.cjs.js.map +2 -2
- package/dist/index.esm.js +24 -96
- package/dist/index.esm.js.map +2 -2
- package/dist/types.d.ts +1 -4
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ export interface LiteLlmApiInterface {
|
|
|
8
8
|
deleteKey(keyId: string): Promise<{
|
|
9
9
|
success: boolean;
|
|
10
10
|
}>;
|
|
11
|
-
rotateKey(keyId: string): Promise<GenerateKeyResponse>;
|
|
12
11
|
blockKey(keyId: string): Promise<void>;
|
|
13
12
|
unblockKey(keyId: string): Promise<void>;
|
|
14
13
|
resetKeySpend(keyId: string): Promise<void>;
|
|
@@ -40,7 +39,6 @@ export declare class LiteLlmApi implements LiteLlmApiInterface {
|
|
|
40
39
|
deleteKey(keyId: string): Promise<{
|
|
41
40
|
success: boolean;
|
|
42
41
|
}>;
|
|
43
|
-
rotateKey(keyId: string): Promise<GenerateKeyResponse>;
|
|
44
42
|
blockKey(keyId: string): Promise<void>;
|
|
45
43
|
unblockKey(keyId: string): Promise<void>;
|
|
46
44
|
resetKeySpend(keyId: string): Promise<void>;
|
|
@@ -7,7 +7,6 @@ interface KeysTableProps {
|
|
|
7
7
|
loading: boolean;
|
|
8
8
|
onGenerateKey: (request: GenerateKeyRequest) => Promise<GenerateKeyResponse>;
|
|
9
9
|
onUpdateKey: (keyId: string, request: UpdateKeyRequest) => Promise<void>;
|
|
10
|
-
onRotateKey: (keyId: string) => Promise<GenerateKeyResponse>;
|
|
11
10
|
onBlockKey: (keyId: string) => Promise<void>;
|
|
12
11
|
onUnblockKey: (keyId: string) => Promise<void>;
|
|
13
12
|
onResetKeySpend: (keyId: string) => Promise<void>;
|
package/dist/index.cjs.js
CHANGED
|
@@ -72,7 +72,8 @@ var init_api = __esm({
|
|
|
72
72
|
} catch {
|
|
73
73
|
body = await response.text().catch(() => "");
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
const message = (typeof body === "object" && body && typeof body.error === "string" ? body.error : void 0) ?? `${response.status} ${response.statusText}`;
|
|
76
|
+
throw new ApiError(message, response.status, body);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
async get(path, params) {
|
|
@@ -118,9 +119,6 @@ var init_api = __esm({
|
|
|
118
119
|
async deleteKey(keyId) {
|
|
119
120
|
return this.del(`/keys/${encodeURIComponent(keyId)}`);
|
|
120
121
|
}
|
|
121
|
-
async rotateKey(keyId) {
|
|
122
|
-
return this.post(`/keys/${encodeURIComponent(keyId)}/regenerate`, {});
|
|
123
|
-
}
|
|
124
122
|
async blockKey(keyId) {
|
|
125
123
|
await this.post(`/keys/${encodeURIComponent(keyId)}/block`, {});
|
|
126
124
|
}
|
|
@@ -311,9 +309,7 @@ var init_KeysTable = __esm({
|
|
|
311
309
|
tpm_limit: void 0,
|
|
312
310
|
rpm_limit: void 0,
|
|
313
311
|
team_id: void 0,
|
|
314
|
-
key_type: "llm_api"
|
|
315
|
-
auto_rotate: false,
|
|
316
|
-
rotation_interval_days: void 0
|
|
312
|
+
key_type: "llm_api"
|
|
317
313
|
});
|
|
318
314
|
keyToEditForm = (k) => ({
|
|
319
315
|
key_alias: k.key_alias ?? "",
|
|
@@ -329,7 +325,6 @@ var init_KeysTable = __esm({
|
|
|
329
325
|
loading,
|
|
330
326
|
onGenerateKey,
|
|
331
327
|
onUpdateKey,
|
|
332
|
-
onRotateKey,
|
|
333
328
|
onBlockKey,
|
|
334
329
|
onUnblockKey,
|
|
335
330
|
onResetKeySpend,
|
|
@@ -347,8 +342,6 @@ var init_KeysTable = __esm({
|
|
|
347
342
|
const [editingKey, setEditingKey] = (0, import_react2.useState)(null);
|
|
348
343
|
const [editForm, setEditForm] = (0, import_react2.useState)({});
|
|
349
344
|
const [editSubmitting, setEditSubmitting] = (0, import_react2.useState)(false);
|
|
350
|
-
const [rotatingKeyId, setRotatingKeyId] = (0, import_react2.useState)(null);
|
|
351
|
-
const [rotatedKeyValue, setRotatedKeyValue] = (0, import_react2.useState)(null);
|
|
352
345
|
const [blockingKeyId, setBlockingKeyId] = (0, import_react2.useState)(null);
|
|
353
346
|
const [deleteConfirmId, setDeleteConfirmId] = (0, import_react2.useState)(null);
|
|
354
347
|
const [deleteSubmitting, setDeleteSubmitting] = (0, import_react2.useState)(false);
|
|
@@ -428,17 +421,6 @@ var init_KeysTable = __esm({
|
|
|
428
421
|
setEditSubmitting(false);
|
|
429
422
|
}
|
|
430
423
|
};
|
|
431
|
-
const handleRotate = async (keyId) => {
|
|
432
|
-
setRotatingKeyId(keyId);
|
|
433
|
-
try {
|
|
434
|
-
const response = await onRotateKey(keyId);
|
|
435
|
-
setRotatedKeyValue(response.key);
|
|
436
|
-
} catch (error) {
|
|
437
|
-
console.error("Failed to rotate key:", error);
|
|
438
|
-
} finally {
|
|
439
|
-
setRotatingKeyId(null);
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
424
|
const handleToggleBlock = async (key) => {
|
|
443
425
|
const keyId = key.token ?? key.key;
|
|
444
426
|
setBlockingKeyId(keyId);
|
|
@@ -543,7 +525,6 @@ var init_KeysTable = __esm({
|
|
|
543
525
|
"Generate Your First Key"
|
|
544
526
|
)))) : filteredKeys.map((key) => {
|
|
545
527
|
const keyId = key.token ?? key.key;
|
|
546
|
-
const isRotating = rotatingKeyId === keyId;
|
|
547
528
|
const isBlocking = blockingKeyId === keyId;
|
|
548
529
|
return /* @__PURE__ */ import_react2.default.createElement(import_material2.TableRow, { key: keyId, sx: key.blocked ? { bgcolor: "action.disabledBackground" } : void 0 }, /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", alignItems: "center", gap: 0.5 }, key.key_alias || "-", key.blocked && /* @__PURE__ */ import_react2.default.createElement(import_material2.Chip, { label: "Blocked", color: "error", size: "small" }))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ import_react2.default.createElement(
|
|
549
530
|
import_material2.Typography,
|
|
@@ -562,14 +543,6 @@ var init_KeysTable = __esm({
|
|
|
562
543
|
},
|
|
563
544
|
shortKeyId(keyId)
|
|
564
545
|
), /* @__PURE__ */ import_react2.default.createElement(import_material2.IconButton, { size: "small", onClick: () => copyToClipboard(keyId), title: "Copy Key ID" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material.ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(ExpiryChip, { expiresAt: key.expires_at })), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2" }, key.tpm_limit ?? "-", " / ", key.rpm_limit ?? "-")), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ import_react2.default.createElement(import_material2.Chip, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ import_react2.default.createElement(import_material2.Chip, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ import_react2.default.createElement(import_material2.TableCell, { align: "right" }, /* @__PURE__ */ import_react2.default.createElement(import_material2.IconButton, { onClick: () => handleOpenEdit(key), title: "Edit key" }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Edit, { fontSize: "small" })), /* @__PURE__ */ import_react2.default.createElement(
|
|
565
|
-
import_material2.IconButton,
|
|
566
|
-
{
|
|
567
|
-
onClick: () => handleRotate(keyId),
|
|
568
|
-
disabled: isRotating,
|
|
569
|
-
title: "Rotate key \u2014 generates a new secret, same settings"
|
|
570
|
-
},
|
|
571
|
-
isRotating ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 18 }) : /* @__PURE__ */ import_react2.default.createElement(import_icons_material.Autorenew, { fontSize: "small" })
|
|
572
|
-
), /* @__PURE__ */ import_react2.default.createElement(
|
|
573
546
|
import_material2.IconButton,
|
|
574
547
|
{
|
|
575
548
|
onClick: () => handleToggleBlock(key),
|
|
@@ -716,36 +689,7 @@ var init_KeysTable = __esm({
|
|
|
716
689
|
onChange: (e) => setFormData({ ...formData, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
717
690
|
fullWidth: true
|
|
718
691
|
}
|
|
719
|
-
), /* @__PURE__ */ import_react2.default.createElement(
|
|
720
|
-
import_material2.FormControlLabel,
|
|
721
|
-
{
|
|
722
|
-
control: /* @__PURE__ */ import_react2.default.createElement(
|
|
723
|
-
import_material2.Checkbox,
|
|
724
|
-
{
|
|
725
|
-
checked: !!formData.auto_rotate,
|
|
726
|
-
onChange: (e) => setFormData({
|
|
727
|
-
...formData,
|
|
728
|
-
auto_rotate: e.target.checked,
|
|
729
|
-
rotation_interval_days: e.target.checked ? formData.rotation_interval_days ?? 90 : void 0
|
|
730
|
-
})
|
|
731
|
-
}
|
|
732
|
-
),
|
|
733
|
-
label: "Auto-rotate (LiteLLM rotates the secret on a schedule)"
|
|
734
|
-
}
|
|
735
|
-
), formData.auto_rotate && /* @__PURE__ */ import_react2.default.createElement(
|
|
736
|
-
import_material2.TextField,
|
|
737
|
-
{
|
|
738
|
-
label: "Rotate every (days)",
|
|
739
|
-
type: "number",
|
|
740
|
-
value: formData.rotation_interval_days ?? "",
|
|
741
|
-
onChange: (e) => setFormData({
|
|
742
|
-
...formData,
|
|
743
|
-
rotation_interval_days: e.target.value ? Number(e.target.value) : void 0
|
|
744
|
-
}),
|
|
745
|
-
helperText: "LiteLLM enforces this interval server-side; the old secret stops working the moment it rotates.",
|
|
746
|
-
fullWidth: true
|
|
747
|
-
}
|
|
748
|
-
))), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleCloseModal }, newKeyValue ? "Done" : "Cancel"), !newKeyValue && /* @__PURE__ */ import_react2.default.createElement(
|
|
692
|
+
))), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, newKeyValue ? /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleCloseModal, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleCloseModal }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(
|
|
749
693
|
import_material2.Button,
|
|
750
694
|
{
|
|
751
695
|
onClick: handleGenerate,
|
|
@@ -754,7 +698,7 @@ var init_KeysTable = __esm({
|
|
|
754
698
|
disabled: !canGenerate
|
|
755
699
|
},
|
|
756
700
|
submitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 24 }) : "Generate"
|
|
757
|
-
)
|
|
701
|
+
)))), /* @__PURE__ */ import_react2.default.createElement(import_material2.Dialog, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogTitle, null, "Edit Key"), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogContent, null, editingKey && /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ import_react2.default.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editingKey.key))), /* @__PURE__ */ import_react2.default.createElement(
|
|
758
702
|
import_material2.TextField,
|
|
759
703
|
{
|
|
760
704
|
label: "Alias",
|
|
@@ -820,26 +764,7 @@ var init_KeysTable = __esm({
|
|
|
820
764
|
onClick: handleResetSpend
|
|
821
765
|
},
|
|
822
766
|
resetSpendSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 16 }) : "Confirm"
|
|
823
|
-
), /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel"))))), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleCloseEdit }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleUpdate, variant: "contained", color: "primary", disabled: editSubmitting }, editSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 24 }) : "Save"))), /* @__PURE__ */ import_react2.default.createElement(import_material2.Dialog, { open: !!
|
|
824
|
-
import_material2.Box,
|
|
825
|
-
{
|
|
826
|
-
display: "flex",
|
|
827
|
-
alignItems: "center",
|
|
828
|
-
gap: 1,
|
|
829
|
-
mt: 2,
|
|
830
|
-
p: 2,
|
|
831
|
-
sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
|
|
832
|
-
},
|
|
833
|
-
/* @__PURE__ */ import_react2.default.createElement(
|
|
834
|
-
import_material2.Typography,
|
|
835
|
-
{
|
|
836
|
-
component: "code",
|
|
837
|
-
sx: { fontFamily: "monospace", wordBreak: "break-all", flex: 1 }
|
|
838
|
-
},
|
|
839
|
-
rotatedKeyValue
|
|
840
|
-
),
|
|
841
|
-
/* @__PURE__ */ import_react2.default.createElement(import_material2.IconButton, { onClick: () => copyToClipboard(rotatedKeyValue) }, /* @__PURE__ */ import_react2.default.createElement(import_icons_material.ContentCopy, null))
|
|
842
|
-
)), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: () => setRotatedKeyValue(null), variant: "contained", color: "success" }, "Done"))), /* @__PURE__ */ import_react2.default.createElement(import_material2.Dialog, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogTitle, null, "Revoke Key?"), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogContent, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(
|
|
767
|
+
), /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { size: "small", onClick: () => setResetSpendConfirm(false) }, "Cancel"))))), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleCloseEdit }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleUpdate, variant: "contained", color: "primary", disabled: editSubmitting }, editSubmitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 24 }) : "Save"))), /* @__PURE__ */ import_react2.default.createElement(import_material2.Dialog, { open: !!deleteConfirmId, onClose: () => setDeleteConfirmId(null), maxWidth: "xs", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogTitle, null, "Revoke Key?"), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogContent, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, null, "This will permanently revoke the key. Any integrations using it will stop working immediately.")), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogActions, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: () => setDeleteConfirmId(null), disabled: deleteSubmitting }, "Cancel"), /* @__PURE__ */ import_react2.default.createElement(
|
|
843
768
|
import_material2.Button,
|
|
844
769
|
{
|
|
845
770
|
onClick: handleConfirmDelete,
|
|
@@ -1180,7 +1105,15 @@ var init_TeamUsage = __esm({
|
|
|
1180
1105
|
color: isOver ? "error" : isNear ? "warning" : "primary",
|
|
1181
1106
|
sx: { height: 6, borderRadius: 1 }
|
|
1182
1107
|
}
|
|
1183
|
-
)), /* @__PURE__ */ import_react4.default.createElement(import_material4.Collapse, { in: expanded }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { mt: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ import_react4.default.createElement(import_material4.Stack, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ import_react4.default.createElement(Stat, { label: "TPM limit", value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react4.default.createElement(Stat, { label: "RPM limit", value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react4.default.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ import_react4.default.createElement(import_material4.Divider, { sx: { mb: 2 } }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_material4.Chip, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ import_react4.default.createElement(import_material4.Divider, { sx: { mb: 2 } }), usageLoading ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.CircularProgress, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 160, mb: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts2.AreaChart, { data: dailyData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.XAxis, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.Area, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Divider, { sx: { mb: 2 } })) : null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ import_react4.default.createElement(import_material4.TableContainer, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Table, { size: "small" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableHead, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, "User"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, "Role"))), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableBody, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, { key: m.user_id }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell,
|
|
1108
|
+
)), /* @__PURE__ */ import_react4.default.createElement(import_material4.Collapse, { in: expanded }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { mt: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ import_react4.default.createElement(import_material4.Stack, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ import_react4.default.createElement(Stat, { label: "TPM limit", value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react4.default.createElement(Stat, { label: "RPM limit", value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ import_react4.default.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ import_react4.default.createElement(import_material4.Divider, { sx: { mb: 2 } }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_material4.Chip, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ import_react4.default.createElement(import_material4.Divider, { sx: { mb: 2 } }), usageLoading ? /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_material4.CircularProgress, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { height: 160, mb: 2 }, /* @__PURE__ */ import_react4.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts2.AreaChart, { data: dailyData }, /* @__PURE__ */ import_react4.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.XAxis, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ import_react4.default.createElement(import_recharts2.Area, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ import_react4.default.createElement(import_material4.Divider, { sx: { mb: 2 } })) : null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Box, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ import_react4.default.createElement(import_icons_material2.Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ import_react4.default.createElement(import_material4.TableContainer, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Table, { size: "small" }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableHead, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, "User"), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, "Role"))), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableBody, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react4.default.createElement(import_material4.TableRow, { key: m.user_id }, /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, m.user_email ? /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react4.default.createElement(
|
|
1109
|
+
import_material4.Typography,
|
|
1110
|
+
{
|
|
1111
|
+
variant: "caption",
|
|
1112
|
+
color: "text.secondary",
|
|
1113
|
+
sx: { fontFamily: "monospace" }
|
|
1114
|
+
},
|
|
1115
|
+
m.user_id
|
|
1116
|
+
)) : /* @__PURE__ */ import_react4.default.createElement(import_material4.Typography, { variant: "body2", sx: { fontFamily: "monospace" } }, m.user_id)), /* @__PURE__ */ import_react4.default.createElement(import_material4.TableCell, null, /* @__PURE__ */ import_react4.default.createElement(
|
|
1184
1117
|
import_material4.Chip,
|
|
1185
1118
|
{
|
|
1186
1119
|
label: m.role,
|
|
@@ -1444,10 +1377,15 @@ var init_LiteLLMPage = __esm({
|
|
|
1444
1377
|
}, [allModels, userInfo, teams]);
|
|
1445
1378
|
const handleGenerateKey = (0, import_react6.useCallback)(
|
|
1446
1379
|
async (request) => {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1380
|
+
try {
|
|
1381
|
+
const response = await api.generateKey(request);
|
|
1382
|
+
setSnackbar({ message: "Key generated successfully", severity: "success" });
|
|
1383
|
+
refreshKeys();
|
|
1384
|
+
return response;
|
|
1385
|
+
} catch (e) {
|
|
1386
|
+
setSnackbar({ message: `Failed to generate key: ${e.message}`, severity: "error" });
|
|
1387
|
+
throw e;
|
|
1388
|
+
}
|
|
1451
1389
|
},
|
|
1452
1390
|
[api, refreshKeys]
|
|
1453
1391
|
);
|
|
@@ -1500,15 +1438,6 @@ var init_LiteLLMPage = __esm({
|
|
|
1500
1438
|
},
|
|
1501
1439
|
[api, refreshKeys]
|
|
1502
1440
|
);
|
|
1503
|
-
const handleRotateKey = (0, import_react6.useCallback)(
|
|
1504
|
-
async (keyId) => {
|
|
1505
|
-
const response = await api.rotateKey(keyId);
|
|
1506
|
-
setSnackbar({ message: "Key rotated \u2014 copy the new secret now", severity: "success" });
|
|
1507
|
-
refreshKeys();
|
|
1508
|
-
return response;
|
|
1509
|
-
},
|
|
1510
|
-
[api, refreshKeys]
|
|
1511
|
-
);
|
|
1512
1441
|
const handleDeleteKey = (0, import_react6.useCallback)(
|
|
1513
1442
|
async (keyId) => {
|
|
1514
1443
|
try {
|
|
@@ -1578,7 +1507,6 @@ var init_LiteLLMPage = __esm({
|
|
|
1578
1507
|
loading: keysLoading || modelsLoading,
|
|
1579
1508
|
onGenerateKey: handleGenerateKey,
|
|
1580
1509
|
onUpdateKey: handleUpdateKey,
|
|
1581
|
-
onRotateKey: handleRotateKey,
|
|
1582
1510
|
onBlockKey: handleBlockKey,
|
|
1583
1511
|
onUnblockKey: handleUnblockKey,
|
|
1584
1512
|
onResetKeySpend: handleResetKeySpend,
|