diff --git a/ui/litellm-dashboard/src/components/model_info_view.test.tsx b/ui/litellm-dashboard/src/components/model_info_view.test.tsx
index 33f77085551..c5a03b0eed1 100644
--- a/ui/litellm-dashboard/src/components/model_info_view.test.tsx
+++ b/ui/litellm-dashboard/src/components/model_info_view.test.tsx
@@ -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(, { 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);
diff --git a/ui/litellm-dashboard/src/components/model_info_view.tsx b/ui/litellm-dashboard/src/components/model_info_view.tsx
index ecf89bd7cb9..fa77b3d5739 100644
--- a/ui/litellm-dashboard/src/components/model_info_view.tsx
+++ b/ui/litellm-dashboard/src/components/model_info_view.tsx
@@ -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}
/>
) : (
Loading...
diff --git a/ui/litellm-dashboard/src/utils/roles.ts b/ui/litellm-dashboard/src/utils/roles.ts
index 62a5f02cc39..30b53e9b089 100644
--- a/ui/litellm-dashboard/src/utils/roles.ts
+++ b/ui/litellm-dashboard/src/utils/roles.ts
@@ -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,