@acarmisc/backstage-plugin-litellm 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -50,7 +50,8 @@ var init_api = __esm({
50
50
  } catch {
51
51
  body = await response.text().catch(() => "");
52
52
  }
53
- throw new ApiError(`${response.status} ${response.statusText}`, response.status, body);
53
+ const message = (typeof body === "object" && body && typeof body.error === "string" ? body.error : void 0) ?? `${response.status} ${response.statusText}`;
54
+ throw new ApiError(message, response.status, body);
54
55
  }
55
56
  }
56
57
  async get(path, params) {
@@ -283,7 +284,7 @@ response = client.chat.completions.create(
283
284
  print(response.choices[0].message.content)`
284
285
  };
285
286
  }
286
- var maskKey, shortKeyId, formatDate, emptyForm, keyToEditForm, KeysTable, SnippetTabs;
287
+ var maskKey, generateDefaultAlias, shortKeyId, formatDate, emptyForm, keyToEditForm, KeysTable, SnippetTabs;
287
288
  var init_KeysTable = __esm({
288
289
  "src/components/KeysTable.tsx"() {
289
290
  "use strict";
@@ -293,6 +294,11 @@ var init_KeysTable = __esm({
293
294
  if (key.length <= 8) return "***";
294
295
  return `${key.slice(0, 4)}...${key.slice(-4)}`;
295
296
  };
297
+ generateDefaultAlias = (username) => {
298
+ const base = (username || "user").split("@")[0].toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "user";
299
+ const hash = Math.random().toString(36).slice(2, 8);
300
+ return `${base}-${hash}`;
301
+ };
296
302
  shortKeyId = (token) => {
297
303
  if (!token) return "-";
298
304
  if (token.length <= 16) return token;
@@ -326,6 +332,8 @@ var init_KeysTable = __esm({
326
332
  keys,
327
333
  models,
328
334
  teams,
335
+ username,
336
+ keyGenerationSettings,
329
337
  loading,
330
338
  onGenerateKey,
331
339
  onUpdateKey,
@@ -359,12 +367,23 @@ var init_KeysTable = __esm({
359
367
  (k) => (k.key_alias ?? "").toLowerCase().includes(q) || k.models?.some((m) => m.toLowerCase().includes(q))
360
368
  );
361
369
  }, [keys, filterText]);
362
- const selectedModels = models.filter((m) => (formData.models || []).includes(m.model_name));
370
+ const allowUnlimitedBudget = keyGenerationSettings?.allowUnlimitedBudget ?? false;
371
+ const teamRequired = keyGenerationSettings?.teamRequired ?? true;
363
372
  const selectedTeam = teams.find((t) => t.team_id === formData.team_id) ?? null;
373
+ const availableModels = useMemo(() => {
374
+ const teamModels = selectedTeam?.models;
375
+ if (teamModels && teamModels.length > 0) {
376
+ const allowed = new Set(teamModels);
377
+ return models.filter((m) => allowed.has(m.model_name));
378
+ }
379
+ return models;
380
+ }, [models, selectedTeam]);
381
+ const selectedModels = availableModels.filter((m) => (formData.models || []).includes(m.model_name));
364
382
  const editSelectedModels = models.filter((m) => (editForm.models || []).includes(m.model_name));
365
383
  const aliasError = !(formData.alias || "").trim();
384
+ const teamError = teamRequired && !formData.team_id;
366
385
  const budgetInvalid = !unlimitedBudget && (formData.max_budget === void 0 || formData.max_budget === null || formData.max_budget <= 0);
367
- const canGenerate = !aliasError && !budgetInvalid && !submitting;
386
+ const canGenerate = !aliasError && !teamError && !budgetInvalid && !submitting;
368
387
  const budgetEstimate = useMemo(() => {
369
388
  if (unlimitedBudget || budgetInvalid) return null;
370
389
  const inputCosts = selectedModels.map((m) => m.input_cost_per_token).filter((c) => typeof c === "number" && c > 0);
@@ -397,6 +416,10 @@ var init_KeysTable = __esm({
397
416
  setSubmitting(false);
398
417
  }
399
418
  };
419
+ const handleOpenGenerate = () => {
420
+ setFormData({ ...emptyForm(), alias: generateDefaultAlias(username) });
421
+ setGenerateModalOpen(true);
422
+ };
400
423
  const handleCloseModal = () => {
401
424
  setGenerateModalOpen(false);
402
425
  setNewKeyValue(null);
@@ -514,7 +537,7 @@ var init_KeysTable = __esm({
514
537
  variant: "contained",
515
538
  color: "primary",
516
539
  startIcon: /* @__PURE__ */ React2.createElement(Add, null),
517
- onClick: () => setGenerateModalOpen(true)
540
+ onClick: handleOpenGenerate
518
541
  },
519
542
  "Generate New Key"
520
543
  ))), /* @__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, "Expires"), /* @__PURE__ */ React2.createElement(TableCell, null, "Budget"), /* @__PURE__ */ React2.createElement(TableCell, null, "TPM / RPM"), /* @__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 }))) : filteredKeys.length === 0 ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center", sx: { py: 4 } }, filterText ? /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary" }, "No keys match filter") : /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary", gutterBottom: true }, "No keys yet \u2014 generate your first key to start calling models."), /* @__PURE__ */ React2.createElement(
@@ -524,7 +547,7 @@ var init_KeysTable = __esm({
524
547
  color: "primary",
525
548
  size: "small",
526
549
  startIcon: /* @__PURE__ */ React2.createElement(Add, null),
527
- onClick: () => setGenerateModalOpen(true)
550
+ onClick: handleOpenGenerate
528
551
  },
529
552
  "Generate Your First Key"
530
553
  )))) : filteredKeys.map((key) => {
@@ -614,36 +637,50 @@ var init_KeysTable = __esm({
614
637
  /* @__PURE__ */ React2.createElement(MenuItem, { value: "30d" }, "30 Days"),
615
638
  /* @__PURE__ */ React2.createElement(MenuItem, { value: "90d" }, "90 Days"),
616
639
  /* @__PURE__ */ React2.createElement(MenuItem, { value: "1y" }, "1 Year")
617
- ), teams.length > 0 && /* @__PURE__ */ React2.createElement(
640
+ ), teams.length > 0 ? /* @__PURE__ */ React2.createElement(
618
641
  Autocomplete,
619
642
  {
620
643
  options: teams,
621
644
  getOptionLabel: (t) => t.team_alias || t.team_id,
622
645
  value: selectedTeam,
623
- onChange: (_e, team) => setFormData({ ...formData, team_id: team?.team_id }),
646
+ onChange: (_e, team) => {
647
+ const teamModels = team?.models;
648
+ const restrictedModels = teamModels && teamModels.length > 0 ? (formData.models || []).filter((m) => teamModels.includes(m)) : formData.models;
649
+ setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
650
+ },
624
651
  renderInput: (params) => /* @__PURE__ */ React2.createElement(
625
652
  TextField,
626
653
  {
627
654
  ...params,
628
655
  label: "Team",
629
- helperText: "Optional: bind this key to a specific team for scoped access",
656
+ error: teamError,
657
+ helperText: teamError ? "Team is required" : teamRequired ? "Bind this key to a team for scoped access" : "Optional: bind this key to a specific team for scoped access",
658
+ required: teamRequired,
630
659
  fullWidth: true
631
660
  }
632
661
  )
633
662
  }
634
- ), models.length > 0 && /* @__PURE__ */ React2.createElement(
663
+ ) : teamRequired ? /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.") : null, availableModels.length > 0 && /* @__PURE__ */ React2.createElement(
635
664
  Autocomplete,
636
665
  {
637
666
  multiple: true,
638
- options: models,
667
+ options: availableModels,
639
668
  groupBy: (m) => m.mode || "other",
640
669
  getOptionLabel: (m) => m.model_name,
641
670
  value: selectedModels,
642
671
  onChange: (_e, selected) => setFormData({ ...formData, models: selected.map((m) => m.model_name) }),
643
672
  renderOption: (props, m) => /* @__PURE__ */ React2.createElement("li", { ...props }, modelOption(m)),
644
- renderInput: (params) => /* @__PURE__ */ React2.createElement(TextField, { ...params, label: "Models", helperText: "Leave empty to allow all models", fullWidth: true })
673
+ renderInput: (params) => /* @__PURE__ */ React2.createElement(
674
+ TextField,
675
+ {
676
+ ...params,
677
+ label: "Models",
678
+ helperText: selectedTeam ? "Leave empty to allow all models available to this team" : "Leave empty to allow all models",
679
+ fullWidth: true
680
+ }
681
+ )
645
682
  }
646
- ), /* @__PURE__ */ React2.createElement(
683
+ ), allowUnlimitedBudget && /* @__PURE__ */ React2.createElement(
647
684
  FormControlLabel,
648
685
  {
649
686
  control: /* @__PURE__ */ React2.createElement(
@@ -682,6 +719,7 @@ var init_KeysTable = __esm({
682
719
  type: "number",
683
720
  value: formData.tpm_limit ?? "",
684
721
  onChange: (e) => setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
722
+ helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
685
723
  fullWidth: true
686
724
  }
687
725
  ), /* @__PURE__ */ React2.createElement(
@@ -691,6 +729,7 @@ var init_KeysTable = __esm({
691
729
  type: "number",
692
730
  value: formData.rpm_limit ?? "",
693
731
  onChange: (e) => setFormData({ ...formData, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
732
+ helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
694
733
  fullWidth: true
695
734
  }
696
735
  ))), /* @__PURE__ */ React2.createElement(DialogActions, null, newKeyValue ? /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseModal, variant: "contained", color: "success" }, "Done") : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseModal }, "Cancel"), /* @__PURE__ */ React2.createElement(
@@ -738,6 +777,7 @@ var init_KeysTable = __esm({
738
777
  type: "number",
739
778
  value: editForm.tpm_limit ?? "",
740
779
  onChange: (e) => setEditForm({ ...editForm, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
780
+ helperText: "Max tokens per minute this key can consume across all models. Leave blank for no limit.",
741
781
  fullWidth: true
742
782
  }
743
783
  ), /* @__PURE__ */ React2.createElement(
@@ -747,6 +787,7 @@ var init_KeysTable = __esm({
747
787
  type: "number",
748
788
  value: editForm.rpm_limit ?? "",
749
789
  onChange: (e) => setEditForm({ ...editForm, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
790
+ helperText: "Max requests per minute this key can make across all models. Leave blank for no limit.",
750
791
  fullWidth: true
751
792
  }
752
793
  ), /* @__PURE__ */ React2.createElement(Box2, { mt: 1, pt: 2, borderTop: "1px solid", sx: { borderColor: "divider" } }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "caption", color: "text.secondary", display: "block", mb: 1 }, "Danger Zone"), !resetSpendConfirm ? /* @__PURE__ */ React2.createElement(
@@ -1170,7 +1211,15 @@ var init_TeamUsage = __esm({
1170
1211
  color: isOver ? "error" : isNear ? "warning" : "primary",
1171
1212
  sx: { height: 6, borderRadius: 1 }
1172
1213
  }
1173
- )), /* @__PURE__ */ React4.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React4.createElement(Box4, { mt: 2 }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React4.createElement(Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ React4.createElement(Stack, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ React4.createElement(Stat, { label: "TPM limit", value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ React4.createElement(Stat, { label: "RPM limit", value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ React4.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ React4.createElement(Divider, { sx: { mb: 2 } }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ React4.createElement(Chip4, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ React4.createElement(Divider, { sx: { mb: 2 } }), usageLoading ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ React4.createElement(CircularProgress2, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React4.createElement(Box4, { height: 160, mb: 2 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart2, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid2, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis2, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ React4.createElement(YAxis2, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip2, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React4.createElement(Area2, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ React4.createElement(Divider, { sx: { mb: 2 } })) : null, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React4.createElement(Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ React4.createElement(TableContainer3, null, /* @__PURE__ */ React4.createElement(Table3, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead3, null, /* @__PURE__ */ React4.createElement(TableRow3, null, /* @__PURE__ */ React4.createElement(TableCell3, null, "User"), /* @__PURE__ */ React4.createElement(TableCell3, null, "Role"))), /* @__PURE__ */ React4.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React4.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React4.createElement(TableCell3, { sx: { fontFamily: "monospace", fontSize: 12 } }, m.user_id), /* @__PURE__ */ React4.createElement(TableCell3, null, /* @__PURE__ */ React4.createElement(
1214
+ )), /* @__PURE__ */ React4.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React4.createElement(Box4, { mt: 2 }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React4.createElement(Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ React4.createElement(Stack, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ React4.createElement(Stat, { label: "TPM limit", value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ React4.createElement(Stat, { label: "RPM limit", value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ React4.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ React4.createElement(Divider, { sx: { mb: 2 } }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ React4.createElement(Chip4, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ React4.createElement(Divider, { sx: { mb: 2 } }), usageLoading ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ React4.createElement(CircularProgress2, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React4.createElement(Box4, { height: 160, mb: 2 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart2, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid2, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis2, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ React4.createElement(YAxis2, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip2, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React4.createElement(Area2, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ React4.createElement(Divider, { sx: { mb: 2 } })) : null, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React4.createElement(Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ React4.createElement(TableContainer3, null, /* @__PURE__ */ React4.createElement(Table3, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead3, null, /* @__PURE__ */ React4.createElement(TableRow3, null, /* @__PURE__ */ React4.createElement(TableCell3, null, "User"), /* @__PURE__ */ React4.createElement(TableCell3, null, "Role"))), /* @__PURE__ */ React4.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React4.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React4.createElement(TableCell3, null, m.user_email ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2" }, m.user_email), /* @__PURE__ */ React4.createElement(
1215
+ Typography4,
1216
+ {
1217
+ variant: "caption",
1218
+ color: "text.secondary",
1219
+ sx: { fontFamily: "monospace" }
1220
+ },
1221
+ m.user_id
1222
+ )) : /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2", sx: { fontFamily: "monospace" } }, m.user_id)), /* @__PURE__ */ React4.createElement(TableCell3, null, /* @__PURE__ */ React4.createElement(
1174
1223
  Chip4,
1175
1224
  {
1176
1225
  label: m.role,
@@ -1401,6 +1450,7 @@ var init_LiteLLMPage = __esm({
1401
1450
  () => api.getTeams().catch(() => []),
1402
1451
  [api]
1403
1452
  );
1453
+ const { value: liteLlmConfig } = useAsync2(() => api.getConfig(), [api]);
1404
1454
  const teams = useMemo3(() => {
1405
1455
  if (!allTeams?.length) return [];
1406
1456
  if (!userInfo) return allTeams;
@@ -1452,10 +1502,15 @@ var init_LiteLLMPage = __esm({
1452
1502
  }, [allModels, userInfo, teams]);
1453
1503
  const handleGenerateKey = useCallback2(
1454
1504
  async (request) => {
1455
- const response = await api.generateKey(request);
1456
- setSnackbar({ message: "Key generated successfully", severity: "success" });
1457
- refreshKeys();
1458
- return response;
1505
+ try {
1506
+ const response = await api.generateKey(request);
1507
+ setSnackbar({ message: "Key generated successfully", severity: "success" });
1508
+ refreshKeys();
1509
+ return response;
1510
+ } catch (e) {
1511
+ setSnackbar({ message: `Failed to generate key: ${e.message}`, severity: "error" });
1512
+ throw e;
1513
+ }
1459
1514
  },
1460
1515
  [api, refreshKeys]
1461
1516
  );
@@ -1574,6 +1629,8 @@ var init_LiteLLMPage = __esm({
1574
1629
  keys: keys ?? [],
1575
1630
  models: allowedModels,
1576
1631
  teams: teams ?? [],
1632
+ username: userInfo.user_id,
1633
+ keyGenerationSettings: liteLlmConfig?.keyGeneration,
1577
1634
  loading: keysLoading || modelsLoading,
1578
1635
  onGenerateKey: handleGenerateKey,
1579
1636
  onUpdateKey: handleUpdateKey,