@acarmisc/backstage-plugin-litellm 0.10.0 → 0.10.1

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 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
@@ -118,9 +118,6 @@ var init_api = __esm({
118
118
  async deleteKey(keyId) {
119
119
  return this.del(`/keys/${encodeURIComponent(keyId)}`);
120
120
  }
121
- async rotateKey(keyId) {
122
- return this.post(`/keys/${encodeURIComponent(keyId)}/regenerate`, {});
123
- }
124
121
  async blockKey(keyId) {
125
122
  await this.post(`/keys/${encodeURIComponent(keyId)}/block`, {});
126
123
  }
@@ -311,9 +308,7 @@ var init_KeysTable = __esm({
311
308
  tpm_limit: void 0,
312
309
  rpm_limit: void 0,
313
310
  team_id: void 0,
314
- key_type: "llm_api",
315
- auto_rotate: false,
316
- rotation_interval_days: void 0
311
+ key_type: "llm_api"
317
312
  });
318
313
  keyToEditForm = (k) => ({
319
314
  key_alias: k.key_alias ?? "",
@@ -329,7 +324,6 @@ var init_KeysTable = __esm({
329
324
  loading,
330
325
  onGenerateKey,
331
326
  onUpdateKey,
332
- onRotateKey,
333
327
  onBlockKey,
334
328
  onUnblockKey,
335
329
  onResetKeySpend,
@@ -347,8 +341,6 @@ var init_KeysTable = __esm({
347
341
  const [editingKey, setEditingKey] = (0, import_react2.useState)(null);
348
342
  const [editForm, setEditForm] = (0, import_react2.useState)({});
349
343
  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
344
  const [blockingKeyId, setBlockingKeyId] = (0, import_react2.useState)(null);
353
345
  const [deleteConfirmId, setDeleteConfirmId] = (0, import_react2.useState)(null);
354
346
  const [deleteSubmitting, setDeleteSubmitting] = (0, import_react2.useState)(false);
@@ -428,17 +420,6 @@ var init_KeysTable = __esm({
428
420
  setEditSubmitting(false);
429
421
  }
430
422
  };
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
423
  const handleToggleBlock = async (key) => {
443
424
  const keyId = key.token ?? key.key;
444
425
  setBlockingKeyId(keyId);
@@ -543,7 +524,6 @@ var init_KeysTable = __esm({
543
524
  "Generate Your First Key"
544
525
  )))) : filteredKeys.map((key) => {
545
526
  const keyId = key.token ?? key.key;
546
- const isRotating = rotatingKeyId === keyId;
547
527
  const isBlocking = blockingKeyId === keyId;
548
528
  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
529
  import_material2.Typography,
@@ -562,14 +542,6 @@ var init_KeysTable = __esm({
562
542
  },
563
543
  shortKeyId(keyId)
564
544
  ), /* @__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
545
  import_material2.IconButton,
574
546
  {
575
547
  onClick: () => handleToggleBlock(key),
@@ -716,36 +688,7 @@ var init_KeysTable = __esm({
716
688
  onChange: (e) => setFormData({ ...formData, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
717
689
  fullWidth: true
718
690
  }
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(
691
+ ))), /* @__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
692
  import_material2.Button,
750
693
  {
751
694
  onClick: handleGenerate,
@@ -754,7 +697,7 @@ var init_KeysTable = __esm({
754
697
  disabled: !canGenerate
755
698
  },
756
699
  submitting ? /* @__PURE__ */ import_react2.default.createElement(import_material2.CircularProgress, { size: 24 }) : "Generate"
757
- ), newKeyValue && /* @__PURE__ */ import_react2.default.createElement(import_material2.Button, { onClick: handleCloseModal, variant: "contained", color: "success" }, "Done"))), /* @__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(
700
+ )))), /* @__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
701
  import_material2.TextField,
759
702
  {
760
703
  label: "Alias",
@@ -820,26 +763,7 @@ var init_KeysTable = __esm({
820
763
  onClick: handleResetSpend
821
764
  },
822
765
  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: !!rotatedKeyValue, onClose: () => setRotatedKeyValue(null), maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogTitle, null, "Key Rotated"), /* @__PURE__ */ import_react2.default.createElement(import_material2.DialogContent, null, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2", color: "text.secondary", gutterBottom: true }, "The old secret is now invalid. Copy the new one \u2014 you won't see it again."), /* @__PURE__ */ import_react2.default.createElement(
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(
766
+ ), /* @__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
767
  import_material2.Button,
844
768
  {
845
769
  onClick: handleConfirmDelete,
@@ -1500,15 +1424,6 @@ var init_LiteLLMPage = __esm({
1500
1424
  },
1501
1425
  [api, refreshKeys]
1502
1426
  );
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
1427
  const handleDeleteKey = (0, import_react6.useCallback)(
1513
1428
  async (keyId) => {
1514
1429
  try {
@@ -1578,7 +1493,6 @@ var init_LiteLLMPage = __esm({
1578
1493
  loading: keysLoading || modelsLoading,
1579
1494
  onGenerateKey: handleGenerateKey,
1580
1495
  onUpdateKey: handleUpdateKey,
1581
- onRotateKey: handleRotateKey,
1582
1496
  onBlockKey: handleBlockKey,
1583
1497
  onUnblockKey: handleUnblockKey,
1584
1498
  onResetKeySpend: handleResetKeySpend,