From a753694a07bc6489859c78f265187fa8d834f45d Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Tue, 6 Oct 2026 13:54:06 -0700 Subject: [PATCH] refactor(proxy): name the team members schemas TeamMemberResponse and TeamMembersQueryParams --- .../proxy/management/teams/dependencies.py | 4 +- litellm/proxy/management/teams/endpoints.py | 6 +- litellm/proxy/management/teams/schemas.py | 4 +- litellm/proxy/management/teams/service.py | 12 ++-- .../proxy/management/teams/test_service.py | 6 +- ui/litellm-dashboard/src/lib/http/schema.d.ts | 64 +++++++++---------- 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/litellm/proxy/management/teams/dependencies.py b/litellm/proxy/management/teams/dependencies.py index 966d109e9df..4b0040803ae 100644 --- a/litellm/proxy/management/teams/dependencies.py +++ b/litellm/proxy/management/teams/dependencies.py @@ -10,7 +10,7 @@ from litellm.proxy.list_api.common import PROBLEM_TYPE_BASE, ManagementProblem from litellm.proxy.list_api.list_framework import QueryPlan, plan_list_query, reject_repeated_params from litellm.proxy.management.teams.authz import TeamAccess from litellm.proxy.management.teams.exceptions import members_not_readable, team_not_found -from litellm.proxy.management.teams.schemas import TeamMembersQuery +from litellm.proxy.management.teams.schemas import TeamMembersQueryParams from litellm.proxy.management.teams.service import TEAM_MEMBERS_LIST_SPEC, team_members_list_query from litellm.proxy.management.users.service import PrismaOrgRoles from litellm.repositories.team_repository import TeamRepository @@ -69,7 +69,7 @@ async def get_readable_team( def get_team_members_plan( - query: Annotated[TeamMembersQuery, Query()], + query: Annotated[TeamMembersQueryParams, Query()], request: Request, caller: Annotated[UserAPIKeyAuth, Depends(user_api_key_auth)], ) -> QueryPlan: diff --git a/litellm/proxy/management/teams/endpoints.py b/litellm/proxy/management/teams/endpoints.py index 6e440a3a025..4d292f40bdc 100644 --- a/litellm/proxy/management/teams/endpoints.py +++ b/litellm/proxy/management/teams/endpoints.py @@ -7,7 +7,7 @@ from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.list_api.list_framework import QueryPlan, list_response from litellm.proxy.management.teams.dependencies import get_readable_team, get_roster_db, get_team_members_plan from litellm.proxy.management.teams.repository import RawQuery -from litellm.proxy.management.teams.schemas import TeamMemberListItem +from litellm.proxy.management.teams.schemas import TeamMemberResponse from litellm.proxy.management.teams.service import get_team_members_list from litellm.proxy.management_endpoints.management_v1.common import MANAGEMENT_V1_PREFIX from litellm.types.proxy.management_endpoints.management_v1 import ListResponse @@ -19,14 +19,14 @@ router: Final = APIRouter(prefix=MANAGEMENT_V1_PREFIX) "/teams/{team_id}/members", tags=["team management"], dependencies=(Depends(user_api_key_auth),), - response_model=ListResponse[TeamMemberListItem], + response_model=ListResponse[TeamMemberResponse], ) async def list_team_members( request: Request, team: Annotated[LiteLLM_TeamTable, Depends(get_readable_team)], plan: Annotated[QueryPlan, Depends(get_team_members_plan)], roster_db: Annotated[RawQuery, Depends(get_roster_db)], -) -> ListResponse[TeamMemberListItem]: +) -> ListResponse[TeamMemberResponse]: """ List a team's members one page at a time, with each member's spend and budget limits in the team. diff --git a/litellm/proxy/management/teams/schemas.py b/litellm/proxy/management/teams/schemas.py index 84161e7eb03..add748dedba 100644 --- a/litellm/proxy/management/teams/schemas.py +++ b/litellm/proxy/management/teams/schemas.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, ConfigDict, Field from litellm.proxy._types import TeamMemberBudgetSource -class TeamMemberListItem(BaseModel): +class TeamMemberResponse(BaseModel): """One roster entry with the member's spend in the team and the budget row their membership points at. The limits are that row's, exactly as `/team/info` returns it under `team_memberships`, so a member @@ -31,7 +31,7 @@ class TeamMemberListItem(BaseModel): allowed_models: tuple[str, ...] -class TeamMembersQuery(BaseModel): +class TeamMembersQueryParams(BaseModel): model_config = ConfigDict(frozen=True, extra="forbid") q: str | None = Field( diff --git a/litellm/proxy/management/teams/service.py b/litellm/proxy/management/teams/service.py index 91260e516e3..8214c86760d 100644 --- a/litellm/proxy/management/teams/service.py +++ b/litellm/proxy/management/teams/service.py @@ -7,7 +7,7 @@ from typing import Final from litellm.proxy._types import TeamMemberBudgetSource, UserAPIKeyAuth from litellm.proxy.list_api.list_framework import Compare, ListQuery, ListSpec, QueryPlan, Scope, ScopeAll, Within from litellm.proxy.management.teams.repository import RawQuery, TeamMemberRow, TeamMemberRows -from litellm.proxy.management.teams.schemas import TeamMemberListItem, TeamMembersQuery +from litellm.proxy.management.teams.schemas import TeamMemberResponse, TeamMembersQueryParams def member_budget_source(budget_id: str | None, team_default_budget_id: str | None) -> TeamMemberBudgetSource: @@ -16,8 +16,8 @@ def member_budget_source(budget_id: str | None, team_default_budget_id: str | No return "team_default" if team_default_budget_id is not None else "none" -def _team_member_item(row: TeamMemberRow) -> TeamMemberListItem: - return TeamMemberListItem( +def _team_member_item(row: TeamMemberRow) -> TeamMemberResponse: + return TeamMemberResponse( user_id=row.user_id, user_email=row.user_email, user_alias=row.user_alias, @@ -40,7 +40,7 @@ def _roster_reader_scope(_caller: UserAPIKeyAuth) -> Scope: return ScopeAll() -TEAM_MEMBERS_LIST_SPEC: Final[ListSpec[TeamMemberRow, TeamMemberListItem]] = ListSpec( +TEAM_MEMBERS_LIST_SPEC: Final[ListSpec[TeamMemberRow, TeamMemberResponse]] = ListSpec( resource="team members", sortable=frozenset( ("user_alias", "user_email", "user_id", "role", "spend", "total_spend", "max_budget_in_team", "budget_reset_at") @@ -56,7 +56,7 @@ TEAM_MEMBERS_LIST_SPEC: Final[ListSpec[TeamMemberRow, TeamMemberListItem]] = Lis ) -def team_members_list_query(query: TeamMembersQuery) -> ListQuery: +def team_members_list_query(query: TeamMembersQueryParams) -> ListQuery: return ListQuery( page=query.page, page_size=query.page_size, @@ -75,7 +75,7 @@ def team_members_list_query(query: TeamMembersQuery) -> ListQuery: @dataclass(frozen=True, slots=True) class TeamMembersPage: - members: tuple[TeamMemberListItem, ...] + members: tuple[TeamMemberResponse, ...] total_count: int diff --git a/tests/unit/proxy/management/teams/test_service.py b/tests/unit/proxy/management/teams/test_service.py index a3760cc4678..510990362bf 100644 --- a/tests/unit/proxy/management/teams/test_service.py +++ b/tests/unit/proxy/management/teams/test_service.py @@ -10,7 +10,7 @@ import pytest from litellm.proxy._types import TeamMemberBudgetSource from litellm.proxy.list_api.list_framework import Compare, ListQuery, QueryPlan, SortKey, Within from litellm.proxy.management.teams.repository import TeamMemberRow -from litellm.proxy.management.teams.schemas import TeamMembersQuery +from litellm.proxy.management.teams.schemas import TeamMembersQueryParams from litellm.proxy.management.teams.service import ( TEAM_MEMBERS_LIST_SPEC, TeamMembersPage, @@ -96,7 +96,7 @@ def test_list_item_carries_the_member_row_and_derives_its_budget_source() -> Non def test_team_members_list_query_carries_every_declared_param_with_roles_as_roster_filters() -> None: - declared: Final = TeamMembersQuery.model_validate( + declared: Final = TeamMembersQueryParams.model_validate( { "q": "ada", "filter[role]": "admin", @@ -117,4 +117,4 @@ def test_team_members_list_query_carries_every_declared_param_with_roles_as_rost def test_team_members_list_query_with_no_params_filters_nothing() -> None: - assert team_members_list_query(TeamMembersQuery()) == ListQuery() + assert team_members_list_query(TeamMembersQueryParams()) == ListQuery() diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index f0d6a1dca9d..98fafdd9e67 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -33899,10 +33899,10 @@ export interface components { links: components["schemas"]["ListLinks"]; meta: components["schemas"]["ListMeta"]; }; - /** ListResponse[TeamMemberListItem] */ - ListResponse_TeamMemberListItem_: { + /** ListResponse[TeamMemberResponse] */ + ListResponse_TeamMemberResponse_: { /** Data */ - data: components["schemas"]["TeamMemberListItem"][]; + data: components["schemas"]["TeamMemberResponse"][]; links: components["schemas"]["ListLinks"]; meta: components["schemas"]["ListMeta"]; }; @@ -45926,13 +45926,39 @@ export interface components { user_id: string; }; /** - * TeamMemberListItem + * TeamMemberRef + * @description One member, named by exactly one of `user_id` or `user_email`. + */ + TeamMemberRef: { + /** User Email */ + user_email?: string | null; + /** User Id */ + user_id?: string | null; + }; + /** TeamMemberResetBudgetResponse */ + TeamMemberResetBudgetResponse: { + /** Budget Id */ + budget_id: string | null; + /** + * Budget Source + * @enum {string} + */ + budget_source: "team_default" | "custom" | "none"; + /** Previous Budget Id */ + previous_budget_id: string | null; + /** Team Id */ + team_id: string; + /** User Id */ + user_id: string; + }; + /** + * TeamMemberResponse * @description One roster entry with the member's spend in the team and the budget row their membership points at. * * The limits are that row's, exactly as `/team/info` returns it under `team_memberships`, so a member * with no row of their own reads null limits even when `budget_source` is `team_default`. */ - TeamMemberListItem: { + TeamMemberResponse: { /** Allowed Models */ allowed_models: string[]; /** Budget Duration */ @@ -45968,32 +45994,6 @@ export interface components { /** User Id */ user_id: string | null; }; - /** - * TeamMemberRef - * @description One member, named by exactly one of `user_id` or `user_email`. - */ - TeamMemberRef: { - /** User Email */ - user_email?: string | null; - /** User Id */ - user_id?: string | null; - }; - /** TeamMemberResetBudgetResponse */ - TeamMemberResetBudgetResponse: { - /** Budget Id */ - budget_id: string | null; - /** - * Budget Source - * @enum {string} - */ - budget_source: "team_default" | "custom" | "none"; - /** Previous Budget Id */ - previous_budget_id: string | null; - /** Team Id */ - team_id: string; - /** User Id */ - user_id: string; - }; /** TeamMemberUpdateRequest */ TeamMemberUpdateRequest: { /** @@ -62990,7 +62990,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["ListResponse_TeamMemberListItem_"]; + "application/json": components["schemas"]["ListResponse_TeamMemberResponse_"]; }; }; /** @description Validation Error */