@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/components/KeysTable.d.ts +7 -0
- package/dist/index.cjs.js +75 -18
- package/dist/index.cjs.js.map +2 -2
- package/dist/index.esm.js +75 -18
- package/dist/index.esm.js.map +2 -2
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
),
|
|
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:
|
|
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(
|
|
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,
|
|
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
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
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,
|