@acarmisc/backstage-plugin-litellm 0.31.1 → 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 +176 -95
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.esm.js +189 -108
- 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
|
@@ -3124,6 +3124,7 @@ import Box10 from "@mui/material/Box";
|
|
|
3124
3124
|
import Divider2 from "@mui/material/Divider";
|
|
3125
3125
|
import Typography8 from "@mui/material/Typography";
|
|
3126
3126
|
import IconButton4 from "@mui/material/IconButton";
|
|
3127
|
+
import Tooltip4 from "@mui/material/Tooltip";
|
|
3127
3128
|
import Paper4 from "@mui/material/Paper";
|
|
3128
3129
|
import Table2 from "@mui/material/Table";
|
|
3129
3130
|
import TableBody2 from "@mui/material/TableBody";
|
|
@@ -3166,7 +3167,10 @@ var init_ManageTeamDialog = __esm({
|
|
|
3166
3167
|
onSaveKnowledgeBases,
|
|
3167
3168
|
canManageMcpServers,
|
|
3168
3169
|
mcpServers,
|
|
3169
|
-
onSaveMcpServers
|
|
3170
|
+
onSaveMcpServers,
|
|
3171
|
+
membersOnly,
|
|
3172
|
+
currentUserId,
|
|
3173
|
+
memberManagerRoles
|
|
3170
3174
|
}) => {
|
|
3171
3175
|
const [alias, setAlias] = useState6("");
|
|
3172
3176
|
const [models, setModels] = useState6([]);
|
|
@@ -3191,7 +3195,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3191
3195
|
const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
|
|
3192
3196
|
const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
|
|
3193
3197
|
const members = useMemo4(() => team?.members_with_roles ?? [], [team]);
|
|
3194
|
-
const showMembers = mode === "edit" && !!canManageMembers;
|
|
3198
|
+
const showMembers = mode === "edit" && !!canManageMembers || membersOnly;
|
|
3195
3199
|
const catalogApi = useApi3(catalogApiRef);
|
|
3196
3200
|
const [searchUsers, setSearchUsers] = useState6([]);
|
|
3197
3201
|
const [usersLoading, setUsersLoading] = useState6(false);
|
|
@@ -3340,7 +3344,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3340
3344
|
const handleAddMember = async () => {
|
|
3341
3345
|
if (!onAddMember || !memberRef.trim()) return;
|
|
3342
3346
|
setMemberError(null);
|
|
3343
|
-
const budget = memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3347
|
+
const budget = !membersOnly && memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3344
3348
|
if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
|
|
3345
3349
|
setMemberError("Max budget in team must be a positive number");
|
|
3346
3350
|
return;
|
|
@@ -3401,72 +3405,95 @@ var init_ManageTeamDialog = __esm({
|
|
|
3401
3405
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
3402
3406
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
3403
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
|
+
};
|
|
3404
3424
|
return /* @__PURE__ */ jsxs10(Dialog3, { open, onClose, maxWidth: "sm", fullWidth: true, children: [
|
|
3405
|
-
/* @__PURE__ */ jsx11(DialogTitle3, { children:
|
|
3425
|
+
/* @__PURE__ */ jsx11(DialogTitle3, { children: getDialogTitle() }),
|
|
3406
3426
|
/* @__PURE__ */ jsxs10(DialogContent3, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
3407
3427
|
error && /* @__PURE__ */ jsx11(Alert2, { severity: "error", children: error }),
|
|
3408
|
-
/* @__PURE__ */
|
|
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
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
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
|
+
] }),
|
|
3470
3497
|
showMembers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3471
3498
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3472
3499
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Members" }),
|
|
@@ -3498,17 +3525,20 @@ var init_ManageTeamDialog = __esm({
|
|
|
3498
3525
|
}
|
|
3499
3526
|
) }),
|
|
3500
3527
|
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children: /* @__PURE__ */ jsx11(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
|
|
3501
|
-
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children:
|
|
3502
|
-
|
|
3503
|
-
{
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
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
|
+
})() })
|
|
3512
3542
|
] }, m.user_id)) })
|
|
3513
3543
|
] }) }),
|
|
3514
3544
|
/* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
|
|
@@ -3556,7 +3586,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3556
3586
|
)
|
|
3557
3587
|
}
|
|
3558
3588
|
),
|
|
3559
|
-
/* @__PURE__ */ jsx11(
|
|
3589
|
+
!membersOnly && /* @__PURE__ */ jsx11(
|
|
3560
3590
|
TextField3,
|
|
3561
3591
|
{
|
|
3562
3592
|
label: "Max budget in team ($)",
|
|
@@ -3580,7 +3610,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3580
3610
|
)
|
|
3581
3611
|
] })
|
|
3582
3612
|
] }),
|
|
3583
|
-
showKnowledgeBases && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3613
|
+
!membersOnly && showKnowledgeBases && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3584
3614
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3585
3615
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Knowledge Bases" }),
|
|
3586
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." }),
|
|
@@ -3607,7 +3637,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3607
3637
|
}
|
|
3608
3638
|
) })
|
|
3609
3639
|
] }),
|
|
3610
|
-
showMcpServers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3640
|
+
!membersOnly && showMcpServers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3611
3641
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3612
3642
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "MCP Servers" }),
|
|
3613
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." }),
|
|
@@ -3636,8 +3666,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
3636
3666
|
] })
|
|
3637
3667
|
] }),
|
|
3638
3668
|
/* @__PURE__ */ jsxs10(DialogActions3, { children: [
|
|
3639
|
-
/* @__PURE__ */ jsx11(Button4, { onClick: onClose, disabled: submitting, children: "Cancel" }),
|
|
3640
|
-
/* @__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" })
|
|
3641
3671
|
] })
|
|
3642
3672
|
] });
|
|
3643
3673
|
};
|
|
@@ -3848,7 +3878,7 @@ import {
|
|
|
3848
3878
|
XAxis,
|
|
3849
3879
|
YAxis,
|
|
3850
3880
|
CartesianGrid,
|
|
3851
|
-
Tooltip as
|
|
3881
|
+
Tooltip as Tooltip5,
|
|
3852
3882
|
ResponsiveContainer,
|
|
3853
3883
|
Legend,
|
|
3854
3884
|
ReferenceLine,
|
|
@@ -4185,7 +4215,7 @@ var init_UsageStats = __esm({
|
|
|
4185
4215
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4186
4216
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }),
|
|
4187
4217
|
/* @__PURE__ */ jsx12(
|
|
4188
|
-
|
|
4218
|
+
Tooltip5,
|
|
4189
4219
|
{
|
|
4190
4220
|
cursor: chart.cursor,
|
|
4191
4221
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
|
|
@@ -4211,7 +4241,7 @@ var init_UsageStats = __esm({
|
|
|
4211
4241
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid }),
|
|
4212
4242
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4213
4243
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }),
|
|
4214
|
-
/* @__PURE__ */ jsx12(
|
|
4244
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt }) }),
|
|
4215
4245
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4216
4246
|
/* @__PURE__ */ jsx12(Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }),
|
|
4217
4247
|
/* @__PURE__ */ jsx12(Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })
|
|
@@ -4220,7 +4250,7 @@ var init_UsageStats = __esm({
|
|
|
4220
4250
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid }),
|
|
4221
4251
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4222
4252
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }),
|
|
4223
|
-
/* @__PURE__ */ jsx12(
|
|
4253
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt }) }),
|
|
4224
4254
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4225
4255
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }),
|
|
4226
4256
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })
|
|
@@ -4230,7 +4260,7 @@ var init_UsageStats = __esm({
|
|
|
4230
4260
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4231
4261
|
/* @__PURE__ */ jsx12(YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }),
|
|
4232
4262
|
/* @__PURE__ */ jsx12(
|
|
4233
|
-
|
|
4263
|
+
Tooltip5,
|
|
4234
4264
|
{
|
|
4235
4265
|
cursor: chart.cursor,
|
|
4236
4266
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: (v) => `${v}%` })
|
|
@@ -4267,7 +4297,7 @@ var init_UsageStats = __esm({
|
|
|
4267
4297
|
}
|
|
4268
4298
|
),
|
|
4269
4299
|
/* @__PURE__ */ jsx12(
|
|
4270
|
-
|
|
4300
|
+
Tooltip5,
|
|
4271
4301
|
{
|
|
4272
4302
|
cursor: chart.cursor,
|
|
4273
4303
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd })
|
|
@@ -4309,7 +4339,7 @@ var init_UsageStats = __esm({
|
|
|
4309
4339
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4310
4340
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }),
|
|
4311
4341
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4312
|
-
/* @__PURE__ */ jsx12(
|
|
4342
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }),
|
|
4313
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)) })
|
|
4314
4344
|
] }) })
|
|
4315
4345
|
}
|
|
@@ -4324,7 +4354,7 @@ var init_UsageStats = __esm({
|
|
|
4324
4354
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4325
4355
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }),
|
|
4326
4356
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4327
|
-
/* @__PURE__ */ jsx12(
|
|
4357
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }),
|
|
4328
4358
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4329
4359
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }),
|
|
4330
4360
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })
|
|
@@ -4356,7 +4386,7 @@ var init_UsageStats = __esm({
|
|
|
4356
4386
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4357
4387
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }),
|
|
4358
4388
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4359
|
-
/* @__PURE__ */ jsx12(
|
|
4389
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }),
|
|
4360
4390
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })
|
|
4361
4391
|
] }) })
|
|
4362
4392
|
}
|
|
@@ -4891,14 +4921,15 @@ import IconButton6 from "@mui/material/IconButton";
|
|
|
4891
4921
|
import Button6 from "@mui/material/Button";
|
|
4892
4922
|
import CircularProgress4 from "@mui/material/CircularProgress";
|
|
4893
4923
|
import { alpha as alpha10 } from "@mui/material/styles";
|
|
4894
|
-
import
|
|
4924
|
+
import MuiTooltip from "@mui/material/Tooltip";
|
|
4925
|
+
import { ExpandMore as ExpandMore3, Group, Add as Add2, LockOutlined, GroupAddOutlined } from "@mui/icons-material";
|
|
4895
4926
|
import {
|
|
4896
4927
|
AreaChart as AreaChart2,
|
|
4897
4928
|
Area as Area2,
|
|
4898
4929
|
XAxis as XAxis2,
|
|
4899
4930
|
YAxis as YAxis2,
|
|
4900
4931
|
CartesianGrid as CartesianGrid2,
|
|
4901
|
-
Tooltip as
|
|
4932
|
+
Tooltip as Tooltip6,
|
|
4902
4933
|
ResponsiveContainer as ResponsiveContainer2
|
|
4903
4934
|
} from "recharts";
|
|
4904
4935
|
import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
@@ -4922,7 +4953,7 @@ var init_TeamUsage = __esm({
|
|
|
4922
4953
|
"use strict";
|
|
4923
4954
|
init_ui();
|
|
4924
4955
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
4925
|
-
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, onExpand }) => {
|
|
4956
|
+
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand }) => {
|
|
4926
4957
|
const [expanded, setExpanded] = useState9(false);
|
|
4927
4958
|
const chart = useChartTheme();
|
|
4928
4959
|
const handleExpandChange = (newExpanded) => {
|
|
@@ -4959,7 +4990,7 @@ var init_TeamUsage = __esm({
|
|
|
4959
4990
|
/* @__PURE__ */ jsx15(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4960
4991
|
/* @__PURE__ */ jsx15(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }),
|
|
4961
4992
|
/* @__PURE__ */ jsx15(
|
|
4962
|
-
|
|
4993
|
+
Tooltip6,
|
|
4963
4994
|
{
|
|
4964
4995
|
cursor: chart.cursor,
|
|
4965
4996
|
content: /* @__PURE__ */ jsx15(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
@@ -5032,6 +5063,17 @@ var init_TeamUsage = __esm({
|
|
|
5032
5063
|
children: "Edit"
|
|
5033
5064
|
}
|
|
5034
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
|
+
) }),
|
|
5035
5077
|
/* @__PURE__ */ jsx15(
|
|
5036
5078
|
IconButton6,
|
|
5037
5079
|
{
|
|
@@ -5162,9 +5204,23 @@ var init_TeamUsage = __esm({
|
|
|
5162
5204
|
onEditTeam,
|
|
5163
5205
|
canCreate,
|
|
5164
5206
|
onCreateTeam,
|
|
5165
|
-
onTeamExpand
|
|
5207
|
+
onTeamExpand,
|
|
5208
|
+
canManageMembers,
|
|
5209
|
+
onManageMembers,
|
|
5210
|
+
readOnly
|
|
5166
5211
|
}) => {
|
|
5167
|
-
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(
|
|
5168
5224
|
Button6,
|
|
5169
5225
|
{
|
|
5170
5226
|
variant: "contained",
|
|
@@ -5175,6 +5231,7 @@ var init_TeamUsage = __esm({
|
|
|
5175
5231
|
children: "Create Team"
|
|
5176
5232
|
}
|
|
5177
5233
|
) : void 0;
|
|
5234
|
+
const createAction = readOnly ? readOnlyBadge : createButton;
|
|
5178
5235
|
if (loading) {
|
|
5179
5236
|
return /* @__PURE__ */ jsxs14(SectionCard, { title: "Teams", actions: createAction, children: [
|
|
5180
5237
|
/* @__PURE__ */ jsx15(Skeleton5, { variant: "rounded", height: 120, sx: { mb: 2 } }),
|
|
@@ -5204,6 +5261,8 @@ var init_TeamUsage = __esm({
|
|
|
5204
5261
|
usageLoading: getTeamUsageLoading(team.team_id),
|
|
5205
5262
|
canManage,
|
|
5206
5263
|
onEditTeam,
|
|
5264
|
+
canManageMembers: canManageMembers?.(team),
|
|
5265
|
+
onManageMembers,
|
|
5207
5266
|
onExpand: onTeamExpand
|
|
5208
5267
|
},
|
|
5209
5268
|
team.team_id
|
|
@@ -5233,7 +5292,7 @@ import TextField5 from "@mui/material/TextField";
|
|
|
5233
5292
|
import InputAdornment3 from "@mui/material/InputAdornment";
|
|
5234
5293
|
import { Search as Search2 } from "@mui/icons-material";
|
|
5235
5294
|
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
|
5236
|
-
import
|
|
5295
|
+
import Tooltip7 from "@mui/material/Tooltip";
|
|
5237
5296
|
import Snackbar from "@mui/material/Snackbar";
|
|
5238
5297
|
import Alert4 from "@mui/material/Alert";
|
|
5239
5298
|
import { alpha as alpha11, useTheme as useTheme3 } from "@mui/material/styles";
|
|
@@ -5417,7 +5476,7 @@ var init_ModelsTable = __esm({
|
|
|
5417
5476
|
] }) }),
|
|
5418
5477
|
/* @__PURE__ */ jsx16(TableBody5, { children: filteredModels.map((m) => /* @__PURE__ */ jsxs15(TableRow5, { hover: true, children: [
|
|
5419
5478
|
/* @__PURE__ */ jsx16(TableCell5, { children: /* @__PURE__ */ jsxs15(Box15, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
|
|
5420
|
-
/* @__PURE__ */ jsx16(
|
|
5479
|
+
/* @__PURE__ */ jsx16(Tooltip7, { title: m.model_name, placement: "top", children: /* @__PURE__ */ jsx16(
|
|
5421
5480
|
"span",
|
|
5422
5481
|
{
|
|
5423
5482
|
role: "button",
|
|
@@ -5429,7 +5488,7 @@ var init_ModelsTable = __esm({
|
|
|
5429
5488
|
children: /* @__PURE__ */ jsx16(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
5430
5489
|
}
|
|
5431
5490
|
) }),
|
|
5432
|
-
/* @__PURE__ */ jsx16(
|
|
5491
|
+
/* @__PURE__ */ jsx16(Tooltip7, { title: "Copy model ID", children: /* @__PURE__ */ jsx16(
|
|
5433
5492
|
Box15,
|
|
5434
5493
|
{
|
|
5435
5494
|
component: "span",
|
|
@@ -5852,6 +5911,19 @@ var init_feedback = __esm({
|
|
|
5852
5911
|
}
|
|
5853
5912
|
});
|
|
5854
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
|
+
|
|
5855
5927
|
// src/unprovisionedCopy.ts
|
|
5856
5928
|
function getUnprovisionedTitle() {
|
|
5857
5929
|
return "Your LiteLLM account isn't set up yet";
|
|
@@ -5929,6 +6001,7 @@ var init_LiteLLMPage = __esm({
|
|
|
5929
6001
|
init_dates();
|
|
5930
6002
|
init_permissions();
|
|
5931
6003
|
init_feedback();
|
|
6004
|
+
init_teamMemberManager();
|
|
5932
6005
|
init_unprovisionedCopy();
|
|
5933
6006
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
5934
6007
|
PAGE_TABS = ["overview", "keys", "teams", "models", "audit"];
|
|
@@ -6374,6 +6447,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6374
6447
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
6375
6448
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
6376
6449
|
const visibleTeams = Array.from(teamsById.values());
|
|
6450
|
+
const readOnly = liteLlmConfig?.teamManagement?.readOnly ?? false;
|
|
6451
|
+
const memberManagerRoles = liteLlmConfig?.teamManagement?.memberManagerRoles ?? [];
|
|
6377
6452
|
return /* @__PURE__ */ jsx18(
|
|
6378
6453
|
TeamUsage,
|
|
6379
6454
|
{
|
|
@@ -6381,10 +6456,13 @@ var init_LiteLLMPage = __esm({
|
|
|
6381
6456
|
loading: teamsLoading,
|
|
6382
6457
|
getTeamUsage: (teamId) => teamUsageCache[teamId] ?? null,
|
|
6383
6458
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
|
|
6384
|
-
canManage: teamMgmtEnabled && canManageTeam,
|
|
6459
|
+
canManage: teamMgmtEnabled && canManageTeam && !readOnly,
|
|
6385
6460
|
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
|
|
6386
|
-
canCreate: teamMgmtEnabled && canCreateTeam,
|
|
6461
|
+
canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
|
|
6387
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,
|
|
6388
6466
|
onTeamExpand: (team) => loadTeamUsage(team.team_id)
|
|
6389
6467
|
}
|
|
6390
6468
|
);
|
|
@@ -6436,10 +6514,13 @@ var init_LiteLLMPage = __esm({
|
|
|
6436
6514
|
{
|
|
6437
6515
|
open: !!manageTeam,
|
|
6438
6516
|
onClose: () => setManageTeam(null),
|
|
6439
|
-
mode: manageTeam?.mode ?? "create",
|
|
6517
|
+
mode: manageTeam?.mode === "members" ? "edit" : manageTeam?.mode ?? "create",
|
|
6440
6518
|
team: manageTeam?.team,
|
|
6441
6519
|
allModels: allModels ?? [],
|
|
6442
6520
|
config: liteLlmConfig,
|
|
6521
|
+
membersOnly: manageTeam?.mode === "members",
|
|
6522
|
+
currentUserId: userInfo?.user_id,
|
|
6523
|
+
memberManagerRoles: liteLlmConfig?.teamManagement?.memberManagerRoles,
|
|
6443
6524
|
onSubmit: async (payload) => {
|
|
6444
6525
|
try {
|
|
6445
6526
|
if (manageTeam?.mode === "edit" && manageTeam.team) {
|
|
@@ -6460,7 +6541,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6460
6541
|
if (alert) alertApi.post(alert);
|
|
6461
6542
|
}
|
|
6462
6543
|
},
|
|
6463
|
-
canManageMembers: teamMgmtEnabled && canManageMembers,
|
|
6544
|
+
canManageMembers: teamMgmtEnabled && canManageMembers && !liteLlmConfig?.teamManagement?.readOnly,
|
|
6464
6545
|
onAddMember: async (userEntityRef, maxBudgetInTeam) => {
|
|
6465
6546
|
if (!manageTeam?.team) return;
|
|
6466
6547
|
const updated = await api.addTeamMember(manageTeam.team.team_id, {
|
|
@@ -6584,7 +6665,7 @@ import FormControl2 from "@mui/material/FormControl";
|
|
|
6584
6665
|
import Select2 from "@mui/material/Select";
|
|
6585
6666
|
import MenuItem6 from "@mui/material/MenuItem";
|
|
6586
6667
|
import Skeleton7 from "@mui/material/Skeleton";
|
|
6587
|
-
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";
|
|
6588
6669
|
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
6589
6670
|
import { useRouteRef as useRouteRef2 } from "@backstage/frontend-plugin-api";
|
|
6590
6671
|
import { Link as Link3 } from "@backstage/core-components";
|
|
@@ -6749,7 +6830,7 @@ var LiteLLMHomeWidget = ({
|
|
|
6749
6830
|
sx: { mb: 0.5 },
|
|
6750
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: [
|
|
6751
6832
|
/* @__PURE__ */ jsx20(XAxis3, { dataKey: "date", hide: true }),
|
|
6752
|
-
/* @__PURE__ */ jsx20(
|
|
6833
|
+
/* @__PURE__ */ jsx20(Tooltip8, { content: /* @__PURE__ */ jsx20(ChartTooltip, { valueFormatter: fmtUsd }) }),
|
|
6753
6834
|
/* @__PURE__ */ jsx20(
|
|
6754
6835
|
Area3,
|
|
6755
6836
|
{
|
|
@@ -6796,7 +6877,7 @@ import Collapse4 from "@mui/material/Collapse";
|
|
|
6796
6877
|
import { useTheme as useTheme4 } from "@mui/material/styles";
|
|
6797
6878
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
6798
6879
|
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
6799
|
-
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";
|
|
6800
6881
|
import { useApi as useApi6 } from "@backstage/core-plugin-api";
|
|
6801
6882
|
import { Link as Link4 } from "@backstage/core-components";
|
|
6802
6883
|
|
|
@@ -7098,7 +7179,7 @@ var SpendStrip = ({ data, mtdSpend, loading, height, usageUnavailable = false, m
|
|
|
7098
7179
|
}
|
|
7099
7180
|
return /* @__PURE__ */ jsx21(ResponsiveContainer4, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs19(AreaChart4, { data, margin: { top: 4, right: 0, bottom: 0, left: 0 }, children: [
|
|
7100
7181
|
/* @__PURE__ */ jsx21(XAxis4, { dataKey: "date", hide: true }),
|
|
7101
|
-
/* @__PURE__ */ jsx21(
|
|
7182
|
+
/* @__PURE__ */ jsx21(Tooltip9, { content: /* @__PURE__ */ jsx21(ChartTooltip, { valueFormatter: fmtUsd }) }),
|
|
7102
7183
|
maxBudget !== void 0 && /* @__PURE__ */ jsx21(
|
|
7103
7184
|
ReferenceLine2,
|
|
7104
7185
|
{
|