@acarmisc/backstage-plugin-litellm 0.31.1 → 0.33.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 +16 -1
- package/dist/components/TeamUsage.d.ts +5 -0
- package/dist/index.cjs.js +356 -195
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.esm.js +369 -208
- 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([]);
|
|
@@ -3180,6 +3184,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3180
3184
|
const [memberBudget, setMemberBudget] = useState6("");
|
|
3181
3185
|
const [memberBusy, setMemberBusy] = useState6(false);
|
|
3182
3186
|
const [memberError, setMemberError] = useState6(null);
|
|
3187
|
+
const [pendingMembers, setPendingMembers] = useState6([]);
|
|
3183
3188
|
const [kbIds, setKbIds] = useState6([]);
|
|
3184
3189
|
const [kbBusy, setKbBusy] = useState6(false);
|
|
3185
3190
|
const [kbError, setKbError] = useState6(null);
|
|
@@ -3190,8 +3195,17 @@ var init_ManageTeamDialog = __esm({
|
|
|
3190
3195
|
const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
|
|
3191
3196
|
const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
|
|
3192
3197
|
const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
|
|
3193
|
-
const
|
|
3194
|
-
const
|
|
3198
|
+
const creating = mode === "create";
|
|
3199
|
+
const members = useMemo4(
|
|
3200
|
+
() => creating ? pendingMembers.map((p) => ({
|
|
3201
|
+
user_id: p.userEntityRef,
|
|
3202
|
+
user_email: p.label,
|
|
3203
|
+
role: "user"
|
|
3204
|
+
})) : team?.members_with_roles ?? [],
|
|
3205
|
+
[creating, pendingMembers, team]
|
|
3206
|
+
);
|
|
3207
|
+
const showMembers = !!canManageMembers || !!membersOnly;
|
|
3208
|
+
const wide = showMembers && !membersOnly;
|
|
3195
3209
|
const catalogApi = useApi3(catalogApiRef);
|
|
3196
3210
|
const [searchUsers, setSearchUsers] = useState6([]);
|
|
3197
3211
|
const [usersLoading, setUsersLoading] = useState6(false);
|
|
@@ -3236,8 +3250,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
3236
3250
|
email,
|
|
3237
3251
|
label: email ? `${displayName} (${email})` : displayName
|
|
3238
3252
|
};
|
|
3239
|
-
}).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
|
|
3240
|
-
}, [searchUsers, members]);
|
|
3253
|
+
}).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).filter((o) => !pendingMembers.some((p) => p.userEntityRef === o.ref)).sort((a, b) => a.label.localeCompare(b.label));
|
|
3254
|
+
}, [searchUsers, members, pendingMembers]);
|
|
3241
3255
|
useEffect4(() => {
|
|
3242
3256
|
return () => {
|
|
3243
3257
|
if (debounceTimerRef.current) {
|
|
@@ -3271,6 +3285,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3271
3285
|
setMemberRef("");
|
|
3272
3286
|
setMemberBudget("");
|
|
3273
3287
|
setMemberError(null);
|
|
3288
|
+
setPendingMembers([]);
|
|
3274
3289
|
setKbIds(team?.object_permission?.vector_stores ?? []);
|
|
3275
3290
|
setKbError(null);
|
|
3276
3291
|
setMcpIds(team?.object_permission?.mcp_servers ?? []);
|
|
@@ -3329,7 +3344,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
3329
3344
|
...budgetDuration && { budget_duration: budgetDuration }
|
|
3330
3345
|
};
|
|
3331
3346
|
}
|
|
3332
|
-
await onSubmit(
|
|
3347
|
+
await onSubmit(
|
|
3348
|
+
payload,
|
|
3349
|
+
creating ? pendingMembers.map(({ userEntityRef, maxBudgetInTeam }) => ({
|
|
3350
|
+
userEntityRef,
|
|
3351
|
+
...maxBudgetInTeam !== void 0 && { maxBudgetInTeam }
|
|
3352
|
+
})) : void 0
|
|
3353
|
+
);
|
|
3333
3354
|
onClose();
|
|
3334
3355
|
} catch (err) {
|
|
3335
3356
|
setError(err instanceof Error ? err.message : "An error occurred");
|
|
@@ -3338,13 +3359,26 @@ var init_ManageTeamDialog = __esm({
|
|
|
3338
3359
|
}
|
|
3339
3360
|
};
|
|
3340
3361
|
const handleAddMember = async () => {
|
|
3341
|
-
if (!
|
|
3362
|
+
if (!memberRef.trim() || !creating && !onAddMember) return;
|
|
3342
3363
|
setMemberError(null);
|
|
3343
|
-
const budget = memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3364
|
+
const budget = !membersOnly && memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3344
3365
|
if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
|
|
3345
3366
|
setMemberError("Max budget in team must be a positive number");
|
|
3346
3367
|
return;
|
|
3347
3368
|
}
|
|
3369
|
+
if (creating) {
|
|
3370
|
+
const ref = memberRef.trim();
|
|
3371
|
+
if (!pendingMembers.some((p) => p.userEntityRef === ref)) {
|
|
3372
|
+
setPendingMembers((list) => [
|
|
3373
|
+
...list,
|
|
3374
|
+
{ userEntityRef: ref, label: memberQuery.trim() || ref, maxBudgetInTeam: budget }
|
|
3375
|
+
]);
|
|
3376
|
+
}
|
|
3377
|
+
setMemberQuery("");
|
|
3378
|
+
setMemberRef("");
|
|
3379
|
+
setMemberBudget("");
|
|
3380
|
+
return;
|
|
3381
|
+
}
|
|
3348
3382
|
try {
|
|
3349
3383
|
setMemberBusy(true);
|
|
3350
3384
|
await onAddMember(memberRef.trim(), budget);
|
|
@@ -3358,6 +3392,10 @@ var init_ManageTeamDialog = __esm({
|
|
|
3358
3392
|
}
|
|
3359
3393
|
};
|
|
3360
3394
|
const handleRemoveMember = async (userId) => {
|
|
3395
|
+
if (creating) {
|
|
3396
|
+
setPendingMembers((list) => list.filter((p) => p.userEntityRef !== userId));
|
|
3397
|
+
return;
|
|
3398
|
+
}
|
|
3361
3399
|
if (!onRemoveMember) return;
|
|
3362
3400
|
setMemberError(null);
|
|
3363
3401
|
try {
|
|
@@ -3401,186 +3439,242 @@ var init_ManageTeamDialog = __esm({
|
|
|
3401
3439
|
const showMcpServers = mode === "edit" && !!canManageMcpServers;
|
|
3402
3440
|
const mcpOptions = (mcpServers ?? []).map((s) => s.id);
|
|
3403
3441
|
const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
|
|
3404
|
-
|
|
3405
|
-
|
|
3442
|
+
const isRemovalDisabled = (memberUserId, memberRole) => {
|
|
3443
|
+
if (!membersOnly) return { disabled: false };
|
|
3444
|
+
if (memberUserId === currentUserId) {
|
|
3445
|
+
return { disabled: true, reason: "You can't remove yourself" };
|
|
3446
|
+
}
|
|
3447
|
+
if (memberManagerRoles?.includes(memberRole)) {
|
|
3448
|
+
return { disabled: true, reason: `Only platform admins can remove a team ${memberRole}` };
|
|
3449
|
+
}
|
|
3450
|
+
return { disabled: false };
|
|
3451
|
+
};
|
|
3452
|
+
const submitLabel = creating ? "Create team" : "Save";
|
|
3453
|
+
const getDialogTitle = () => {
|
|
3454
|
+
if (membersOnly) {
|
|
3455
|
+
return `Manage members \u2014 ${team?.team_alias || "Untitled team"}`;
|
|
3456
|
+
}
|
|
3457
|
+
return mode === "create" ? "Create Team" : "Edit Team";
|
|
3458
|
+
};
|
|
3459
|
+
return /* @__PURE__ */ jsxs10(Dialog3, { open, onClose, maxWidth: wide ? "md" : "sm", fullWidth: true, children: [
|
|
3460
|
+
/* @__PURE__ */ jsx11(DialogTitle3, { children: getDialogTitle() }),
|
|
3406
3461
|
/* @__PURE__ */ jsxs10(DialogContent3, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
3407
3462
|
error && /* @__PURE__ */ jsx11(Alert2, { severity: "error", children: error }),
|
|
3408
|
-
/* @__PURE__ */
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
fullWidth: true
|
|
3416
|
-
}
|
|
3417
|
-
),
|
|
3418
|
-
/* @__PURE__ */ jsx11(
|
|
3419
|
-
Autocomplete2,
|
|
3420
|
-
{
|
|
3421
|
-
multiple: true,
|
|
3422
|
-
options: modelNames,
|
|
3423
|
-
value: models,
|
|
3424
|
-
onChange: (_, newValue) => setModels(newValue),
|
|
3425
|
-
disabled: submitting,
|
|
3426
|
-
renderInput: (params) => /* @__PURE__ */ jsx11(
|
|
3427
|
-
TextField3,
|
|
3428
|
-
{
|
|
3429
|
-
...params,
|
|
3430
|
-
label: "Models",
|
|
3431
|
-
required: true,
|
|
3432
|
-
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.)"
|
|
3433
|
-
}
|
|
3434
|
-
)
|
|
3435
|
-
}
|
|
3436
|
-
),
|
|
3437
|
-
/* @__PURE__ */ jsx11(
|
|
3438
|
-
TextField3,
|
|
3439
|
-
{
|
|
3440
|
-
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
3441
|
-
type: "number",
|
|
3442
|
-
value: maxBudget,
|
|
3443
|
-
onChange: (e) => setMaxBudget(e.target.value),
|
|
3444
|
-
disabled: submitting || unlimited,
|
|
3445
|
-
inputProps: { min: 0, max: maxBudgetCeiling },
|
|
3446
|
-
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
3447
|
-
fullWidth: true
|
|
3448
|
-
}
|
|
3449
|
-
),
|
|
3450
|
-
allowUnlimitedBudget && /* @__PURE__ */ jsx11(
|
|
3451
|
-
FormControlLabel2,
|
|
3452
|
-
{
|
|
3453
|
-
control: /* @__PURE__ */ jsx11(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
3454
|
-
label: "Unlimited Budget"
|
|
3455
|
-
}
|
|
3456
|
-
),
|
|
3457
|
-
/* @__PURE__ */ jsx11(
|
|
3458
|
-
TextField3,
|
|
3463
|
+
membersOnly && /* @__PURE__ */ jsxs10(Alert2, { severity: "info", children: [
|
|
3464
|
+
"As a team ",
|
|
3465
|
+
team?.members_with_roles?.find((m) => m.user_id === currentUserId)?.role || "member",
|
|
3466
|
+
" you can add or remove members. Budgets and models are managed by your platform admins."
|
|
3467
|
+
] }),
|
|
3468
|
+
/* @__PURE__ */ jsxs10(
|
|
3469
|
+
Box10,
|
|
3459
3470
|
{
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Members" }),
|
|
3473
|
-
memberError && /* @__PURE__ */ jsx11(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null), children: memberError }),
|
|
3474
|
-
members.length === 0 ? /* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "No members yet." }) : /* @__PURE__ */ jsx11(TableContainer2, { component: Paper4, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 }, children: /* @__PURE__ */ jsxs10(Table2, { size: "small", sx: dataTableSx, children: [
|
|
3475
|
-
/* @__PURE__ */ jsx11(TableHead2, { children: /* @__PURE__ */ jsxs10(TableRow2, { children: [
|
|
3476
|
-
/* @__PURE__ */ jsx11(TableCell2, { children: "User" }),
|
|
3477
|
-
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children: "Role" }),
|
|
3478
|
-
/* @__PURE__ */ jsx11(TableCell2, { align: "right", sx: { width: 40 } })
|
|
3479
|
-
] }) }),
|
|
3480
|
-
/* @__PURE__ */ jsx11(TableBody2, { children: members.map((m) => /* @__PURE__ */ jsxs10(TableRow2, { children: [
|
|
3481
|
-
/* @__PURE__ */ jsx11(TableCell2, { children: m.user_email ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
3482
|
-
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", children: m.user_email }),
|
|
3471
|
+
sx: {
|
|
3472
|
+
display: "grid",
|
|
3473
|
+
gridTemplateColumns: {
|
|
3474
|
+
xs: "1fr",
|
|
3475
|
+
md: wide ? "minmax(0, 1fr) minmax(0, 1fr)" : "1fr"
|
|
3476
|
+
},
|
|
3477
|
+
gap: 3,
|
|
3478
|
+
alignItems: "start"
|
|
3479
|
+
},
|
|
3480
|
+
children: [
|
|
3481
|
+
!membersOnly && /* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
3482
|
+
wide && /* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", children: "Settings" }),
|
|
3483
3483
|
/* @__PURE__ */ jsx11(
|
|
3484
|
-
|
|
3484
|
+
TextField3,
|
|
3485
3485
|
{
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3486
|
+
label: "Team Alias",
|
|
3487
|
+
value: alias,
|
|
3488
|
+
onChange: (e) => setAlias(e.target.value),
|
|
3489
|
+
disabled: submitting,
|
|
3490
|
+
fullWidth: true
|
|
3490
3491
|
}
|
|
3491
|
-
)
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
children: /* @__PURE__ */ jsx11(DeleteIcon, { fontSize: "small" })
|
|
3510
|
-
}
|
|
3511
|
-
) })
|
|
3512
|
-
] }, m.user_id)) })
|
|
3513
|
-
] }) }),
|
|
3514
|
-
/* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
|
|
3515
|
-
/* @__PURE__ */ jsx11(
|
|
3516
|
-
Autocomplete2,
|
|
3517
|
-
{
|
|
3518
|
-
freeSolo: true,
|
|
3519
|
-
autoHighlight: true,
|
|
3520
|
-
options: userOptions,
|
|
3521
|
-
loading: usersLoading,
|
|
3522
|
-
inputValue: memberQuery,
|
|
3523
|
-
onInputChange: (_, newInputValue, reason) => {
|
|
3524
|
-
setMemberQuery(newInputValue);
|
|
3525
|
-
if (reason === "input" || reason === "clear") {
|
|
3526
|
-
setMemberRef(newInputValue);
|
|
3527
|
-
handleUserSearch(newInputValue);
|
|
3492
|
+
),
|
|
3493
|
+
/* @__PURE__ */ jsx11(
|
|
3494
|
+
Autocomplete2,
|
|
3495
|
+
{
|
|
3496
|
+
multiple: true,
|
|
3497
|
+
options: modelNames,
|
|
3498
|
+
value: models,
|
|
3499
|
+
onChange: (_, newValue) => setModels(newValue),
|
|
3500
|
+
disabled: submitting,
|
|
3501
|
+
renderInput: (params) => /* @__PURE__ */ jsx11(
|
|
3502
|
+
TextField3,
|
|
3503
|
+
{
|
|
3504
|
+
...params,
|
|
3505
|
+
label: "Models",
|
|
3506
|
+
required: true,
|
|
3507
|
+
helperText: "Only models your admin allow-listed for teams. At least one is required."
|
|
3508
|
+
}
|
|
3509
|
+
)
|
|
3528
3510
|
}
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3511
|
+
),
|
|
3512
|
+
/* @__PURE__ */ jsxs10(
|
|
3513
|
+
Box10,
|
|
3514
|
+
{
|
|
3515
|
+
sx: {
|
|
3516
|
+
display: "grid",
|
|
3517
|
+
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
|
|
3518
|
+
gap: 2
|
|
3519
|
+
},
|
|
3520
|
+
children: [
|
|
3521
|
+
/* @__PURE__ */ jsx11(
|
|
3522
|
+
TextField3,
|
|
3523
|
+
{
|
|
3524
|
+
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
3525
|
+
type: "number",
|
|
3526
|
+
value: maxBudget,
|
|
3527
|
+
onChange: (e) => setMaxBudget(e.target.value),
|
|
3528
|
+
disabled: submitting || unlimited,
|
|
3529
|
+
inputProps: { min: 0, max: maxBudgetCeiling },
|
|
3530
|
+
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
3531
|
+
fullWidth: true
|
|
3532
|
+
}
|
|
3533
|
+
),
|
|
3534
|
+
/* @__PURE__ */ jsx11(
|
|
3535
|
+
TextField3,
|
|
3536
|
+
{
|
|
3537
|
+
select: true,
|
|
3538
|
+
label: "Budget Duration",
|
|
3539
|
+
value: budgetDuration,
|
|
3540
|
+
onChange: (e) => setBudgetDuration(e.target.value),
|
|
3541
|
+
disabled: submitting || unlimited,
|
|
3542
|
+
helperText: "Spend-reset period for the team budget",
|
|
3543
|
+
fullWidth: true,
|
|
3544
|
+
children: durationOptions.map((o) => /* @__PURE__ */ jsx11(MenuItem2, { value: o.value, children: o.label }, o.value))
|
|
3545
|
+
}
|
|
3546
|
+
)
|
|
3547
|
+
]
|
|
3534
3548
|
}
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
disabled: memberBusy,
|
|
3539
|
-
sx: { flex: 1, minWidth: 260 },
|
|
3540
|
-
renderInput: (params) => /* @__PURE__ */ jsx11(
|
|
3541
|
-
TextField3,
|
|
3549
|
+
),
|
|
3550
|
+
allowUnlimitedBudget && /* @__PURE__ */ jsx11(
|
|
3551
|
+
FormControlLabel2,
|
|
3542
3552
|
{
|
|
3543
|
-
|
|
3544
|
-
label: "
|
|
3545
|
-
placeholder: "Search by name or email\u2026",
|
|
3546
|
-
size: "small",
|
|
3547
|
-
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
3548
|
-
InputProps: {
|
|
3549
|
-
...params.InputProps,
|
|
3550
|
-
endAdornment: /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
3551
|
-
usersLoading ? /* @__PURE__ */ jsx11(CircularProgress3, { color: "inherit", size: 14 }) : null,
|
|
3552
|
-
params.InputProps.endAdornment
|
|
3553
|
-
] })
|
|
3554
|
-
}
|
|
3553
|
+
control: /* @__PURE__ */ jsx11(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
3554
|
+
label: "Unlimited Budget"
|
|
3555
3555
|
}
|
|
3556
3556
|
)
|
|
3557
|
-
}
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3557
|
+
] }),
|
|
3558
|
+
showMembers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3559
|
+
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: creating ? 0.25 : 1 }, children: creating ? "Initial members" : "Members" }),
|
|
3560
|
+
creating && /* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Added as team users right after the team is created." }),
|
|
3561
|
+
memberError && /* @__PURE__ */ jsx11(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null), children: memberError }),
|
|
3562
|
+
members.length === 0 ? /* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: creating ? "No members picked yet \u2014 you can also add them later." : "No members yet." }) : /* @__PURE__ */ jsx11(TableContainer2, { component: Paper4, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 }, children: /* @__PURE__ */ jsxs10(Table2, { size: "small", sx: dataTableSx, children: [
|
|
3563
|
+
/* @__PURE__ */ jsx11(TableHead2, { children: /* @__PURE__ */ jsxs10(TableRow2, { children: [
|
|
3564
|
+
/* @__PURE__ */ jsx11(TableCell2, { children: "User" }),
|
|
3565
|
+
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children: "Role" }),
|
|
3566
|
+
/* @__PURE__ */ jsx11(TableCell2, { align: "right", sx: { width: 40 } })
|
|
3567
|
+
] }) }),
|
|
3568
|
+
/* @__PURE__ */ jsx11(TableBody2, { children: members.map((m) => /* @__PURE__ */ jsxs10(TableRow2, { children: [
|
|
3569
|
+
/* @__PURE__ */ jsx11(TableCell2, { children: m.user_email ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
3570
|
+
/* @__PURE__ */ jsx11(Typography8, { variant: "body2", children: m.user_email }),
|
|
3571
|
+
/* @__PURE__ */ jsx11(
|
|
3572
|
+
Typography8,
|
|
3573
|
+
{
|
|
3574
|
+
variant: "caption",
|
|
3575
|
+
color: "text.secondary",
|
|
3576
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 },
|
|
3577
|
+
children: m.user_id
|
|
3578
|
+
}
|
|
3579
|
+
)
|
|
3580
|
+
] }) : /* @__PURE__ */ jsx11(
|
|
3581
|
+
Typography8,
|
|
3582
|
+
{
|
|
3583
|
+
variant: "body2",
|
|
3584
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" },
|
|
3585
|
+
children: m.user_id
|
|
3586
|
+
}
|
|
3587
|
+
) }),
|
|
3588
|
+
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children: /* @__PURE__ */ jsx11(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
|
|
3589
|
+
/* @__PURE__ */ jsx11(TableCell2, { align: "right", children: (() => {
|
|
3590
|
+
const { disabled, reason } = isRemovalDisabled(m.user_id, m.role);
|
|
3591
|
+
return /* @__PURE__ */ jsx11(Tooltip4, { title: reason ?? "", children: /* @__PURE__ */ jsx11("span", { children: /* @__PURE__ */ jsx11(
|
|
3592
|
+
IconButton4,
|
|
3593
|
+
{
|
|
3594
|
+
edge: "end",
|
|
3595
|
+
size: "small",
|
|
3596
|
+
"aria-label": `remove ${m.user_id}`,
|
|
3597
|
+
disabled: memberBusy || disabled,
|
|
3598
|
+
onClick: () => handleRemoveMember(m.user_id),
|
|
3599
|
+
children: /* @__PURE__ */ jsx11(DeleteIcon, { fontSize: "small" })
|
|
3600
|
+
}
|
|
3601
|
+
) }) });
|
|
3602
|
+
})() })
|
|
3603
|
+
] }, m.user_id)) })
|
|
3604
|
+
] }) }),
|
|
3605
|
+
/* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
|
|
3606
|
+
/* @__PURE__ */ jsx11(
|
|
3607
|
+
Autocomplete2,
|
|
3608
|
+
{
|
|
3609
|
+
freeSolo: true,
|
|
3610
|
+
autoHighlight: true,
|
|
3611
|
+
options: userOptions,
|
|
3612
|
+
loading: usersLoading,
|
|
3613
|
+
inputValue: memberQuery,
|
|
3614
|
+
onInputChange: (_, newInputValue, reason) => {
|
|
3615
|
+
setMemberQuery(newInputValue);
|
|
3616
|
+
if (reason === "input" || reason === "clear") {
|
|
3617
|
+
setMemberRef(newInputValue);
|
|
3618
|
+
handleUserSearch(newInputValue);
|
|
3619
|
+
}
|
|
3620
|
+
},
|
|
3621
|
+
onChange: (_, newValue) => {
|
|
3622
|
+
if (newValue && typeof newValue !== "string") {
|
|
3623
|
+
setMemberQuery(newValue.label);
|
|
3624
|
+
setMemberRef(newValue.ref);
|
|
3625
|
+
}
|
|
3626
|
+
},
|
|
3627
|
+
getOptionLabel: (o) => typeof o === "string" ? o : o.label,
|
|
3628
|
+
isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
|
|
3629
|
+
disabled: memberBusy,
|
|
3630
|
+
sx: { flex: "1 1 240px" },
|
|
3631
|
+
renderInput: (params) => /* @__PURE__ */ jsx11(
|
|
3632
|
+
TextField3,
|
|
3633
|
+
{
|
|
3634
|
+
...params,
|
|
3635
|
+
label: "Add member",
|
|
3636
|
+
placeholder: "Search by name or email\u2026",
|
|
3637
|
+
size: "small",
|
|
3638
|
+
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
3639
|
+
InputProps: {
|
|
3640
|
+
...params.InputProps,
|
|
3641
|
+
endAdornment: /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
3642
|
+
usersLoading ? /* @__PURE__ */ jsx11(CircularProgress3, { color: "inherit", size: 14 }) : null,
|
|
3643
|
+
params.InputProps.endAdornment
|
|
3644
|
+
] })
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
)
|
|
3648
|
+
}
|
|
3649
|
+
),
|
|
3650
|
+
!membersOnly && /* @__PURE__ */ jsx11(
|
|
3651
|
+
TextField3,
|
|
3652
|
+
{
|
|
3653
|
+
label: "Max budget in team ($)",
|
|
3654
|
+
type: "number",
|
|
3655
|
+
value: memberBudget,
|
|
3656
|
+
onChange: (e) => setMemberBudget(e.target.value),
|
|
3657
|
+
disabled: memberBusy,
|
|
3658
|
+
size: "small",
|
|
3659
|
+
sx: { width: 150 }
|
|
3660
|
+
}
|
|
3661
|
+
),
|
|
3662
|
+
/* @__PURE__ */ jsx11(
|
|
3663
|
+
Button4,
|
|
3664
|
+
{
|
|
3665
|
+
onClick: handleAddMember,
|
|
3666
|
+
disabled: memberBusy || !memberRef.trim(),
|
|
3667
|
+
variant: "outlined",
|
|
3668
|
+
sx: { mt: 0.5 },
|
|
3669
|
+
children: "Add"
|
|
3670
|
+
}
|
|
3671
|
+
)
|
|
3672
|
+
] })
|
|
3673
|
+
] })
|
|
3674
|
+
]
|
|
3675
|
+
}
|
|
3676
|
+
),
|
|
3677
|
+
!membersOnly && showKnowledgeBases && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3584
3678
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3585
3679
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Knowledge Bases" }),
|
|
3586
3680
|
/* @__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 +3701,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3607
3701
|
}
|
|
3608
3702
|
) })
|
|
3609
3703
|
] }),
|
|
3610
|
-
showMcpServers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3704
|
+
!membersOnly && showMcpServers && /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
3611
3705
|
/* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
|
|
3612
3706
|
/* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "MCP Servers" }),
|
|
3613
3707
|
/* @__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 +3730,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
3636
3730
|
] })
|
|
3637
3731
|
] }),
|
|
3638
3732
|
/* @__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..." :
|
|
3733
|
+
/* @__PURE__ */ jsx11(Button4, { onClick: onClose, disabled: submitting, children: membersOnly ? "Done" : "Cancel" }),
|
|
3734
|
+
!membersOnly && /* @__PURE__ */ jsx11(Button4, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : submitLabel })
|
|
3641
3735
|
] })
|
|
3642
3736
|
] });
|
|
3643
3737
|
};
|
|
@@ -3848,7 +3942,7 @@ import {
|
|
|
3848
3942
|
XAxis,
|
|
3849
3943
|
YAxis,
|
|
3850
3944
|
CartesianGrid,
|
|
3851
|
-
Tooltip as
|
|
3945
|
+
Tooltip as Tooltip5,
|
|
3852
3946
|
ResponsiveContainer,
|
|
3853
3947
|
Legend,
|
|
3854
3948
|
ReferenceLine,
|
|
@@ -4185,7 +4279,7 @@ var init_UsageStats = __esm({
|
|
|
4185
4279
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4186
4280
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }),
|
|
4187
4281
|
/* @__PURE__ */ jsx12(
|
|
4188
|
-
|
|
4282
|
+
Tooltip5,
|
|
4189
4283
|
{
|
|
4190
4284
|
cursor: chart.cursor,
|
|
4191
4285
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
|
|
@@ -4211,7 +4305,7 @@ var init_UsageStats = __esm({
|
|
|
4211
4305
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid }),
|
|
4212
4306
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4213
4307
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }),
|
|
4214
|
-
/* @__PURE__ */ jsx12(
|
|
4308
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt }) }),
|
|
4215
4309
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4216
4310
|
/* @__PURE__ */ jsx12(Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }),
|
|
4217
4311
|
/* @__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 +4314,7 @@ var init_UsageStats = __esm({
|
|
|
4220
4314
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid }),
|
|
4221
4315
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4222
4316
|
/* @__PURE__ */ jsx12(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }),
|
|
4223
|
-
/* @__PURE__ */ jsx12(
|
|
4317
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt }) }),
|
|
4224
4318
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4225
4319
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }),
|
|
4226
4320
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })
|
|
@@ -4230,7 +4324,7 @@ var init_UsageStats = __esm({
|
|
|
4230
4324
|
/* @__PURE__ */ jsx12(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4231
4325
|
/* @__PURE__ */ jsx12(YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }),
|
|
4232
4326
|
/* @__PURE__ */ jsx12(
|
|
4233
|
-
|
|
4327
|
+
Tooltip5,
|
|
4234
4328
|
{
|
|
4235
4329
|
cursor: chart.cursor,
|
|
4236
4330
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: (v) => `${v}%` })
|
|
@@ -4267,7 +4361,7 @@ var init_UsageStats = __esm({
|
|
|
4267
4361
|
}
|
|
4268
4362
|
),
|
|
4269
4363
|
/* @__PURE__ */ jsx12(
|
|
4270
|
-
|
|
4364
|
+
Tooltip5,
|
|
4271
4365
|
{
|
|
4272
4366
|
cursor: chart.cursor,
|
|
4273
4367
|
content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd })
|
|
@@ -4309,7 +4403,7 @@ var init_UsageStats = __esm({
|
|
|
4309
4403
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4310
4404
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }),
|
|
4311
4405
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4312
|
-
/* @__PURE__ */ jsx12(
|
|
4406
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }),
|
|
4313
4407
|
/* @__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
4408
|
] }) })
|
|
4315
4409
|
}
|
|
@@ -4324,7 +4418,7 @@ var init_UsageStats = __esm({
|
|
|
4324
4418
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4325
4419
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }),
|
|
4326
4420
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4327
|
-
/* @__PURE__ */ jsx12(
|
|
4421
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }),
|
|
4328
4422
|
/* @__PURE__ */ jsx12(Legend, { ...chart.legend }),
|
|
4329
4423
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }),
|
|
4330
4424
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })
|
|
@@ -4356,7 +4450,7 @@ var init_UsageStats = __esm({
|
|
|
4356
4450
|
/* @__PURE__ */ jsx12(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }),
|
|
4357
4451
|
/* @__PURE__ */ jsx12(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }),
|
|
4358
4452
|
/* @__PURE__ */ jsx12(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }),
|
|
4359
|
-
/* @__PURE__ */ jsx12(
|
|
4453
|
+
/* @__PURE__ */ jsx12(Tooltip5, { cursor: chart.cursor, content: /* @__PURE__ */ jsx12(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }),
|
|
4360
4454
|
/* @__PURE__ */ jsx12(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })
|
|
4361
4455
|
] }) })
|
|
4362
4456
|
}
|
|
@@ -4891,14 +4985,15 @@ import IconButton6 from "@mui/material/IconButton";
|
|
|
4891
4985
|
import Button6 from "@mui/material/Button";
|
|
4892
4986
|
import CircularProgress4 from "@mui/material/CircularProgress";
|
|
4893
4987
|
import { alpha as alpha10 } from "@mui/material/styles";
|
|
4894
|
-
import
|
|
4988
|
+
import MuiTooltip from "@mui/material/Tooltip";
|
|
4989
|
+
import { ExpandMore as ExpandMore3, Group, Add as Add2, LockOutlined, GroupAddOutlined } from "@mui/icons-material";
|
|
4895
4990
|
import {
|
|
4896
4991
|
AreaChart as AreaChart2,
|
|
4897
4992
|
Area as Area2,
|
|
4898
4993
|
XAxis as XAxis2,
|
|
4899
4994
|
YAxis as YAxis2,
|
|
4900
4995
|
CartesianGrid as CartesianGrid2,
|
|
4901
|
-
Tooltip as
|
|
4996
|
+
Tooltip as Tooltip6,
|
|
4902
4997
|
ResponsiveContainer as ResponsiveContainer2
|
|
4903
4998
|
} from "recharts";
|
|
4904
4999
|
import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
@@ -4922,7 +5017,7 @@ var init_TeamUsage = __esm({
|
|
|
4922
5017
|
"use strict";
|
|
4923
5018
|
init_ui();
|
|
4924
5019
|
fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
|
|
4925
|
-
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, onExpand }) => {
|
|
5020
|
+
TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand }) => {
|
|
4926
5021
|
const [expanded, setExpanded] = useState9(false);
|
|
4927
5022
|
const chart = useChartTheme();
|
|
4928
5023
|
const handleExpandChange = (newExpanded) => {
|
|
@@ -4959,7 +5054,7 @@ var init_TeamUsage = __esm({
|
|
|
4959
5054
|
/* @__PURE__ */ jsx15(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }),
|
|
4960
5055
|
/* @__PURE__ */ jsx15(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }),
|
|
4961
5056
|
/* @__PURE__ */ jsx15(
|
|
4962
|
-
|
|
5057
|
+
Tooltip6,
|
|
4963
5058
|
{
|
|
4964
5059
|
cursor: chart.cursor,
|
|
4965
5060
|
content: /* @__PURE__ */ jsx15(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
|
|
@@ -5032,6 +5127,17 @@ var init_TeamUsage = __esm({
|
|
|
5032
5127
|
children: "Edit"
|
|
5033
5128
|
}
|
|
5034
5129
|
),
|
|
5130
|
+
canManageMembers && onManageMembers && !canManage && /* @__PURE__ */ jsx15(MuiTooltip, { title: "Add or remove members of this team", children: /* @__PURE__ */ jsx15(
|
|
5131
|
+
Button6,
|
|
5132
|
+
{
|
|
5133
|
+
size: "small",
|
|
5134
|
+
variant: "text",
|
|
5135
|
+
startIcon: /* @__PURE__ */ jsx15(GroupAddOutlined, {}),
|
|
5136
|
+
onClick: () => onManageMembers(team),
|
|
5137
|
+
sx: { textTransform: "none" },
|
|
5138
|
+
children: "Manage members"
|
|
5139
|
+
}
|
|
5140
|
+
) }),
|
|
5035
5141
|
/* @__PURE__ */ jsx15(
|
|
5036
5142
|
IconButton6,
|
|
5037
5143
|
{
|
|
@@ -5162,9 +5268,23 @@ var init_TeamUsage = __esm({
|
|
|
5162
5268
|
onEditTeam,
|
|
5163
5269
|
canCreate,
|
|
5164
5270
|
onCreateTeam,
|
|
5165
|
-
onTeamExpand
|
|
5271
|
+
onTeamExpand,
|
|
5272
|
+
canManageMembers,
|
|
5273
|
+
onManageMembers,
|
|
5274
|
+
readOnly
|
|
5166
5275
|
}) => {
|
|
5167
|
-
const
|
|
5276
|
+
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(
|
|
5277
|
+
Box14,
|
|
5278
|
+
{
|
|
5279
|
+
component: "span",
|
|
5280
|
+
sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "text.secondary", fontSize: 12.5 },
|
|
5281
|
+
children: [
|
|
5282
|
+
/* @__PURE__ */ jsx15(LockOutlined, { sx: { fontSize: 15 } }),
|
|
5283
|
+
"Synced from identity provider"
|
|
5284
|
+
]
|
|
5285
|
+
}
|
|
5286
|
+
) });
|
|
5287
|
+
const createButton = canCreate && onCreateTeam ? /* @__PURE__ */ jsx15(
|
|
5168
5288
|
Button6,
|
|
5169
5289
|
{
|
|
5170
5290
|
variant: "contained",
|
|
@@ -5175,6 +5295,7 @@ var init_TeamUsage = __esm({
|
|
|
5175
5295
|
children: "Create Team"
|
|
5176
5296
|
}
|
|
5177
5297
|
) : void 0;
|
|
5298
|
+
const createAction = readOnly ? readOnlyBadge : createButton;
|
|
5178
5299
|
if (loading) {
|
|
5179
5300
|
return /* @__PURE__ */ jsxs14(SectionCard, { title: "Teams", actions: createAction, children: [
|
|
5180
5301
|
/* @__PURE__ */ jsx15(Skeleton5, { variant: "rounded", height: 120, sx: { mb: 2 } }),
|
|
@@ -5204,6 +5325,8 @@ var init_TeamUsage = __esm({
|
|
|
5204
5325
|
usageLoading: getTeamUsageLoading(team.team_id),
|
|
5205
5326
|
canManage,
|
|
5206
5327
|
onEditTeam,
|
|
5328
|
+
canManageMembers: canManageMembers?.(team),
|
|
5329
|
+
onManageMembers,
|
|
5207
5330
|
onExpand: onTeamExpand
|
|
5208
5331
|
},
|
|
5209
5332
|
team.team_id
|
|
@@ -5233,7 +5356,7 @@ import TextField5 from "@mui/material/TextField";
|
|
|
5233
5356
|
import InputAdornment3 from "@mui/material/InputAdornment";
|
|
5234
5357
|
import { Search as Search2 } from "@mui/icons-material";
|
|
5235
5358
|
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
|
5236
|
-
import
|
|
5359
|
+
import Tooltip7 from "@mui/material/Tooltip";
|
|
5237
5360
|
import Snackbar from "@mui/material/Snackbar";
|
|
5238
5361
|
import Alert4 from "@mui/material/Alert";
|
|
5239
5362
|
import { alpha as alpha11, useTheme as useTheme3 } from "@mui/material/styles";
|
|
@@ -5417,7 +5540,7 @@ var init_ModelsTable = __esm({
|
|
|
5417
5540
|
] }) }),
|
|
5418
5541
|
/* @__PURE__ */ jsx16(TableBody5, { children: filteredModels.map((m) => /* @__PURE__ */ jsxs15(TableRow5, { hover: true, children: [
|
|
5419
5542
|
/* @__PURE__ */ jsx16(TableCell5, { children: /* @__PURE__ */ jsxs15(Box15, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
|
|
5420
|
-
/* @__PURE__ */ jsx16(
|
|
5543
|
+
/* @__PURE__ */ jsx16(Tooltip7, { title: m.model_name, placement: "top", children: /* @__PURE__ */ jsx16(
|
|
5421
5544
|
"span",
|
|
5422
5545
|
{
|
|
5423
5546
|
role: "button",
|
|
@@ -5429,7 +5552,7 @@ var init_ModelsTable = __esm({
|
|
|
5429
5552
|
children: /* @__PURE__ */ jsx16(TagChip, { label: m.model_name, title: "Copy model ID" })
|
|
5430
5553
|
}
|
|
5431
5554
|
) }),
|
|
5432
|
-
/* @__PURE__ */ jsx16(
|
|
5555
|
+
/* @__PURE__ */ jsx16(Tooltip7, { title: "Copy model ID", children: /* @__PURE__ */ jsx16(
|
|
5433
5556
|
Box15,
|
|
5434
5557
|
{
|
|
5435
5558
|
component: "span",
|
|
@@ -5852,6 +5975,19 @@ var init_feedback = __esm({
|
|
|
5852
5975
|
}
|
|
5853
5976
|
});
|
|
5854
5977
|
|
|
5978
|
+
// src/teamMemberManager.ts
|
|
5979
|
+
function isTeamMemberManager(team, userId, memberManagerRoles, readOnly) {
|
|
5980
|
+
if (!team || !userId || !memberManagerRoles || memberManagerRoles.length === 0 || readOnly) {
|
|
5981
|
+
return false;
|
|
5982
|
+
}
|
|
5983
|
+
return team.members_with_roles?.some((m) => m.user_id === userId && memberManagerRoles.includes(m.role)) ?? false;
|
|
5984
|
+
}
|
|
5985
|
+
var init_teamMemberManager = __esm({
|
|
5986
|
+
"src/teamMemberManager.ts"() {
|
|
5987
|
+
"use strict";
|
|
5988
|
+
}
|
|
5989
|
+
});
|
|
5990
|
+
|
|
5855
5991
|
// src/unprovisionedCopy.ts
|
|
5856
5992
|
function getUnprovisionedTitle() {
|
|
5857
5993
|
return "Your LiteLLM account isn't set up yet";
|
|
@@ -5929,6 +6065,7 @@ var init_LiteLLMPage = __esm({
|
|
|
5929
6065
|
init_dates();
|
|
5930
6066
|
init_permissions();
|
|
5931
6067
|
init_feedback();
|
|
6068
|
+
init_teamMemberManager();
|
|
5932
6069
|
init_unprovisionedCopy();
|
|
5933
6070
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
5934
6071
|
PAGE_TABS = ["overview", "keys", "teams", "models", "audit"];
|
|
@@ -6374,6 +6511,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6374
6511
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
6375
6512
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
6376
6513
|
const visibleTeams = Array.from(teamsById.values());
|
|
6514
|
+
const readOnly = liteLlmConfig?.teamManagement?.readOnly ?? false;
|
|
6515
|
+
const memberManagerRoles = liteLlmConfig?.teamManagement?.memberManagerRoles ?? [];
|
|
6377
6516
|
return /* @__PURE__ */ jsx18(
|
|
6378
6517
|
TeamUsage,
|
|
6379
6518
|
{
|
|
@@ -6381,10 +6520,13 @@ var init_LiteLLMPage = __esm({
|
|
|
6381
6520
|
loading: teamsLoading,
|
|
6382
6521
|
getTeamUsage: (teamId) => teamUsageCache[teamId] ?? null,
|
|
6383
6522
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
|
|
6384
|
-
canManage: teamMgmtEnabled && canManageTeam,
|
|
6523
|
+
canManage: teamMgmtEnabled && canManageTeam && !readOnly,
|
|
6385
6524
|
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
|
|
6386
|
-
canCreate: teamMgmtEnabled && canCreateTeam,
|
|
6525
|
+
canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
|
|
6387
6526
|
onCreateTeam: () => setManageTeam({ mode: "create" }),
|
|
6527
|
+
canManageMembers: (t) => teamMgmtEnabled && canManageMembers && isTeamMemberManager(t, userInfo?.user_id, memberManagerRoles, readOnly),
|
|
6528
|
+
onManageMembers: (t) => setManageTeam({ mode: "members", team: t }),
|
|
6529
|
+
readOnly,
|
|
6388
6530
|
onTeamExpand: (team) => loadTeamUsage(team.team_id)
|
|
6389
6531
|
}
|
|
6390
6532
|
);
|
|
@@ -6436,11 +6578,14 @@ var init_LiteLLMPage = __esm({
|
|
|
6436
6578
|
{
|
|
6437
6579
|
open: !!manageTeam,
|
|
6438
6580
|
onClose: () => setManageTeam(null),
|
|
6439
|
-
mode: manageTeam?.mode ?? "create",
|
|
6581
|
+
mode: manageTeam?.mode === "members" ? "edit" : manageTeam?.mode ?? "create",
|
|
6440
6582
|
team: manageTeam?.team,
|
|
6441
6583
|
allModels: allModels ?? [],
|
|
6442
6584
|
config: liteLlmConfig,
|
|
6443
|
-
|
|
6585
|
+
membersOnly: manageTeam?.mode === "members",
|
|
6586
|
+
currentUserId: userInfo?.user_id,
|
|
6587
|
+
memberManagerRoles: liteLlmConfig?.teamManagement?.memberManagerRoles,
|
|
6588
|
+
onSubmit: async (payload, initialMembers) => {
|
|
6444
6589
|
try {
|
|
6445
6590
|
if (manageTeam?.mode === "edit" && manageTeam.team) {
|
|
6446
6591
|
const loadedAt = manageTeam.team.metadata?.updated_at_iso;
|
|
@@ -6450,7 +6595,23 @@ var init_LiteLLMPage = __esm({
|
|
|
6450
6595
|
...typeof loadedAt === "string" ? { expectedUpdatedAtIso: loadedAt } : {}
|
|
6451
6596
|
});
|
|
6452
6597
|
} else {
|
|
6453
|
-
await api.createTeam(payload);
|
|
6598
|
+
const created = await api.createTeam(payload);
|
|
6599
|
+
const failed = [];
|
|
6600
|
+
for (const m of initialMembers ?? []) {
|
|
6601
|
+
try {
|
|
6602
|
+
await api.addTeamMember(created.team_id, m);
|
|
6603
|
+
} catch (e) {
|
|
6604
|
+
failed.push(`${m.userEntityRef} (${e.message})`);
|
|
6605
|
+
}
|
|
6606
|
+
}
|
|
6607
|
+
if (failed.length) {
|
|
6608
|
+
alertApi.post({
|
|
6609
|
+
message: `Team created, but some members could not be added: ${failed.join(", ")}`,
|
|
6610
|
+
severity: "warning"
|
|
6611
|
+
});
|
|
6612
|
+
refreshTeams();
|
|
6613
|
+
return;
|
|
6614
|
+
}
|
|
6454
6615
|
}
|
|
6455
6616
|
const alert = toastFor("teamSaveSuccess");
|
|
6456
6617
|
if (alert) alertApi.post(alert);
|
|
@@ -6460,7 +6621,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6460
6621
|
if (alert) alertApi.post(alert);
|
|
6461
6622
|
}
|
|
6462
6623
|
},
|
|
6463
|
-
canManageMembers: teamMgmtEnabled && canManageMembers,
|
|
6624
|
+
canManageMembers: teamMgmtEnabled && canManageMembers && !liteLlmConfig?.teamManagement?.readOnly,
|
|
6464
6625
|
onAddMember: async (userEntityRef, maxBudgetInTeam) => {
|
|
6465
6626
|
if (!manageTeam?.team) return;
|
|
6466
6627
|
const updated = await api.addTeamMember(manageTeam.team.team_id, {
|
|
@@ -6584,7 +6745,7 @@ import FormControl2 from "@mui/material/FormControl";
|
|
|
6584
6745
|
import Select2 from "@mui/material/Select";
|
|
6585
6746
|
import MenuItem6 from "@mui/material/MenuItem";
|
|
6586
6747
|
import Skeleton7 from "@mui/material/Skeleton";
|
|
6587
|
-
import { AreaChart as AreaChart3, Area as Area3, XAxis as XAxis3, Tooltip as
|
|
6748
|
+
import { AreaChart as AreaChart3, Area as Area3, XAxis as XAxis3, Tooltip as Tooltip8, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
|
|
6588
6749
|
import { useApi as useApi5 } from "@backstage/core-plugin-api";
|
|
6589
6750
|
import { useRouteRef as useRouteRef2 } from "@backstage/frontend-plugin-api";
|
|
6590
6751
|
import { Link as Link3 } from "@backstage/core-components";
|
|
@@ -6749,7 +6910,7 @@ var LiteLLMHomeWidget = ({
|
|
|
6749
6910
|
sx: { mb: 0.5 },
|
|
6750
6911
|
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
6912
|
/* @__PURE__ */ jsx20(XAxis3, { dataKey: "date", hide: true }),
|
|
6752
|
-
/* @__PURE__ */ jsx20(
|
|
6913
|
+
/* @__PURE__ */ jsx20(Tooltip8, { content: /* @__PURE__ */ jsx20(ChartTooltip, { valueFormatter: fmtUsd }) }),
|
|
6753
6914
|
/* @__PURE__ */ jsx20(
|
|
6754
6915
|
Area3,
|
|
6755
6916
|
{
|
|
@@ -6796,7 +6957,7 @@ import Collapse4 from "@mui/material/Collapse";
|
|
|
6796
6957
|
import { useTheme as useTheme4 } from "@mui/material/styles";
|
|
6797
6958
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
6798
6959
|
import { ExpandMore as ExpandMore4 } from "@mui/icons-material";
|
|
6799
|
-
import { AreaChart as AreaChart4, Area as Area4, ResponsiveContainer as ResponsiveContainer4, Tooltip as
|
|
6960
|
+
import { AreaChart as AreaChart4, Area as Area4, ResponsiveContainer as ResponsiveContainer4, Tooltip as Tooltip9, ReferenceLine as ReferenceLine2, XAxis as XAxis4 } from "recharts";
|
|
6800
6961
|
import { useApi as useApi6 } from "@backstage/core-plugin-api";
|
|
6801
6962
|
import { Link as Link4 } from "@backstage/core-components";
|
|
6802
6963
|
|
|
@@ -7098,7 +7259,7 @@ var SpendStrip = ({ data, mtdSpend, loading, height, usageUnavailable = false, m
|
|
|
7098
7259
|
}
|
|
7099
7260
|
return /* @__PURE__ */ jsx21(ResponsiveContainer4, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs19(AreaChart4, { data, margin: { top: 4, right: 0, bottom: 0, left: 0 }, children: [
|
|
7100
7261
|
/* @__PURE__ */ jsx21(XAxis4, { dataKey: "date", hide: true }),
|
|
7101
|
-
/* @__PURE__ */ jsx21(
|
|
7262
|
+
/* @__PURE__ */ jsx21(Tooltip9, { content: /* @__PURE__ */ jsx21(ChartTooltip, { valueFormatter: fmtUsd }) }),
|
|
7102
7263
|
maxBudget !== void 0 && /* @__PURE__ */ jsx21(
|
|
7103
7264
|
ReferenceLine2,
|
|
7104
7265
|
{
|