refactor(proxy): move team roster refusal mapping out of endpoints.py

The 404/403 problem mapping for TeamNotFound and RosterHidden now lives in
management/teams/exceptions.py, next to the other HTTP-boundary adapters,
instead of as a private helper inside the route module
This commit is contained in:
ryan-crabbe-berri 2026-10-05 18:09:06 -07:00
parent 168540d187
commit 1513048a7f
3 changed files with 54 additions and 25 deletions

View file

@ -10,6 +10,7 @@ from litellm.proxy.list_api.common import PROBLEM_TYPE_BASE, ManagementProblem
from litellm.proxy.list_api.list_framework import handle_list
from litellm.proxy.management.teams.authz import TeamAccess
from litellm.proxy.management.teams.dependencies import get_roster_db, get_team_access, get_teams
from litellm.proxy.management.teams.exceptions import roster_problem
from litellm.proxy.management.teams.repository import RawQuery, TeamMemberRows
from litellm.proxy.management.teams.schemas import TeamMemberListItem
from litellm.proxy.management.teams.service import (
@ -26,30 +27,6 @@ from litellm.types.proxy.management_endpoints.management_v1 import ListResponse,
router: Final = APIRouter(prefix=MANAGEMENT_V1_PREFIX)
def _roster_problem(refusal: TeamNotFound | RosterHidden) -> ManagementProblem:
match refusal:
case TeamNotFound(team_id=team_id):
return ManagementProblem(
ProblemDetail(
type=f"{PROBLEM_TYPE_BASE}team-not-found",
title="Team not found",
status=404,
detail=f"Team id={team_id} does not exist in db",
)
)
case RosterHidden(team_id=team_id):
return ManagementProblem(
ProblemDetail(
type=f"{PROBLEM_TYPE_BASE}forbidden",
title="Forbidden",
status=403,
detail=f"Not allowed to read the members of team={team_id}",
)
)
case _:
assert_never(refusal)
@router.get(
"/teams/{team_id}/members",
tags=["team management"],
@ -97,7 +74,7 @@ async def list_team_members(
caller=user_api_key_dict,
)
case TeamNotFound() | RosterHidden():
raise _roster_problem(lookup)
raise roster_problem(lookup)
case _:
assert_never(lookup)
except ManagementProblem:

View file

@ -0,0 +1,29 @@
from typing_extensions import assert_never
from litellm.proxy.list_api.common import PROBLEM_TYPE_BASE, ManagementProblem
from litellm.proxy.management.teams.service import RosterHidden, TeamNotFound
from litellm.types.proxy.management_endpoints.management_v1 import ProblemDetail
def roster_problem(refusal: TeamNotFound | RosterHidden) -> ManagementProblem:
match refusal:
case TeamNotFound(team_id=team_id):
return ManagementProblem(
ProblemDetail(
type=f"{PROBLEM_TYPE_BASE}team-not-found",
title="Team not found",
status=404,
detail=f"Team id={team_id} does not exist in db",
)
)
case RosterHidden(team_id=team_id):
return ManagementProblem(
ProblemDetail(
type=f"{PROBLEM_TYPE_BASE}forbidden",
title="Forbidden",
status=403,
detail=f"Not allowed to read the members of team={team_id}",
)
)
case _:
assert_never(refusal)

View file

@ -0,0 +1,23 @@
from __future__ import annotations
import pytest
from litellm.proxy.list_api.common import PROBLEM_TYPE_BASE
from litellm.proxy.management.teams.exceptions import roster_problem
from litellm.proxy.management.teams.service import RosterHidden, TeamNotFound
@pytest.mark.parametrize(
("refusal", "status", "problem_type"),
[
pytest.param(TeamNotFound(team_id="team-404"), 404, f"{PROBLEM_TYPE_BASE}team-not-found", id="unknown-team"),
pytest.param(RosterHidden(team_id="team-403"), 403, f"{PROBLEM_TYPE_BASE}forbidden", id="hidden-roster"),
],
)
def test_roster_problem_answers_each_refusal_with_its_own_status_and_names_the_team(
refusal: TeamNotFound | RosterHidden, status: int, problem_type: str
) -> None:
problem = roster_problem(refusal).problem
assert (problem.status, problem.type) == (status, problem_type)
assert refusal.team_id in problem.detail