@acarmisc/backstage-plugin-litellm 0.3.0 → 0.3.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/index.esm.js CHANGED
@@ -143,7 +143,7 @@ var init_DashboardHeader = __esm({
143
143
  });
144
144
 
145
145
  // src/components/KeysTable.tsx
146
- import React2, { useEffect, useState } from "react";
146
+ import React2, { useState } from "react";
147
147
  import {
148
148
  Paper as Paper2,
149
149
  Table,
@@ -166,8 +166,8 @@ import {
166
166
  CircularProgress,
167
167
  Autocomplete
168
168
  } from "@mui/material";
169
- import { ContentCopy, Delete, Add, Edit, Visibility, VisibilityOff } from "@mui/icons-material";
170
- var maskKey, formatDate, emptyForm, keyToEditForm, KeysTable;
169
+ import { ContentCopy, Delete, Add, Edit } from "@mui/icons-material";
170
+ var maskKey, shortKeyId, formatDate, emptyForm, keyToEditForm, KeysTable;
171
171
  var init_KeysTable = __esm({
172
172
  "src/components/KeysTable.tsx"() {
173
173
  "use strict";
@@ -175,6 +175,11 @@ var init_KeysTable = __esm({
175
175
  if (key.length <= 8) return "***";
176
176
  return `${key.slice(0, 4)}...${key.slice(-4)}`;
177
177
  };
178
+ shortKeyId = (token) => {
179
+ if (!token) return "-";
180
+ if (token.length <= 16) return token;
181
+ return `${token.slice(0, 12)}\u2026`;
182
+ };
178
183
  formatDate = (dateStr) => {
179
184
  try {
180
185
  return new Date(dateStr).toLocaleDateString();
@@ -186,7 +191,7 @@ var init_KeysTable = __esm({
186
191
  alias: "",
187
192
  models: [],
188
193
  duration: "30d",
189
- max_budget: void 0,
194
+ max_budget: 100,
190
195
  tpm_limit: void 0,
191
196
  team_id: void 0,
192
197
  key_type: "llm_api"
@@ -208,16 +213,9 @@ var init_KeysTable = __esm({
208
213
  onDeleteKey
209
214
  }) => {
210
215
  const [generateModalOpen, setGenerateModalOpen] = useState(false);
211
- const [showKeyValue, setShowKeyValue] = useState(null);
212
216
  const [newKeyValue, setNewKeyValue] = useState(null);
213
217
  const [formData, setFormData] = useState(emptyForm());
214
218
  const [submitting, setSubmitting] = useState(false);
215
- useEffect(() => {
216
- if (!generateModalOpen) return;
217
- if (!formData.team_id && teams.length > 0) {
218
- setFormData((f) => ({ ...f, team_id: teams[0].team_id }));
219
- }
220
- }, [generateModalOpen, teams, formData.team_id]);
221
219
  const canGenerate = true;
222
220
  const [editingKey, setEditingKey] = useState(null);
223
221
  const [editForm, setEditForm] = useState({});
@@ -231,10 +229,6 @@ var init_KeysTable = __esm({
231
229
  const response = await onGenerateKey(formData);
232
230
  setNewKeyValue(response.key);
233
231
  setFormData(emptyForm());
234
- setTimeout(() => {
235
- setGenerateModalOpen(false);
236
- setNewKeyValue(null);
237
- }, 1500);
238
232
  } catch (error) {
239
233
  console.error("Failed to generate key:", error);
240
234
  } finally {
@@ -278,32 +272,26 @@ var init_KeysTable = __esm({
278
272
  onClick: () => setGenerateModalOpen(true)
279
273
  },
280
274
  "Generate New Key"
281
- )), /* @__PURE__ */ React2.createElement(TableContainer, null, /* @__PURE__ */ React2.createElement(Table, null, /* @__PURE__ */ React2.createElement(TableHead, null, /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, null, "Alias"), /* @__PURE__ */ React2.createElement(TableCell, null, "Key"), /* @__PURE__ */ React2.createElement(TableCell, null, "Created"), /* @__PURE__ */ React2.createElement(TableCell, null, "Spend"), /* @__PURE__ */ React2.createElement(TableCell, null, "Budget"), /* @__PURE__ */ React2.createElement(TableCell, null, "TPM Limit"), /* @__PURE__ */ React2.createElement(TableCell, null, "Models"), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React2.createElement(TableBody, null, loading ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }))) : keys.length === 0 ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary" }, "No keys found"))) : keys.map((key) => /* @__PURE__ */ React2.createElement(TableRow, { key: key.key }, /* @__PURE__ */ React2.createElement(TableCell, null, key.key_alias || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(
282
- Typography2,
283
- {
284
- variant: "body2",
285
- component: "code",
286
- color: "primary",
287
- sx: {
288
- fontFamily: "monospace",
289
- backgroundColor: "background.default",
290
- px: 1,
291
- py: 0.5,
292
- borderRadius: 1,
293
- maxWidth: "250px",
294
- overflow: "hidden",
295
- textOverflow: "ellipsis"
296
- }
297
- },
298
- showKeyValue === key.key ? key.key : maskKey(key.key)
299
- ), /* @__PURE__ */ React2.createElement(
300
- IconButton,
301
- {
302
- size: "small",
303
- onClick: () => setShowKeyValue(showKeyValue === key.key ? null : key.key)
304
- },
305
- showKeyValue === key.key ? /* @__PURE__ */ React2.createElement(VisibilityOff, null) : /* @__PURE__ */ React2.createElement(Visibility, null)
306
- ), /* @__PURE__ */ React2.createElement(IconButton, { size: "small", onClick: () => copyToClipboard(key.key) }, /* @__PURE__ */ React2.createElement(ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ React2.createElement(TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ React2.createElement(TableCell, null, "$", key.spend?.toFixed(4) || "0.00"), /* @__PURE__ */ React2.createElement(TableCell, null, key.max_budget ? `$${key.max_budget}` : "-"), /* @__PURE__ */ React2.createElement(TableCell, null, key.tpm_limit || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ React2.createElement(Chip2, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ React2.createElement(Chip2, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React2.createElement(IconButton, { onClick: () => handleOpenEdit(key) }, /* @__PURE__ */ React2.createElement(Edit, { fontSize: "small" })), /* @__PURE__ */ React2.createElement(IconButton, { color: "error", onClick: () => onDeleteKey(key.key) }, /* @__PURE__ */ React2.createElement(Delete, null))))))))), /* @__PURE__ */ React2.createElement(Dialog, { open: generateModalOpen, onClose: handleCloseModal, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, newKeyValue ? /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ React2.createElement(
275
+ )), /* @__PURE__ */ React2.createElement(TableContainer, null, /* @__PURE__ */ React2.createElement(Table, null, /* @__PURE__ */ React2.createElement(TableHead, null, /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, null, "Alias"), /* @__PURE__ */ React2.createElement(TableCell, null, "Key ID"), /* @__PURE__ */ React2.createElement(TableCell, null, "Created"), /* @__PURE__ */ React2.createElement(TableCell, null, "Spend"), /* @__PURE__ */ React2.createElement(TableCell, null, "Budget"), /* @__PURE__ */ React2.createElement(TableCell, null, "TPM Limit"), /* @__PURE__ */ React2.createElement(TableCell, null, "Models"), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React2.createElement(TableBody, null, loading ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }))) : keys.length === 0 ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary" }, "No keys found"))) : keys.map((key) => {
276
+ const keyId = key.token ?? key.key;
277
+ return /* @__PURE__ */ React2.createElement(TableRow, { key: keyId }, /* @__PURE__ */ React2.createElement(TableCell, null, key.key_alias || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(
278
+ Typography2,
279
+ {
280
+ variant: "body2",
281
+ component: "code",
282
+ color: "text.secondary",
283
+ title: keyId,
284
+ sx: {
285
+ fontFamily: "monospace",
286
+ backgroundColor: "background.default",
287
+ px: 1,
288
+ py: 0.5,
289
+ borderRadius: 1
290
+ }
291
+ },
292
+ shortKeyId(keyId)
293
+ ), /* @__PURE__ */ React2.createElement(IconButton, { size: "small", onClick: () => copyToClipboard(keyId), title: "Copy Key ID" }, /* @__PURE__ */ React2.createElement(ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ React2.createElement(TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ React2.createElement(TableCell, null, "$", key.spend?.toFixed(4) || "0.00"), /* @__PURE__ */ React2.createElement(TableCell, null, key.max_budget ? `$${key.max_budget}` : "-"), /* @__PURE__ */ React2.createElement(TableCell, null, key.tpm_limit || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ React2.createElement(Chip2, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ React2.createElement(Chip2, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React2.createElement(IconButton, { onClick: () => handleOpenEdit(key) }, /* @__PURE__ */ React2.createElement(Edit, { fontSize: "small" })), /* @__PURE__ */ React2.createElement(IconButton, { color: "error", onClick: () => onDeleteKey(key.key) }, /* @__PURE__ */ React2.createElement(Delete, null))));
294
+ }))))), /* @__PURE__ */ React2.createElement(Dialog, { open: generateModalOpen, onClose: handleCloseModal, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, newKeyValue ? /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ React2.createElement(
307
295
  Box2,
308
296
  {
309
297
  display: "flex",
@@ -312,7 +300,9 @@ var init_KeysTable = __esm({
312
300
  mt: 2,
313
301
  p: 2,
314
302
  sx: {
315
- backgroundColor: "grey.100",
303
+ backgroundColor: "action.hover",
304
+ border: "1px solid",
305
+ borderColor: "divider",
316
306
  borderRadius: 1
317
307
  }
318
308
  },
@@ -332,6 +322,7 @@ var init_KeysTable = __esm({
332
322
  label: "Alias",
333
323
  value: formData.alias || "",
334
324
  onChange: (e) => setFormData({ ...formData, alias: e.target.value }),
325
+ required: true,
335
326
  fullWidth: true
336
327
  }
337
328
  ), /* @__PURE__ */ React2.createElement(
@@ -375,7 +366,7 @@ var init_KeysTable = __esm({
375
366
  value: selectedModels,
376
367
  onChange: (_e, selected) => setFormData({ ...formData, models: selected.map((m) => m.model_name) }),
377
368
  renderOption: (props, m) => /* @__PURE__ */ React2.createElement("li", { ...props }, m.model_name, m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F"),
378
- renderInput: (params) => /* @__PURE__ */ React2.createElement(TextField, { ...params, label: "Models", fullWidth: true })
369
+ renderInput: (params) => /* @__PURE__ */ React2.createElement(TextField, { ...params, label: "Models", helperText: "Leave empty to allow all models", fullWidth: true })
379
370
  }
380
371
  ), /* @__PURE__ */ React2.createElement(
381
372
  TextField,
@@ -384,6 +375,7 @@ var init_KeysTable = __esm({
384
375
  type: "number",
385
376
  value: formData.max_budget ?? "",
386
377
  onChange: (e) => setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
378
+ required: true,
387
379
  fullWidth: true
388
380
  }
389
381
  ), /* @__PURE__ */ React2.createElement(
@@ -958,7 +950,7 @@ init_TeamUsage();
958
950
 
959
951
  // src/components/LiteLLMHomeWidget.tsx
960
952
  init_api();
961
- import React7, { useState as useState5, useEffect as useEffect2 } from "react";
953
+ import React7, { useState as useState5, useEffect } from "react";
962
954
  import {
963
955
  Paper as Paper6,
964
956
  Box as Box6,
@@ -997,7 +989,7 @@ var LiteLLMHomeWidget = ({
997
989
  const [error, setError] = useState5(null);
998
990
  const [usage, setUsage] = useState5(null);
999
991
  const [keys, setKeys] = useState5([]);
1000
- useEffect2(() => {
992
+ useEffect(() => {
1001
993
  let cancelled = false;
1002
994
  setLoading(true);
1003
995
  setError(null);