@acarmisc/backstage-plugin-litellm 0.31.0 → 0.32.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/ManageTeamDialog.d.ts +6 -0
- package/dist/components/TeamUsage.d.ts +5 -0
- package/dist/index.cjs.js +193 -98
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.esm.js +206 -111
- package/dist/index.esm.js.map +4 -4
- package/dist/teamMemberManager.d.ts +9 -0
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -2555,9 +2555,22 @@ var init_KeyFormDialog = __esm({
|
|
|
2555
2555
|
setCloseWithoutCopyConfirm(false);
|
|
2556
2556
|
setTouched({});
|
|
2557
2557
|
setSubmitAttempted(false);
|
|
2558
|
-
onGetConfig().then(setConfig).catch(() => setConfig(null));
|
|
2559
2558
|
}
|
|
2560
|
-
}, [open, isCreate, keyToEdit, username, keyGenerationSettings?.allowUnlimitedBudget
|
|
2559
|
+
}, [open, isCreate, keyToEdit, username, keyGenerationSettings?.allowUnlimitedBudget]);
|
|
2560
|
+
const onGetConfigRef = useRef3(onGetConfig);
|
|
2561
|
+
onGetConfigRef.current = onGetConfig;
|
|
2562
|
+
useEffect3(() => {
|
|
2563
|
+
if (!open) return void 0;
|
|
2564
|
+
let cancelled = false;
|
|
2565
|
+
onGetConfigRef.current().then((c) => {
|
|
2566
|
+
if (!cancelled) setConfig(c);
|
|
2567
|
+
}).catch(() => {
|
|
2568
|
+
if (!cancelled) setConfig(null);
|
|
2569
|
+
});
|
|
2570
|
+
return () => {
|
|
2571
|
+
cancelled = true;
|
|
2572
|
+
};
|
|
2573
|
+
}, [open]);
|
|
2561
2574
|
const allowUnlimitedBudget = keyGenerationSettings?.allowUnlimitedBudget ?? false;
|
|
2562
2575
|
const teamRequired = keyGenerationSettings?.teamRequired ?? true;
|
|
2563
2576
|
const formData = isCreate ? generateForm : editFormState;
|
|
@@ -3111,6 +3124,7 @@ import Box10 from "@mui/material/Box";
|
|
|
3111
3124
|
import Divider2 from "@mui/material/Divider";
|
|
3112
3125
|
import Typography8 from "@mui/material/Typography";
|
|
3113
3126
|
import IconButton4 from "@mui/material/IconButton";
|
|
3127
|
+
import Tooltip4 from "@mui/material/Tooltip";
|
|
3114
3128
|
import Paper4 from "@mui/material/Paper";
|
|
3115
3129
|
import Table2 from "@mui/material/Table";
|
|
3116
3130
|
import TableBody2 from "@mui/material/TableBody";
|
|
@@ -3153,7 +3167,10 @@ var init_ManageTeamDialog = __esm({
|
|
|
3153
3167
|
onSaveKnowledgeBases,
|
|
3154
3168
|
canManageMcpServers,
|
|
3155
3169
|
mcpServers,
|
|
3156
|
-
onSaveMcpServers
|
|
3170
|
+
onSaveMcpServers,
|
|
3171
|
+
membersOnly,
|
|
3172
|
+
currentUserId,
|
|
3173
|
+
memberManagerRoles
|
|
3157
3174
|
}) => {
|
|
3158
3175
|
const [alias, setAlias] = useState6("");
|
|
3159
3176
|
const [models, setModels] = useState6([]);
|
|
@@ -3178,7 +3195,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3178
3195
|
const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
|
|
3179
3196
|
const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
|
|
3180
3197
|
const members = useMemo4(() => team?.members_with_roles ?? [], [team]);
|
|
3181
|
-
const showMembers = mode === "edit" && !!canManageMembers;
|
|
3198
|
+
const showMembers = mode === "edit" && !!canManageMembers || membersOnly;
|
|
3182
3199
|
const catalogApi = useApi3(catalogApiRef);
|
|
3183
3200
|
const [searchUsers, setSearchUsers] = useState6([]);
|
|
3184
3201
|
const [usersLoading, setUsersLoading] = useState6(false);
|
|
@@ -3327,7 +3344,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3327
3344
|
const handleAddMember = async () => {
|
|
3328
3345
|
if (!onAddMember || !memberRef.trim()) return;
|
|
3329
3346
|
setMemberError(null);
|
|
3330
|
-
const budget = memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3347
|
+
const budget = !membersOnly && memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3331
3348
|
if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
|
|
3332
3349
|
setMemberError("Max budget in team must be a positive number");
|
|
3333
3350
|
return;
|
|
@@ -3388,72 +3405,95 @@ var init_ManageTeamDialog = __esm({
|
|
|
3388
3405
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
3389
3406
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
3390
3407
|
const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
|
|
3408
|
+
const isRemovalDisabled = (memberUserId, memberRole) => {
|
|
3409
|
+
if (!membersOnly) return { disabled: false };
|
|
3410
|
+
if (memberUserId === currentUserId) {
|
|
3411
|
+
return { disabled: true, reason: "You can't remove yourself" };
|
|
3412
|
+
}
|
|
3413
|
+
if (memberManagerRoles?.includes(memberRole)) {
|
|
3414
|
+
return { disabled: true, reason: `Only platform admins can remove a team ${memberRole}` };
|
|
3415
|
+
}
|
|
3416
|
+
return { disabled: false };
|
|
3417
|
+
};
|
|
3418
|
+
const getDialogTitle = () => {
|
|
3419
|
+
if (membersOnly) {
|
|
3420
|
+
return `Manage members \u2014 ${team?.team_alias || "Untitled team"}`;
|
|
3421
|
+
}
|
|
3422
|
+
return mode === "create" ? "Create Team" : "Edit Team";
|
|
3423
|
+
};
|
|
3391
3424
|
return /* @__PURE__ */ jsxs10(Dialog3, { open, onClose, maxWidth: "sm", fullWidth: true, children: [
|
|
3392
|
-
/* @__PURE__ */ jsx11(DialogTitle3, { children:
|
|
3425
|
+
/* @__PURE__ */ jsx11(DialogTitle3, { children: getDialogTitle() }),
|
|
3393
3426
|
/* @__PURE__ */ jsxs10(DialogContent3, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
3394
3427
|
error && /* @__PURE__ */ jsx11(Alert2, { severity: "error", children: error }),
|
|
3395
|
-
/* @__PURE__ */
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3428
|
+
membersOnly && /* @__PURE__ */ jsxs10(Alert2, { severity: "info", children: [
|
|
3429
|
+
"As a team ",
|
|
3430
|
+
team?.members_with_roles?.find((m) => m.user_id === currentUserId)?.role || "member",
|
|
3431
|
+
" you can add or remove members. Budgets and models are managed by your platform admins."
|
|
3432
|
+
] }),
|
|
3433
|
+
!membersOnly && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
3434
|
+
/* @__PURE__ */ jsx11(
|
|
3435
|
+
TextField3,
|
|
3436
|
+
{
|
|
3437
|
+
label: "Team Alias",
|
|
3438
|
+
value: alias,
|
|
3439
|
+
onChange: (e) => setAlias(e.target.value),
|
|
3440
|
+
disabled: submitting,
|
|
3441
|
+
fullWidth: true
|
|
3442
|
+
}
|
|
3443
|
+
),
|
|
3444
|
+
/* @__PURE__ */ jsx11(
|
|
3445
|
+
Autocomplete2,
|
|
3446
|
+
{
|
|
3447
|
+
multiple: true,
|
|
3448
|
+
options: modelNames,
|
|
3449
|
+
value: models,
|
|
3450
|
+
onChange: (_, newValue) => setModels(newValue),
|
|
3451
|
+
disabled: submitting,
|
|
3452
|
+
renderInput: (params) => /* @__PURE__ */ jsx11(
|
|
3453
|
+
TextField3,
|
|
3454
|
+
{
|
|
3455
|
+
...params,
|
|
3456
|
+
label: "Models",
|
|
3457
|
+
required: true,
|
|
3458
|
+
helperText: "At least one model is required here \u2014 this delegated flow only grants access to models your admin has allow-listed for team creation, not every proxy model. (Teams with no model restriction shown elsewhere were configured directly in LiteLLM.)"
|
|
3459
|
+
}
|
|
3460
|
+
)
|
|
3461
|
+
}
|
|
3462
|
+
),
|
|
3463
|
+
/* @__PURE__ */ jsx11(
|
|
3464
|
+
TextField3,
|
|
3465
|
+
{
|
|
3466
|
+
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
3467
|
+
type: "number",
|
|
3468
|
+
value: maxBudget,
|
|
3469
|
+
onChange: (e) => setMaxBudget(e.target.value),
|
|
3470
|
+
disabled: submitting || unlimited,
|
|
3471
|
+
inputProps: { min: 0, max: maxBudgetCeiling },
|
|
3472
|
+
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
3473
|
+
fullWidth: true
|
|
3474
|
+
}
|
|
3475
|
+
),
|
|
3476
|
+
allowUnlimitedBudget && /* @__PURE__ */ jsx11(
|
|
3477
|
+
FormControlLabel2,
|
|
3478
|
+
{
|
|
3479
|
+
control: /* @__PURE__ */ jsx11(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
3480
|
+
label: "Unlimited Budget"
|
|
3481
|
+
}
|
|
3482
|
+
),
|
|
3483
|
+
/* @__PURE__ */ jsx11(
|
|
3484
|
+
TextField3,
|
|
3485
|
+
{
|
|
3486
|
+
select: true,
|
|
3487
|
+
label: "Budget Duration",
|
|
3488
|
+
value: budgetDuration,
|
|
3489
|
+
onChange: (e) => setBudgetDuration(e.target.value),
|
|
3490
|
+
disabled: submitting || unlimited,
|
|
3491
|
+
helperText: "Spend-reset period for the team budget",
|
|
3492
|
+
fullWidth: true,
|
|
3493
|
+
children: durationOptions.map((o) => /* @__PURE__ */ jsx11(MenuItem2, { value: o.value, children: o.label }, o.value))
|
|
3494
|
+
}
|
|
3495
|
+
)
|
|
3496
|
+
] }),
|
|
3457
3497
|
showMembers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3458
3498
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3459
3499
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Members" }),
|
|
@@ -3485,17 +3525,20 @@ var init_ManageTeamDialog = __esm({
|
|
|
3485
3525
|
}
|
|
3486
3526
|
) }),
|
|
3487
3527
|
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children: /* @__PURE__ */ jsx11(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
|
|
3488
|
-
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children:
|
|
3489
|
-
|
|
3490
|
-
{
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3528
|
+
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children: (() => {
|
|
3529
|
+
const { disabled, reason } = isRemovalDisabled(m.user_id, m.role);
|
|
3530
|
+
return /* @__PURE__ */ jsx11(Tooltip4, { title: reason ?? "", children: /* @__PURE__ */ jsx11("span", { children: /* @__PURE__ */ jsx11(
|
|
3531
|
+
IconButton4,
|
|
3532
|
+
{
|
|
3533
|
+
edge: "end",
|
|
3534
|
+
size: "small",
|
|
3535
|
+
"aria-label": `remove ${m.user_id}`,
|
|
3536
|
+
disabled: memberBusy || disabled,
|
|
3537
|
+
onClick: () => handleRemoveMember(m.user_id),
|
|
3538
|
+
children: /* @__PURE__ */ jsx11(DeleteIcon, { fontSize: "small" })
|
|
3539
|
+
}
|
|
3540
|
+
) }) });
|
|
3541
|
+
})() })
|
|
3499
3542
|
] }, m.user_id)) })
|
|
3500
3543
|
] }) }),
|
|
3501
3544
|
/* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
|
|
@@ -3543,7 +3586,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3543
3586
|
)
|
|
3544
3587
|
}
|
|
3545
3588
|
),
|
|
3546
|
-
/* @__PURE__ */ jsx11(
|
|
3589
|
+
!membersOnly && /* @__PURE__ */ jsx11(
|
|
3547
3590
|
TextField3,
|
|
3548
3591
|
{
|
|
3549
3592
|
label: "Max budget in team ($)",
|
|
@@ -3567,7 +3610,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3567
3610
|
)
|
|
3568
3611
|
] })
|
|
3569
3612
|
] }),
|
|
3570
|
-
showKnowledgeBases && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3613
|
+
!membersOnly && showKnowledgeBases && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3571
3614
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3572
3615
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Knowledge Bases" }),
|
|
3573
3616
|
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Attaching a knowledge base exposes its documents to every key in this team." }),
|
|
@@ -3594,7 +3637,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3594
3637
|
}
|
|
3595
3638
|
) })
|
|
3596
3639
|
] }),
|
|
3597
|
-
showMcpServers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3640
|
+
!membersOnly && showMcpServers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3598
3641
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3599
3642
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "MCP Servers" }),
|
|
3600
3643
|
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Attaching an MCP server lets every key in this team invoke that server's tools through the model \u2014 including any side effects those tools have." }),
|
|
@@ -3623,8 +3666,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
3623
3666
|
] })
|
|
3624
3667
|
] }),
|
|
3625
3668
|
/* @__PURE__ */ jsxs10(DialogActions3, { children: [
|
|
3626
|
-
/* @__PURE__ */ jsx11(Button4, { onClick: onClose, disabled: submitting, children: "Cancel" }),
|
|
3627
|
-
/* @__PURE__ */ jsx11(Button4, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : "Save" })
|
|
3669
|
+
/* @__PURE__ */ jsx11(Button4, { onClick: onClose, disabled: submitting, children: membersOnly ? "Done" : "Cancel" }),
|
|
3670
|
+
!membersOnly && /* @__PURE__ */ jsx11(Button4, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : "Save" })
|
|
3628
3671
|
] })
|
|
3629
3672
|
] });
|
|
3630
3673
|
};
|
|
@@ -3835,7 +3878,7 @@ import {
|
|
|
3835
3878
|
XAxis,
|
|
3836
3879
|
YAxis,
|
|
3837
3880
|
CartesianGrid,
|
|
3838
|
-
Tooltip as
|
|
3881
|
+
Tooltip as Tooltip5,
|
|
3839
3882
|
ResponsiveContainer,
|
|
3840
3883
|
Legend,
|
|
3841
3884
|
ReferenceLine,
|
|
@@ -4172,7 +4215,7 @@ var init_UsageStats = __esm({
|
|
|
4172
4215
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4173
4216
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }),
|
|
4174
4217
|
/* @__PURE__ */ jsx12(
|
|
4175
|
-
|
|
4218
|
+
Tooltip5,
|
|
4176
4219
|
{
|
|
4177
4220
|
cursor: chart.cursor,
|
|
4178
4221
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
|
|
@@ -4198,7 +4241,7 @@ var init_UsageStats = __esm({
|
|
|
4198
4241
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid }),
|
|
4199
4242
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4200
4243
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }),
|
|
4201
|
-
/* @__PURE__ */ jsx12(
|
|
4244
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt }) }),
|
|
4202
4245
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4203
4246
|
/* @__PURE__ */ jsx12(Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }),
|
|
4204
4247
|
/* @__PURE__ */ jsx12(Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })
|
|
@@ -4207,7 +4250,7 @@ var init_UsageStats = __esm({
|
|
|
4207
4250
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid }),
|
|
4208
4251
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4209
4252
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }),
|
|
4210
|
-
/* @__PURE__ */ jsx12(
|
|
4253
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt }) }),
|
|
4211
4254
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4212
4255
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }),
|
|
4213
4256
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })
|
|
@@ -4217,7 +4260,7 @@ var init_UsageStats = __esm({
|
|
|
4217
4260
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4218
4261
|
/* @__PURE__ */ jsx12(YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }),
|
|
4219
4262
|
/* @__PURE__ */ jsx12(
|
|
4220
|
-
|
|
4263
|
+
Tooltip5,
|
|
4221
4264
|
{
|
|
4222
4265
|
cursor: chart.cursor,
|
|
4223
4266
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: (v) => `${v}%` })
|
|
@@ -4254,7 +4297,7 @@ var init_UsageStats = __esm({
|
|
|
4254
4297
|
}
|
|
4255
4298
|
),
|
|
4256
4299
|
/* @__PURE__ */ jsx12(
|
|
4257
|
-
|
|
4300
|
+
Tooltip5,
|
|
4258
4301
|
{
|
|
4259
4302
|
cursor: chart.cursor,
|
|
4260
4303
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd })
|
|
@@ -4296,7 +4339,7 @@ var init_UsageStats = __esm({
|
|
|
4296
4339
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4297
4340
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }),
|
|
4298
4341
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4299
|
-
/* @__PURE__ */ jsx12(
|
|
4342
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }),
|
|
4300
4343
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "spend", name: "Spend", radius: [0, 3, 3, 0], barSize: 14, children: topModelSpendBars.map((r) => /* @__PURE__ */ jsx12(Cell, { fill: chartColor(r.model) }, r.model)) })
|
|
4301
4344
|
] }) })
|
|
4302
4345
|
}
|
|
@@ -4311,7 +4354,7 @@ var init_UsageStats = __esm({
|
|
|
4311
4354
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4312
4355
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }),
|
|
4313
4356
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4314
|
-
/* @__PURE__ */ jsx12(
|
|
4357
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }),
|
|
4315
4358
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4316
4359
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }),
|
|
4317
4360
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })
|
|
@@ -4343,7 +4386,7 @@ var init_UsageStats = __esm({
|
|
|
4343
4386
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4344
4387
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }),
|
|
4345
4388
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4346
|
-
/* @__PURE__ */ jsx12(
|
|
4389
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }),
|
|
4347
4390
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })
|
|
4348
4391
|
] }) })
|
|
4349
4392
|
}
|
|
@@ -4878,14 +4921,15 @@ import IconButton6 from "@mui/material/IconButton";
|
|
|
4878
4921
|
import Button6 from "@mui/material/Button";
|
|
4879
4922
|
import CircularProgress4 from "@mui/material/CircularProgress";
|
|
4880
4923
|
import { alpha as alpha10 } from "@mui/material/styles";
|
|
4881
|
-
import
|
|
4924
|
+
import MuiTooltip from "@mui/material/Tooltip";
|
|
4925
|
+
import { ExpandMore as ExpandMore3, Group, Add as Add2, LockOutlined, GroupAddOutlined } from "@mui/icons-material";
|
|
4882
4926
|
import {
|
|
4883
4927
|
AreaChart as AreaChart2,
|
|
4884
4928
|
Area as Area2,
|
|
4885
4929
|
XAxis as XAxis2,
|
|
4886
4930
|
YAxis as YAxis2,
|
|
4887
4931
|
CartesianGrid as CartesianGrid2,
|
|
4888
|
-
Tooltip as
|
|
4932
|
+
Tooltip as Tooltip6,
|
|
4889
4933
|
ResponsiveContainer as ResponsiveContainer2
|
|
4890
4934
|
} from "recharts";
|
|
4891
4935
|
import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
@@ -4909,7 +4953,7 @@ var init_TeamUsage = __esm({
|
|
|
4909
4953
|
"use strict";
|
|
4910
4954
|
init_ui();
|
|
4911
4955
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
4912
|
-
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, onExpand }) => {
|
|
4956
|
+
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand }) => {
|
|
4913
4957
|
const [expanded, setExpanded] = useState9(false);
|
|
4914
4958
|
const chart = useChartTheme();
|
|
4915
4959
|
const handleExpandChange = (newExpanded) => {
|
|
@@ -4946,7 +4990,7 @@ var init_TeamUsage = __esm({
|
|
|
4946
4990
|
/* @__PURE__ */ jsx15(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4947
4991
|
/* @__PURE__ */ jsx15(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }),
|
|
4948
4992
|
/* @__PURE__ */ jsx15(
|
|
4949
|
-
|
|
4993
|
+
Tooltip6,
|
|
4950
4994
|
{
|
|
4951
4995
|
cursor: chart.cursor,
|
|
4952
4996
|
content: /* @__PURE__ */ jsx15(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
@@ -5019,6 +5063,17 @@ var init_TeamUsage = __esm({
|
|
|
5019
5063
|
children: "Edit"
|
|
5020
5064
|
}
|
|
5021
5065
|
),
|
|
5066
|
+
canManageMembers && onManageMembers && !canManage && /* @__PURE__ */ jsx15(MuiTooltip, { title: "Add or remove members of this team", children: /* @__PURE__ */ jsx15(
|
|
5067
|
+
Button6,
|
|
5068
|
+
{
|
|
5069
|
+
size: "small",
|
|
5070
|
+
variant: "text",
|
|
5071
|
+
startIcon: /* @__PURE__ */ jsx15(GroupAddOutlined, {}),
|
|
5072
|
+
onClick: () => onManageMembers(team),
|
|
5073
|
+
sx: { textTransform: "none" },
|
|
5074
|
+
children: "Manage members"
|
|
5075
|
+
}
|
|
5076
|
+
) }),
|
|
5022
5077
|
/* @__PURE__ */ jsx15(
|
|
5023
5078
|
IconButton6,
|
|
5024
5079
|
{
|
|
@@ -5149,9 +5204,23 @@ var init_TeamUsage = __esm({
|
|
|
5149
5204
|
onEditTeam,
|
|
5150
5205
|
canCreate,
|
|
5151
5206
|
onCreateTeam,
|
|
5152
|
-
onTeamExpand
|
|
5207
|
+
onTeamExpand,
|
|
5208
|
+
canManageMembers,
|
|
5209
|
+
onManageMembers,
|
|
5210
|
+
readOnly
|
|
5153
5211
|
}) => {
|
|
5154
|
-
const
|
|
5212
|
+
const readOnlyBadge = /* @__PURE__ */ jsx15(MuiTooltip, { title: "Teams and memberships are managed in your identity provider. Changes made here would be overwritten, so editing is disabled.", children: /* @__PURE__ */ jsxs14(
|
|
5213
|
+
Box14,
|
|
5214
|
+
{
|
|
5215
|
+
component: "span",
|
|
5216
|
+
sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "text.secondary", fontSize: 12.5 },
|
|
5217
|
+
children: [
|
|
5218
|
+
/* @__PURE__ */ jsx15(LockOutlined, { sx: { fontSize: 15 } }),
|
|
5219
|
+
"Synced from identity provider"
|
|
5220
|
+
]
|
|
5221
|
+
}
|
|
5222
|
+
) });
|
|
5223
|
+
const createButton = canCreate && onCreateTeam ? /* @__PURE__ */ jsx15(
|
|
5155
5224
|
Button6,
|
|
5156
5225
|
{
|
|
5157
5226
|
variant: "contained",
|
|
@@ -5162,6 +5231,7 @@ var init_TeamUsage = __esm({
|
|
|
5162
5231
|
children: "Create Team"
|
|
5163
5232
|
}
|
|
5164
5233
|
) : void 0;
|
|
5234
|
+
const createAction = readOnly ? readOnlyBadge : createButton;
|
|
5165
5235
|
if (loading) {
|
|
5166
5236
|
return /* @__PURE__ */ jsxs14(SectionCard, { title: "Teams", actions: createAction, children: [
|
|
5167
5237
|
/* @__PURE__ */ jsx15(Skeleton5, { variant: "rounded", height: 120, sx: { mb: 2 } }),
|
|
@@ -5191,6 +5261,8 @@ var init_TeamUsage = __esm({
|
|
|
5191
5261
|
usageLoading: getTeamUsageLoading(team.team_id),
|
|
5192
5262
|
canManage,
|
|
5193
5263
|
onEditTeam,
|
|
5264
|
+
canManageMembers: canManageMembers?.(team),
|
|
5265
|
+
onManageMembers,
|
|
5194
5266
|
onExpand: onTeamExpand
|
|
5195
5267
|
},
|
|
5196
5268
|
team.team_id
|
|
@@ -5220,7 +5292,7 @@ import TextField5 from "@mui/material/TextField";
|
|
|
5220
5292
|
import InputAdornment3 from "@mui/material/InputAdornment";
|
|
5221
5293
|
import { Search as Search2 } from "@mui/icons-material";
|
|
5222
5294
|
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
|
5223
|
-
import
|
|
5295
|
+
import Tooltip7 from "@mui/material/Tooltip";
|
|
5224
5296
|
import Snackbar from "@mui/material/Snackbar";
|
|
5225
5297
|
import Alert4 from "@mui/material/Alert";
|
|
5226
5298
|
import { alpha as alpha11, useTheme as useTheme3 } from "@mui/material/styles";
|
|
@@ -5404,7 +5476,7 @@ var init_ModelsTable = __esm({
|
|
|
5404
5476
|
] }) }),
|
|
5405
5477
|
/* @__PURE__ */ jsx16(TableBody5, { children: filteredModels.map((m) => /* @__PURE__ */ jsxs15(TableRow5, { hover: true, children: [
|
|
5406
5478
|
/* @__PURE__ */ jsx16(TableCell5, { children: /* @__PURE__ */ jsxs15(Box15, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
|
|
5407
|
-
/* @__PURE__ */ jsx16(
|
|
5479
|
+
/* @__PURE__ */ jsx16(Tooltip7, { title: m.model_name, placement: "top", children: /* @__PURE__ */ jsx16(
|
|
5408
5480
|
"span",
|
|
5409
5481
|
{
|
|
5410
5482
|
role: "button",
|
|
@@ -5416,7 +5488,7 @@ var init_ModelsTable = __esm({
|
|
|
5416
5488
|
children: /* @__PURE__ */ jsx16(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
5417
5489
|
}
|
|
5418
5490
|
) }),
|
|
5419
|
-
/* @__PURE__ */ jsx16(
|
|
5491
|
+
/* @__PURE__ */ jsx16(Tooltip7, { title: "Copy model ID", children: /* @__PURE__ */ jsx16(
|
|
5420
5492
|
Box15,
|
|
5421
5493
|
{
|
|
5422
5494
|
component: "span",
|
|
@@ -5839,6 +5911,19 @@ var init_feedback = __esm({
|
|
|
5839
5911
|
}
|
|
5840
5912
|
});
|
|
5841
5913
|
|
|
5914
|
+
// src/teamMemberManager.ts
|
|
5915
|
+
function isTeamMemberManager(team, userId, memberManagerRoles, readOnly) {
|
|
5916
|
+
if (!team || !userId || !memberManagerRoles || memberManagerRoles.length === 0 || readOnly) {
|
|
5917
|
+
return false;
|
|
5918
|
+
}
|
|
5919
|
+
return team.members_with_roles?.some((m) => m.user_id === userId && memberManagerRoles.includes(m.role)) ?? false;
|
|
5920
|
+
}
|
|
5921
|
+
var init_teamMemberManager = __esm({
|
|
5922
|
+
"src/teamMemberManager.ts"() {
|
|
5923
|
+
"use strict";
|
|
5924
|
+
}
|
|
5925
|
+
});
|
|
5926
|
+
|
|
5842
5927
|
// src/unprovisionedCopy.ts
|
|
5843
5928
|
function getUnprovisionedTitle() {
|
|
5844
5929
|
return "Your LiteLLM account isn't set up yet";
|
|
@@ -5916,6 +6001,7 @@ var init_LiteLLMPage = __esm({
|
|
|
5916
6001
|
init_dates();
|
|
5917
6002
|
init_permissions();
|
|
5918
6003
|
init_feedback();
|
|
6004
|
+
init_teamMemberManager();
|
|
5919
6005
|
init_unprovisionedCopy();
|
|
5920
6006
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
5921
6007
|
PAGE_TABS = ["overview", "keys", "teams", "models", "audit"];
|
|
@@ -6089,6 +6175,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6089
6175
|
]);
|
|
6090
6176
|
return allModels.filter((m) => allowed.has(m.model_name));
|
|
6091
6177
|
}, [allModels, userInfo, teams]);
|
|
6178
|
+
const getConfig = useCallback4(() => api.getConfig(), [api]);
|
|
6092
6179
|
const handleGenerateKey = useCallback4(
|
|
6093
6180
|
async (request) => {
|
|
6094
6181
|
try {
|
|
@@ -6360,6 +6447,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6360
6447
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
6361
6448
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
6362
6449
|
const visibleTeams = Array.from(teamsById.values());
|
|
6450
|
+
const readOnly = liteLlmConfig?.teamManagement?.readOnly ?? false;
|
|
6451
|
+
const memberManagerRoles = liteLlmConfig?.teamManagement?.memberManagerRoles ?? [];
|
|
6363
6452
|
return /* @__PURE__ */ jsx18(
|
|
6364
6453
|
TeamUsage,
|
|
6365
6454
|
{
|
|
@@ -6367,10 +6456,13 @@ var init_LiteLLMPage = __esm({
|
|
|
6367
6456
|
loading: teamsLoading,
|
|
6368
6457
|
getTeamUsage: (teamId) => teamUsageCache[teamId] ?? null,
|
|
6369
6458
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
|
|
6370
|
-
canManage: teamMgmtEnabled && canManageTeam,
|
|
6459
|
+
canManage: teamMgmtEnabled && canManageTeam && !readOnly,
|
|
6371
6460
|
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
|
|
6372
|
-
canCreate: teamMgmtEnabled && canCreateTeam,
|
|
6461
|
+
canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
|
|
6373
6462
|
onCreateTeam: () => setManageTeam({ mode: "create" }),
|
|
6463
|
+
canManageMembers: (t) => teamMgmtEnabled && canManageMembers && isTeamMemberManager(t, userInfo?.user_id, memberManagerRoles, readOnly),
|
|
6464
|
+
onManageMembers: (t) => setManageTeam({ mode: "members", team: t }),
|
|
6465
|
+
readOnly,
|
|
6374
6466
|
onTeamExpand: (team) => loadTeamUsage(team.team_id)
|
|
6375
6467
|
}
|
|
6376
6468
|
);
|
|
@@ -6414,7 +6506,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6414
6506
|
onCreateKey: handleGenerateKey,
|
|
6415
6507
|
onUpdateKey: handleUpdateKey,
|
|
6416
6508
|
onResetKeySpend: handleResetKeySpend,
|
|
6417
|
-
onGetConfig:
|
|
6509
|
+
onGetConfig: getConfig
|
|
6418
6510
|
}
|
|
6419
6511
|
),
|
|
6420
6512
|
/* @__PURE__ */ jsx18(
|
|
@@ -6422,10 +6514,13 @@ var init_LiteLLMPage = __esm({
|
|
|
6422
6514
|
{
|
|
6423
6515
|
open: !!manageTeam,
|
|
6424
6516
|
onClose: () => setManageTeam(null),
|
|
6425
|
-
mode: manageTeam?.mode ?? "create",
|
|
6517
|
+
mode: manageTeam?.mode === "members" ? "edit" : manageTeam?.mode ?? "create",
|
|
6426
6518
|
team: manageTeam?.team,
|
|
6427
6519
|
allModels: allModels ?? [],
|
|
6428
6520
|
config: liteLlmConfig,
|
|
6521
|
+
membersOnly: manageTeam?.mode === "members",
|
|
6522
|
+
currentUserId: userInfo?.user_id,
|
|
6523
|
+
memberManagerRoles: liteLlmConfig?.teamManagement?.memberManagerRoles,
|
|
6429
6524
|
onSubmit: async (payload) => {
|
|
6430
6525
|
try {
|
|
6431
6526
|
if (manageTeam?.mode === "edit" && manageTeam.team) {
|
|
@@ -6446,7 +6541,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6446
6541
|
if (alert) alertApi.post(alert);
|
|
6447
6542
|
}
|
|
6448
6543
|
},
|
|
6449
|
-
canManageMembers: teamMgmtEnabled && canManageMembers,
|
|
6544
|
+
canManageMembers: teamMgmtEnabled && canManageMembers && !liteLlmConfig?.teamManagement?.readOnly,
|
|
6450
6545
|
onAddMember: async (userEntityRef, maxBudgetInTeam) => {
|
|
6451
6546
|
if (!manageTeam?.team) return;
|
|
6452
6547
|
const updated = await api.addTeamMember(manageTeam.team.team_id, {
|
|
@@ -6570,7 +6665,7 @@ import FormControl2 from "@mui/material/FormControl";
|
|
|
6570
6665
|
import Select2 from "@mui/material/Select";
|
|
6571
6666
|
import MenuItem6 from "@mui/material/MenuItem";
|
|
6572
6667
|
import Skeleton7 from "@mui/material/Skeleton";
|
|
6573
|
-
import { AreaChart as AreaChart3, Area as Area3, XAxis as XAxis3, Tooltip as
|
|
6668
|
+
import { AreaChart as AreaChart3, Area as Area3, XAxis as XAxis3, Tooltip as Tooltip8, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
|
|
6574
6669
|
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
6575
6670
|
import { useRouteRef as useRouteRef2 } from "@backstage/frontend-plugin-api";
|
|
6576
6671
|
import { Link as Link3 } from "@backstage/core-components";
|
|
@@ -6735,7 +6830,7 @@ var LiteLLMHomeWidget = ({
|
|
|
6735
6830
|
sx: { mb: 0.5 },
|
|
6736
6831
|
children: /* @__PURE__ */ jsx20(ResponsiveContainer3, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs18(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 }, children: [
|
|
6737
6832
|
/* @__PURE__ */ jsx20(XAxis3, { dataKey: "date", hide: true }),
|
|
6738
|
-
/* @__PURE__ */ jsx20(
|
|
6833
|
+
/* @__PURE__ */ jsx20(Tooltip8, { content: /* @__PURE__ */ jsx20(ChartTooltip, { valueFormatter: fmtUsd }) }),
|
|
6739
6834
|
/* @__PURE__ */ jsx20(
|
|
6740
6835
|
Area3,
|
|
6741
6836
|
{
|
|
@@ -6782,7 +6877,7 @@ import Collapse4 from "@mui/material/Collapse";
|
|
|
6782
6877
|
import { useTheme as useTheme4 } from "@mui/material/styles";
|
|
6783
6878
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
6784
6879
|
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
6785
|
-
import { AreaChart as AreaChart4, Area as Area4, ResponsiveContainer as ResponsiveContainer4, Tooltip as
|
|
6880
|
+
import { AreaChart as AreaChart4, Area as Area4, ResponsiveContainer as ResponsiveContainer4, Tooltip as Tooltip9, ReferenceLine as ReferenceLine2, XAxis as XAxis4 } from "recharts";
|
|
6786
6881
|
import { useApi as useApi6 } from "@backstage/core-plugin-api";
|
|
6787
6882
|
import { Link as Link4 } from "@backstage/core-components";
|
|
6788
6883
|
|
|
@@ -7084,7 +7179,7 @@ var SpendStrip = ({ data, mtdSpend, loading, height, usageUnavailable = false, m
|
|
|
7084
7179
|
}
|
|
7085
7180
|
return /* @__PURE__ */ jsx21(ResponsiveContainer4, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs19(AreaChart4, { data, margin: { top: 4, right: 0, bottom: 0, left: 0 }, children: [
|
|
7086
7181
|
/* @__PURE__ */ jsx21(XAxis4, { dataKey: "date", hide: true }),
|
|
7087
|
-
/* @__PURE__ */ jsx21(
|
|
7182
|
+
/* @__PURE__ */ jsx21(Tooltip9, { content: /* @__PURE__ */ jsx21(ChartTooltip, { valueFormatter: fmtUsd }) }),
|
|
7088
7183
|
maxBudget !== void 0 && /* @__PURE__ */ jsx21(
|
|
7089
7184
|
ReferenceLine2,
|
|
7090
7185
|
{
|