@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
|
@@ -26,6 +26,12 @@ interface ManageTeamDialogProps {
|
|
|
26
26
|
mcpServers?: McpServerInfo[];
|
|
27
27
|
/** Called to replace the team's attached MCP servers; parent refreshes `team`. */
|
|
28
28
|
onSaveMcpServers?: (mcpServerIds: string[]) => Promise<void>;
|
|
29
|
+
/** When true, only members section is shown (not budget/models/KB/MCP); title changes; Save becomes Done. */
|
|
30
|
+
membersOnly?: boolean;
|
|
31
|
+
/** Current user's LiteLLM ID; used in membersOnly mode to prevent self-removal and role-restricted removal. */
|
|
32
|
+
currentUserId?: string;
|
|
33
|
+
/** Roles that can manage members in membersOnly mode; used to disable removal of role holders. */
|
|
34
|
+
memberManagerRoles?: string[];
|
|
29
35
|
}
|
|
30
36
|
export declare const ManageTeamDialog: FC<ManageTeamDialogProps>;
|
|
31
37
|
export {};
|
|
@@ -12,6 +12,11 @@ interface TeamUsageProps {
|
|
|
12
12
|
onCreateTeam?: () => void;
|
|
13
13
|
/** Called when a team card is expanded to trigger data loading. */
|
|
14
14
|
onTeamExpand?: (team: TeamInfo) => void;
|
|
15
|
+
/** Per-team: whether the caller may manage this team's members only. */
|
|
16
|
+
canManageMembers?: (team: TeamInfo) => boolean;
|
|
17
|
+
onManageMembers?: (team: TeamInfo) => void;
|
|
18
|
+
/** Teams are synced from an identity provider: show a badge, no write controls. */
|
|
19
|
+
readOnly?: boolean;
|
|
15
20
|
}
|
|
16
21
|
export declare const TeamUsage: FC<TeamUsageProps>;
|
|
17
22
|
export {};
|
package/dist/index.cjs.js
CHANGED
|
@@ -3127,7 +3127,7 @@ var init_KeyFormDialog = __esm({
|
|
|
3127
3127
|
});
|
|
3128
3128
|
|
|
3129
3129
|
// src/components/ManageTeamDialog.tsx
|
|
3130
|
-
var import_react7, import_Dialog3, import_DialogTitle3, import_DialogContent3, import_DialogActions3, import_TextField3, import_Button4, import_Alert2, import_Checkbox2, import_FormControlLabel2, import_Autocomplete2, import_CircularProgress3, import_MenuItem2, import_Box10, import_Divider2, import_Typography8, import_IconButton4, import_Paper4, import_Table2, import_TableBody2, import_TableCell2, import_TableContainer2, import_TableHead2, import_TableRow2, import_icons_material5, import_core_plugin_api4, import_plugin_catalog_react, import_catalog_model, import_jsx_runtime11, BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
|
|
3130
|
+
var import_react7, import_Dialog3, import_DialogTitle3, import_DialogContent3, import_DialogActions3, import_TextField3, import_Button4, import_Alert2, import_Checkbox2, import_FormControlLabel2, import_Autocomplete2, import_CircularProgress3, import_MenuItem2, import_Box10, import_Divider2, import_Typography8, import_IconButton4, import_Tooltip4, import_Paper4, import_Table2, import_TableBody2, import_TableCell2, import_TableContainer2, import_TableHead2, import_TableRow2, import_icons_material5, import_core_plugin_api4, import_plugin_catalog_react, import_catalog_model, import_jsx_runtime11, BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
|
|
3131
3131
|
var init_ManageTeamDialog = __esm({
|
|
3132
3132
|
"src/components/ManageTeamDialog.tsx"() {
|
|
3133
3133
|
"use strict";
|
|
@@ -3148,6 +3148,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3148
3148
|
import_Divider2 = __toESM(require("@mui/material/Divider"));
|
|
3149
3149
|
import_Typography8 = __toESM(require("@mui/material/Typography"));
|
|
3150
3150
|
import_IconButton4 = __toESM(require("@mui/material/IconButton"));
|
|
3151
|
+
import_Tooltip4 = __toESM(require("@mui/material/Tooltip"));
|
|
3151
3152
|
import_Paper4 = __toESM(require("@mui/material/Paper"));
|
|
3152
3153
|
import_Table2 = __toESM(require("@mui/material/Table"));
|
|
3153
3154
|
import_TableBody2 = __toESM(require("@mui/material/TableBody"));
|
|
@@ -3186,7 +3187,10 @@ var init_ManageTeamDialog = __esm({
|
|
|
3186
3187
|
onSaveKnowledgeBases,
|
|
3187
3188
|
canManageMcpServers,
|
|
3188
3189
|
mcpServers,
|
|
3189
|
-
onSaveMcpServers
|
|
3190
|
+
onSaveMcpServers,
|
|
3191
|
+
membersOnly,
|
|
3192
|
+
currentUserId,
|
|
3193
|
+
memberManagerRoles
|
|
3190
3194
|
}) => {
|
|
3191
3195
|
const [alias, setAlias] = (0, import_react7.useState)("");
|
|
3192
3196
|
const [models, setModels] = (0, import_react7.useState)([]);
|
|
@@ -3211,7 +3215,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3211
3215
|
const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
|
|
3212
3216
|
const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
|
|
3213
3217
|
const members = (0, import_react7.useMemo)(() => team?.members_with_roles ?? [], [team]);
|
|
3214
|
-
const showMembers = mode === "edit" && !!canManageMembers;
|
|
3218
|
+
const showMembers = mode === "edit" && !!canManageMembers || membersOnly;
|
|
3215
3219
|
const catalogApi = (0, import_core_plugin_api4.useApi)(import_plugin_catalog_react.catalogApiRef);
|
|
3216
3220
|
const [searchUsers, setSearchUsers] = (0, import_react7.useState)([]);
|
|
3217
3221
|
const [usersLoading, setUsersLoading] = (0, import_react7.useState)(false);
|
|
@@ -3360,7 +3364,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3360
3364
|
const handleAddMember = async () => {
|
|
3361
3365
|
if (!onAddMember || !memberRef.trim()) return;
|
|
3362
3366
|
setMemberError(null);
|
|
3363
|
-
const budget = memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3367
|
+
const budget = !membersOnly && memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3364
3368
|
if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
|
|
3365
3369
|
setMemberError("Max budget in team must be a positive number");
|
|
3366
3370
|
return;
|
|
@@ -3421,72 +3425,95 @@ var init_ManageTeamDialog = __esm({
|
|
|
3421
3425
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
3422
3426
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
3423
3427
|
const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
|
|
3428
|
+
const isRemovalDisabled = (memberUserId, memberRole) => {
|
|
3429
|
+
if (!membersOnly) return { disabled: false };
|
|
3430
|
+
if (memberUserId === currentUserId) {
|
|
3431
|
+
return { disabled: true, reason: "You can't remove yourself" };
|
|
3432
|
+
}
|
|
3433
|
+
if (memberManagerRoles?.includes(memberRole)) {
|
|
3434
|
+
return { disabled: true, reason: `Only platform admins can remove a team ${memberRole}` };
|
|
3435
|
+
}
|
|
3436
|
+
return { disabled: false };
|
|
3437
|
+
};
|
|
3438
|
+
const getDialogTitle = () => {
|
|
3439
|
+
if (membersOnly) {
|
|
3440
|
+
return `Manage members \u2014 ${team?.team_alias || "Untitled team"}`;
|
|
3441
|
+
}
|
|
3442
|
+
return mode === "create" ? "Create Team" : "Edit Team";
|
|
3443
|
+
};
|
|
3424
3444
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Dialog3.default, { open, onClose, maxWidth: "sm", fullWidth: true, children: [
|
|
3425
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_DialogTitle3.default, { children:
|
|
3445
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_DialogTitle3.default, { children: getDialogTitle() }),
|
|
3426
3446
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_DialogContent3.default, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
3427
3447
|
error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Alert2.default, { severity: "error", children: error }),
|
|
3428
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.
|
|
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
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3448
|
+
membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Alert2.default, { severity: "info", children: [
|
|
3449
|
+
"As a team ",
|
|
3450
|
+
team?.members_with_roles?.find((m) => m.user_id === currentUserId)?.role || "member",
|
|
3451
|
+
" you can add or remove members. Budgets and models are managed by your platform admins."
|
|
3452
|
+
] }),
|
|
3453
|
+
!membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
3454
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3455
|
+
import_TextField3.default,
|
|
3456
|
+
{
|
|
3457
|
+
label: "Team Alias",
|
|
3458
|
+
value: alias,
|
|
3459
|
+
onChange: (e) => setAlias(e.target.value),
|
|
3460
|
+
disabled: submitting,
|
|
3461
|
+
fullWidth: true
|
|
3462
|
+
}
|
|
3463
|
+
),
|
|
3464
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3465
|
+
import_Autocomplete2.default,
|
|
3466
|
+
{
|
|
3467
|
+
multiple: true,
|
|
3468
|
+
options: modelNames,
|
|
3469
|
+
value: models,
|
|
3470
|
+
onChange: (_, newValue) => setModels(newValue),
|
|
3471
|
+
disabled: submitting,
|
|
3472
|
+
renderInput: (params) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3473
|
+
import_TextField3.default,
|
|
3474
|
+
{
|
|
3475
|
+
...params,
|
|
3476
|
+
label: "Models",
|
|
3477
|
+
required: true,
|
|
3478
|
+
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.)"
|
|
3479
|
+
}
|
|
3480
|
+
)
|
|
3481
|
+
}
|
|
3482
|
+
),
|
|
3483
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3484
|
+
import_TextField3.default,
|
|
3485
|
+
{
|
|
3486
|
+
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
3487
|
+
type: "number",
|
|
3488
|
+
value: maxBudget,
|
|
3489
|
+
onChange: (e) => setMaxBudget(e.target.value),
|
|
3490
|
+
disabled: submitting || unlimited,
|
|
3491
|
+
inputProps: { min: 0, max: maxBudgetCeiling },
|
|
3492
|
+
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
3493
|
+
fullWidth: true
|
|
3494
|
+
}
|
|
3495
|
+
),
|
|
3496
|
+
allowUnlimitedBudget && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3497
|
+
import_FormControlLabel2.default,
|
|
3498
|
+
{
|
|
3499
|
+
control: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Checkbox2.default, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
3500
|
+
label: "Unlimited Budget"
|
|
3501
|
+
}
|
|
3502
|
+
),
|
|
3503
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3504
|
+
import_TextField3.default,
|
|
3505
|
+
{
|
|
3506
|
+
select: true,
|
|
3507
|
+
label: "Budget Duration",
|
|
3508
|
+
value: budgetDuration,
|
|
3509
|
+
onChange: (e) => setBudgetDuration(e.target.value),
|
|
3510
|
+
disabled: submitting || unlimited,
|
|
3511
|
+
helperText: "Spend-reset period for the team budget",
|
|
3512
|
+
fullWidth: true,
|
|
3513
|
+
children: durationOptions.map((o) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_MenuItem2.default, { value: o.value, children: o.label }, o.value))
|
|
3514
|
+
}
|
|
3515
|
+
)
|
|
3516
|
+
] }),
|
|
3490
3517
|
showMembers && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3491
3518
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Divider2.default, { sx: { my: 1 } }),
|
|
3492
3519
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "subtitle2", sx: { mb: 1 }, children: "Members" }),
|
|
@@ -3518,17 +3545,20 @@ var init_ManageTeamDialog = __esm({
|
|
|
3518
3545
|
}
|
|
3519
3546
|
) }),
|
|
3520
3547
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
|
|
3521
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children:
|
|
3522
|
-
|
|
3523
|
-
{
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3548
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: (() => {
|
|
3549
|
+
const { disabled, reason } = isRemovalDisabled(m.user_id, m.role);
|
|
3550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Tooltip4.default, { title: reason ?? "", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3551
|
+
import_IconButton4.default,
|
|
3552
|
+
{
|
|
3553
|
+
edge: "end",
|
|
3554
|
+
size: "small",
|
|
3555
|
+
"aria-label": `remove ${m.user_id}`,
|
|
3556
|
+
disabled: memberBusy || disabled,
|
|
3557
|
+
onClick: () => handleRemoveMember(m.user_id),
|
|
3558
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material5.Delete, { fontSize: "small" })
|
|
3559
|
+
}
|
|
3560
|
+
) }) });
|
|
3561
|
+
})() })
|
|
3532
3562
|
] }, m.user_id)) })
|
|
3533
3563
|
] }) }),
|
|
3534
3564
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
|
|
@@ -3576,7 +3606,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3576
3606
|
)
|
|
3577
3607
|
}
|
|
3578
3608
|
),
|
|
3579
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3609
|
+
!membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3580
3610
|
import_TextField3.default,
|
|
3581
3611
|
{
|
|
3582
3612
|
label: "Max budget in team ($)",
|
|
@@ -3600,7 +3630,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3600
3630
|
)
|
|
3601
3631
|
] })
|
|
3602
3632
|
] }),
|
|
3603
|
-
showKnowledgeBases && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3633
|
+
!membersOnly && showKnowledgeBases && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3604
3634
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Divider2.default, { sx: { my: 1 } }),
|
|
3605
3635
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "subtitle2", sx: { mb: 1 }, children: "Knowledge Bases" }),
|
|
3606
3636
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Attaching a knowledge base exposes its documents to every key in this team." }),
|
|
@@ -3627,7 +3657,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3627
3657
|
}
|
|
3628
3658
|
) })
|
|
3629
3659
|
] }),
|
|
3630
|
-
showMcpServers && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3660
|
+
!membersOnly && showMcpServers && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3631
3661
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Divider2.default, { sx: { my: 1 } }),
|
|
3632
3662
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "subtitle2", sx: { mb: 1 }, children: "MCP Servers" }),
|
|
3633
3663
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { 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." }),
|
|
@@ -3656,8 +3686,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
3656
3686
|
] })
|
|
3657
3687
|
] }),
|
|
3658
3688
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_DialogActions3.default, { children: [
|
|
3659
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Button4.default, { onClick: onClose, disabled: submitting, children: "Cancel" }),
|
|
3660
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Button4.default, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : "Save" })
|
|
3689
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Button4.default, { onClick: onClose, disabled: submitting, children: membersOnly ? "Done" : "Cancel" }),
|
|
3690
|
+
!membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Button4.default, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : "Save" })
|
|
3661
3691
|
] })
|
|
3662
3692
|
] });
|
|
3663
3693
|
};
|
|
@@ -4893,7 +4923,7 @@ function budgetStatValue(hidden, budget) {
|
|
|
4893
4923
|
if (budget > 0) return fmtUsd2(budget);
|
|
4894
4924
|
return "Unlimited";
|
|
4895
4925
|
}
|
|
4896
|
-
var import_react10, import_Paper8, import_Box14, import_Typography12, import_Skeleton5, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton6, import_Button6, import_CircularProgress4, import_styles10, import_icons_material7, import_recharts2, import_jsx_runtime15, fmtUsd2, TeamCard, TeamUsage;
|
|
4926
|
+
var import_react10, import_Paper8, import_Box14, import_Typography12, import_Skeleton5, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton6, import_Button6, import_CircularProgress4, import_styles10, import_Tooltip5, import_icons_material7, import_recharts2, import_jsx_runtime15, fmtUsd2, TeamCard, TeamUsage;
|
|
4897
4927
|
var init_TeamUsage = __esm({
|
|
4898
4928
|
"src/components/TeamUsage.tsx"() {
|
|
4899
4929
|
"use strict";
|
|
@@ -4914,12 +4944,13 @@ var init_TeamUsage = __esm({
|
|
|
4914
4944
|
import_Button6 = __toESM(require("@mui/material/Button"));
|
|
4915
4945
|
import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
|
|
4916
4946
|
import_styles10 = require("@mui/material/styles");
|
|
4947
|
+
import_Tooltip5 = __toESM(require("@mui/material/Tooltip"));
|
|
4917
4948
|
import_icons_material7 = require("@mui/icons-material");
|
|
4918
4949
|
import_recharts2 = require("recharts");
|
|
4919
4950
|
init_ui();
|
|
4920
4951
|
import_jsx_runtime15 = require("react/jsx-runtime");
|
|
4921
4952
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
4922
|
-
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, onExpand }) => {
|
|
4953
|
+
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand }) => {
|
|
4923
4954
|
const [expanded, setExpanded] = (0, import_react10.useState)(false);
|
|
4924
4955
|
const chart = useChartTheme();
|
|
4925
4956
|
const handleExpandChange = (newExpanded) => {
|
|
@@ -5029,6 +5060,17 @@ var init_TeamUsage = __esm({
|
|
|
5029
5060
|
children: "Edit"
|
|
5030
5061
|
}
|
|
5031
5062
|
),
|
|
5063
|
+
canManageMembers && onManageMembers && !canManage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Tooltip5.default, { title: "Add or remove members of this team", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
5064
|
+
import_Button6.default,
|
|
5065
|
+
{
|
|
5066
|
+
size: "small",
|
|
5067
|
+
variant: "text",
|
|
5068
|
+
startIcon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons_material7.GroupAddOutlined, {}),
|
|
5069
|
+
onClick: () => onManageMembers(team),
|
|
5070
|
+
sx: { textTransform: "none" },
|
|
5071
|
+
children: "Manage members"
|
|
5072
|
+
}
|
|
5073
|
+
) }),
|
|
5032
5074
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
5033
5075
|
import_IconButton6.default,
|
|
5034
5076
|
{
|
|
@@ -5159,9 +5201,23 @@ var init_TeamUsage = __esm({
|
|
|
5159
5201
|
onEditTeam,
|
|
5160
5202
|
canCreate,
|
|
5161
5203
|
onCreateTeam,
|
|
5162
|
-
onTeamExpand
|
|
5204
|
+
onTeamExpand,
|
|
5205
|
+
canManageMembers,
|
|
5206
|
+
onManageMembers,
|
|
5207
|
+
readOnly
|
|
5163
5208
|
}) => {
|
|
5164
|
-
const
|
|
5209
|
+
const readOnlyBadge = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Tooltip5.default, { title: "Teams and memberships are managed in your identity provider. Changes made here would be overwritten, so editing is disabled.", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
5210
|
+
import_Box14.default,
|
|
5211
|
+
{
|
|
5212
|
+
component: "span",
|
|
5213
|
+
sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "text.secondary", fontSize: 12.5 },
|
|
5214
|
+
children: [
|
|
5215
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons_material7.LockOutlined, { sx: { fontSize: 15 } }),
|
|
5216
|
+
"Synced from identity provider"
|
|
5217
|
+
]
|
|
5218
|
+
}
|
|
5219
|
+
) });
|
|
5220
|
+
const createButton = canCreate && onCreateTeam ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
5165
5221
|
import_Button6.default,
|
|
5166
5222
|
{
|
|
5167
5223
|
variant: "contained",
|
|
@@ -5172,6 +5228,7 @@ var init_TeamUsage = __esm({
|
|
|
5172
5228
|
children: "Create Team"
|
|
5173
5229
|
}
|
|
5174
5230
|
) : void 0;
|
|
5231
|
+
const createAction = readOnly ? readOnlyBadge : createButton;
|
|
5175
5232
|
if (loading) {
|
|
5176
5233
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(SectionCard, { title: "Teams", actions: createAction, children: [
|
|
5177
5234
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Skeleton5.default, { variant: "rounded", height: 120, sx: { mb: 2 } }),
|
|
@@ -5201,6 +5258,8 @@ var init_TeamUsage = __esm({
|
|
|
5201
5258
|
usageLoading: getTeamUsageLoading(team.team_id),
|
|
5202
5259
|
canManage,
|
|
5203
5260
|
onEditTeam,
|
|
5261
|
+
canManageMembers: canManageMembers?.(team),
|
|
5262
|
+
onManageMembers,
|
|
5204
5263
|
onExpand: onTeamExpand
|
|
5205
5264
|
},
|
|
5206
5265
|
team.team_id
|
|
@@ -5229,7 +5288,7 @@ function fmtTokens(n) {
|
|
|
5229
5288
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
5230
5289
|
return String(n);
|
|
5231
5290
|
}
|
|
5232
|
-
var import_react11, import_Box15, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography13, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment3, import_icons_material8, import_ContentCopy,
|
|
5291
|
+
var import_react11, import_Box15, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography13, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment3, import_icons_material8, import_ContentCopy, import_Tooltip6, import_Snackbar, import_Alert4, import_styles11, import_jsx_runtime16, ALL_PROXY_MODELS, ModelsTable;
|
|
5233
5292
|
var init_ModelsTable = __esm({
|
|
5234
5293
|
"src/components/ModelsTable.tsx"() {
|
|
5235
5294
|
"use strict";
|
|
@@ -5251,7 +5310,7 @@ var init_ModelsTable = __esm({
|
|
|
5251
5310
|
import_InputAdornment3 = __toESM(require("@mui/material/InputAdornment"));
|
|
5252
5311
|
import_icons_material8 = require("@mui/icons-material");
|
|
5253
5312
|
import_ContentCopy = __toESM(require("@mui/icons-material/ContentCopy"));
|
|
5254
|
-
|
|
5313
|
+
import_Tooltip6 = __toESM(require("@mui/material/Tooltip"));
|
|
5255
5314
|
import_Snackbar = __toESM(require("@mui/material/Snackbar"));
|
|
5256
5315
|
import_Alert4 = __toESM(require("@mui/material/Alert"));
|
|
5257
5316
|
init_ui();
|
|
@@ -5414,7 +5473,7 @@ var init_ModelsTable = __esm({
|
|
|
5414
5473
|
] }) }),
|
|
5415
5474
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_TableBody5.default, { children: filteredModels.map((m) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_TableRow5.default, { hover: true, children: [
|
|
5416
5475
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_TableCell5.default, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_Box15.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
|
|
5417
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5476
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_Tooltip6.default, { title: m.model_name, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5418
5477
|
"span",
|
|
5419
5478
|
{
|
|
5420
5479
|
role: "button",
|
|
@@ -5426,7 +5485,7 @@ var init_ModelsTable = __esm({
|
|
|
5426
5485
|
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
5427
5486
|
}
|
|
5428
5487
|
) }),
|
|
5429
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5488
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_Tooltip6.default, { title: "Copy model ID", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
5430
5489
|
import_Box15.default,
|
|
5431
5490
|
{
|
|
5432
5491
|
component: "span",
|
|
@@ -5836,6 +5895,19 @@ var init_feedback = __esm({
|
|
|
5836
5895
|
}
|
|
5837
5896
|
});
|
|
5838
5897
|
|
|
5898
|
+
// src/teamMemberManager.ts
|
|
5899
|
+
function isTeamMemberManager(team, userId, memberManagerRoles, readOnly) {
|
|
5900
|
+
if (!team || !userId || !memberManagerRoles || memberManagerRoles.length === 0 || readOnly) {
|
|
5901
|
+
return false;
|
|
5902
|
+
}
|
|
5903
|
+
return team.members_with_roles?.some((m) => m.user_id === userId && memberManagerRoles.includes(m.role)) ?? false;
|
|
5904
|
+
}
|
|
5905
|
+
var init_teamMemberManager = __esm({
|
|
5906
|
+
"src/teamMemberManager.ts"() {
|
|
5907
|
+
"use strict";
|
|
5908
|
+
}
|
|
5909
|
+
});
|
|
5910
|
+
|
|
5839
5911
|
// src/unprovisionedCopy.ts
|
|
5840
5912
|
function getUnprovisionedTitle() {
|
|
5841
5913
|
return "Your LiteLLM account isn't set up yet";
|
|
@@ -5912,6 +5984,7 @@ var init_LiteLLMPage = __esm({
|
|
|
5912
5984
|
init_dates();
|
|
5913
5985
|
init_permissions();
|
|
5914
5986
|
init_feedback();
|
|
5987
|
+
init_teamMemberManager();
|
|
5915
5988
|
init_unprovisionedCopy();
|
|
5916
5989
|
import_jsx_runtime18 = require("react/jsx-runtime");
|
|
5917
5990
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
@@ -6358,6 +6431,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6358
6431
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
6359
6432
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
6360
6433
|
const visibleTeams = Array.from(teamsById.values());
|
|
6434
|
+
const readOnly = liteLlmConfig?.teamManagement?.readOnly ?? false;
|
|
6435
|
+
const memberManagerRoles = liteLlmConfig?.teamManagement?.memberManagerRoles ?? [];
|
|
6361
6436
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
6362
6437
|
TeamUsage,
|
|
6363
6438
|
{
|
|
@@ -6365,10 +6440,13 @@ var init_LiteLLMPage = __esm({
|
|
|
6365
6440
|
loading: teamsLoading,
|
|
6366
6441
|
getTeamUsage: (teamId) => teamUsageCache[teamId] ?? null,
|
|
6367
6442
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
|
|
6368
|
-
canManage: teamMgmtEnabled && canManageTeam,
|
|
6443
|
+
canManage: teamMgmtEnabled && canManageTeam && !readOnly,
|
|
6369
6444
|
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
|
|
6370
|
-
canCreate: teamMgmtEnabled && canCreateTeam,
|
|
6445
|
+
canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
|
|
6371
6446
|
onCreateTeam: () => setManageTeam({ mode: "create" }),
|
|
6447
|
+
canManageMembers: (t) => teamMgmtEnabled && canManageMembers && isTeamMemberManager(t, userInfo?.user_id, memberManagerRoles, readOnly),
|
|
6448
|
+
onManageMembers: (t) => setManageTeam({ mode: "members", team: t }),
|
|
6449
|
+
readOnly,
|
|
6372
6450
|
onTeamExpand: (team) => loadTeamUsage(team.team_id)
|
|
6373
6451
|
}
|
|
6374
6452
|
);
|
|
@@ -6420,10 +6498,13 @@ var init_LiteLLMPage = __esm({
|
|
|
6420
6498
|
{
|
|
6421
6499
|
open: !!manageTeam,
|
|
6422
6500
|
onClose: () => setManageTeam(null),
|
|
6423
|
-
mode: manageTeam?.mode ?? "create",
|
|
6501
|
+
mode: manageTeam?.mode === "members" ? "edit" : manageTeam?.mode ?? "create",
|
|
6424
6502
|
team: manageTeam?.team,
|
|
6425
6503
|
allModels: allModels ?? [],
|
|
6426
6504
|
config: liteLlmConfig,
|
|
6505
|
+
membersOnly: manageTeam?.mode === "members",
|
|
6506
|
+
currentUserId: userInfo?.user_id,
|
|
6507
|
+
memberManagerRoles: liteLlmConfig?.teamManagement?.memberManagerRoles,
|
|
6427
6508
|
onSubmit: async (payload) => {
|
|
6428
6509
|
try {
|
|
6429
6510
|
if (manageTeam?.mode === "edit" && manageTeam.team) {
|
|
@@ -6444,7 +6525,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6444
6525
|
if (alert) alertApi.post(alert);
|
|
6445
6526
|
}
|
|
6446
6527
|
},
|
|
6447
|
-
canManageMembers: teamMgmtEnabled && canManageMembers,
|
|
6528
|
+
canManageMembers: teamMgmtEnabled && canManageMembers && !liteLlmConfig?.teamManagement?.readOnly,
|
|
6448
6529
|
onAddMember: async (userEntityRef, maxBudgetInTeam) => {
|
|
6449
6530
|
if (!manageTeam?.team) return;
|
|
6450
6531
|
const updated = await api.addTeamMember(manageTeam.team.team_id, {
|