mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
fix(ui): only offer team admins the teams they administer in the Team ID selector
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
a7daa21df7
commit
59693d5019
3 changed files with 41 additions and 7 deletions
|
|
@ -1668,6 +1668,32 @@ describe("ModelInfoView", () => {
|
|||
expect(payload.model_info.team_id).toBe("team-2");
|
||||
});
|
||||
|
||||
it("only offers a team admin the teams they administer", async () => {
|
||||
mockUseTeams.mockReturnValue({
|
||||
data: [
|
||||
{ team_id: "team-1", team_alias: "alpha", members_with_roles: [{ user_id: "123", role: "admin" }] },
|
||||
{ team_id: "team-2", team_alias: "beta", members_with_roles: [{ user_id: "123", role: "user" }] },
|
||||
{ team_id: "team-3", team_alias: "gamma", members_with_roles: [{ user_id: "123", role: "admin" }] },
|
||||
],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
const teamModel = {
|
||||
...defaultModelData,
|
||||
model_info: { ...defaultModelData.model_info, team_id: "team-1" },
|
||||
};
|
||||
mockUseModelsInfo.mockReturnValue({ data: { data: [teamModel] }, isLoading: false, error: null });
|
||||
mockModelInfoV1Call.mockResolvedValue({ data: [teamModel] });
|
||||
const user = userEvent.setup();
|
||||
render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} userRole="Internal User" />, { wrapper });
|
||||
await user.click(await screen.findByRole("button", { name: /edit settings/i }));
|
||||
|
||||
await user.click(await screen.findByText("alpha (team-1)"));
|
||||
|
||||
expect(await screen.findByRole("option", { name: "gamma (team-3)" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("option", { name: "beta (team-2)" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("sends the edited LiteLLM extra params", async () => {
|
||||
const user = userEvent.setup();
|
||||
await enterEditMode(user);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
isComplexityRouter as isComplexityRouterParams,
|
||||
} from "./add_model/auto_router_strategies";
|
||||
import { canEditAutoRouter, canModifyModel } from "@/utils/modelPermissions";
|
||||
import { teamsUserCanAssign } from "@/utils/roles";
|
||||
import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
|
||||
import DeleteResourceModal from "./common_components/DeleteResourceModal";
|
||||
import EditAutoRouterModal from "./edit_auto_router/edit_auto_router_modal";
|
||||
|
|
@ -130,6 +131,7 @@ export default function ModelInfoView({
|
|||
};
|
||||
const canEditModel = canModifyModel(actor, teams ?? null, origin);
|
||||
const canEditRouter = canEditAutoRouter(actor, teams ?? null, origin);
|
||||
const assignableTeams = useMemo(() => teamsUserCanAssign(teams ?? null, userRole, userID), [teams, userRole, userID]);
|
||||
// Editor-aware on purpose: an adaptive or quality router must not offer Edit Auto Router.
|
||||
const isAutoRouterModel = hasAutoRouterEditor(modelData?.litellm_params);
|
||||
// Broader than the editor check: adaptive and quality routers equally have no upstream
|
||||
|
|
@ -376,12 +378,7 @@ export default function ModelInfoView({
|
|||
health_check_model: values.health_check_model,
|
||||
};
|
||||
}
|
||||
if (values.team_id) {
|
||||
updatedModelInfo = {
|
||||
...updatedModelInfo,
|
||||
team_id: values.team_id,
|
||||
};
|
||||
}
|
||||
if (values.team_id) updatedModelInfo = { ...updatedModelInfo, team_id: values.team_id };
|
||||
updatedModelInfo = applyPtuModelInfo(updatedModelInfo, values, ptuCostAttributionEnabled);
|
||||
} catch (e) {
|
||||
toast.fromError("Invalid JSON in Model Info");
|
||||
|
|
@ -742,7 +739,7 @@ export default function ModelInfoView({
|
|||
tagsList={tagsList}
|
||||
credentialsList={credentialsList}
|
||||
healthCheckModelOptions={healthCheckModelOptions}
|
||||
teams={teams ?? null}
|
||||
teams={assignableTeams}
|
||||
/>
|
||||
) : (
|
||||
<p className="text-sm">Loading...</p>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,17 @@ export const isUserTeamAdminForSingleTeam = (teamMemberWithRoles: Member[] | nul
|
|||
return teamMemberWithRoles.some((member) => member.user_id === userID && member.role === "admin");
|
||||
};
|
||||
|
||||
export const teamsUserCanAssign = (
|
||||
teams: Team[] | null,
|
||||
userRole: string | null,
|
||||
userID: string | null,
|
||||
): Team[] | null => {
|
||||
if (teams == null || all_admin_roles.includes(userRole ?? "")) {
|
||||
return teams;
|
||||
}
|
||||
return teams.filter((team) => isUserTeamAdminForSingleTeam(team.members_with_roles, userID ?? ""));
|
||||
};
|
||||
|
||||
export const isOrgAdminForAnyOrg = (
|
||||
organizations: Organization[] | null | undefined,
|
||||
userID: string | null | undefined,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue