Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f626587
feat(teams): resolve access group models/MCPs/agents in team endpoints
ryan-crabbe-berri Apr 2, 2026
bbe708b
perf(teams): single-pass access group resolution + asyncio.gather in …
ryan-crabbe-berri Apr 2, 2026
59b0910
docs: add default_team_params to config reference and update examples
ryan-crabbe-berri Apr 2, 2026
c19a63e
docs: clarify that models sub-field only applies to SSO auto-created …
ryan-crabbe-berri Apr 2, 2026
f0bd334
fix: lazy import get_access_object to break cyclic import + short-cir…
ryan-crabbe-berri Apr 2, 2026
5a8f910
add: making organizations a select instead of read only badges
ryan-crabbe-berri Apr 3, 2026
1bb726a
fix(ui): only send organization_id when changed and use raw initial v…
ryan-crabbe-berri Apr 3, 2026
cd7b262
fix(ui): add paginated team search to usage page filter
ryan-crabbe-berri Apr 3, 2026
1533f68
fix(ui): fix imports and update placeholder for team multi select
ryan-crabbe-berri Apr 3, 2026
96b660b
fix(ui): wire team_id filter to key alias dropdown on Virtual Keys tab
ryan-crabbe-berri Apr 3, 2026
1521004
Merge pull request #25114 from BerriAI/litellm_fix-virtual-keys-team-…
ryan-crabbe-berri Apr 3, 2026
0575c3f
Merge pull request #25107 from BerriAI/litellm_fix-team-id-search-filter
ryan-crabbe-berri Apr 3, 2026
20077fe
Merge pull request #25095 from BerriAI/litellm_fix-ui-update-org-teams
ryan-crabbe-berri Apr 3, 2026
eb780a8
Merge pull request #25032 from BerriAI/litellm_docs-default-team-params
ryan-crabbe-berri Apr 3, 2026
38f6c94
fix(tests): correct mock targets in TestResolveAccessGroupResources
ryan-crabbe-berri Apr 3, 2026
ea32cb5
fix: use direct attribute access with or [] fallback in _resolve_acce…
ryan-crabbe-berri Apr 3, 2026
3bdd042
fix(ui): remove model source legend from team detail view
ryan-crabbe-berri Apr 3, 2026
bb03a11
fix(ui): add missing access_group fields to TeamData.team_info type
ryan-crabbe-berri Apr 3, 2026
93369bf
perf(teams): batch-fetch access groups in single DB query
ryan-crabbe-berri Apr 4, 2026
0331fb5
Merge pull request #25027 from BerriAI/litellm_add-access-group-to-model
ryan-crabbe-berri Apr 4, 2026
ce219fc
refactor(proxy): extract helpers to fix PLR0915 violations
ryan-crabbe-berri Apr 4, 2026
866c4a2
test(ui): update tests to match new team_id / access-group signatures
ryan-crabbe-berri Apr 4, 2026
c495acd
fix(ui): send null (not '') for cleared organization_id on team update
ryan-crabbe-berri Apr 4, 2026
ad203dc
poetry
ryan-crabbe-berri Apr 4, 2026
ad81892
Merge remote-tracking branch 'origin/main' into litellm_ryan-march-31
ryan-crabbe-berri Apr 4, 2026
76c0591
chore: regen poetry.lock for litellm-proxy-extras 0.4.64 bump
ryan-crabbe-berri Apr 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/my-website/docs/proxy/config_settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ router_settings:
| enable_json_schema_validation | boolean | If true, enables json schema validation for all requests. |
| enable_key_alias_format_validation | boolean | If true, validates `key_alias` format on `/key/generate` and `/key/update`. Must be 2-255 chars, start/end with alphanumeric, only allow `a-zA-Z0-9_-/.@`. Default `false`. |
| disable_copilot_system_to_assistant | boolean | **DEPRECATED** - GitHub Copilot API supports system prompts. |
| default_team_params | object | Default parameters applied to every new team created via `/team/new` (including SSO auto-created teams). Only fills in fields not explicitly set in the request. Sub-fields: `max_budget` (float), `budget_duration` (string, e.g. `"30d"`), `tpm_limit` (integer), `rpm_limit` (integer), `team_member_permissions` (array of strings, e.g. `["/team/daily/activity", "/key/generate"]`), `models` (array of strings — only applied to SSO auto-created teams). |

### general_settings - Reference

Expand Down
25 changes: 17 additions & 8 deletions docs/my-website/docs/proxy/self_serve.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,15 @@ When you connect litellm to your SSO provider, litellm can auto-create teams. Us

```yaml showLineNumbers title="Default Params for new teams"
litellm_settings:
default_team_params: # Default Params to apply when litellm auto creates a team from SSO IDP provider
max_budget: 100 # Optional[float], optional): $100 budget for the team
budget_duration: 30d # Optional[str], optional): 30 days budget_duration for the team
models: ["gpt-3.5-turbo"] # Optional[List[str]], optional): models to be used by the team
default_team_params: # Applied to all /team/new calls (including SSO auto-created teams) when the field is not explicitly set
max_budget: 100 # Optional[float]: $100 budget for the team
budget_duration: 30d # Optional[str]: 30 days budget_duration for the team
models: ["gpt-3.5-turbo"] # Optional[List[str]]: models for the team (only applied to SSO auto-created teams)
tpm_limit: 100000 # Optional[int]: tokens per minute limit
rpm_limit: 1000 # Optional[int]: requests per minute limit
team_member_permissions: # Optional[List[str]]: permissions granted to non-admin team members
- "/team/daily/activity" # Allow members to view team usage
- "/key/generate" # Allow members to generate API keys
```


Expand Down Expand Up @@ -390,10 +395,14 @@ litellm_settings:
max_budget_in_team: 100 # Optional[float], optional): $100 budget for the team. Defaults to None.
user_role: "user" # Optional[str], optional): "user" or "admin". Defaults to "user"

default_team_params: # Default Params to apply when litellm auto creates a team from SSO IDP provider
max_budget: 100 # Optional[float], optional): $100 budget for the team
budget_duration: 30d # Optional[str], optional): 30 days budget_duration for the team
models: ["gpt-3.5-turbo"] # Optional[List[str]], optional): models to be used by the team
default_team_params: # Applied to all /team/new calls (including SSO auto-created teams) when the field is not explicitly set
max_budget: 100 # Optional[float]: $100 budget for the team
budget_duration: 30d # Optional[str]: 30 days budget_duration for the team
models: ["gpt-3.5-turbo"] # Optional[List[str]]: models for the team (only applied to SSO auto-created teams)
tpm_limit: 100000 # Optional[int]: tokens per minute limit
rpm_limit: 1000 # Optional[int]: requests per minute limit
team_member_permissions: # Optional[List[str]]: permissions granted to non-admin team members
- "/team/daily/activity"


upperbound_key_generate_params: # Upperbound for /key/generate requests when self-serve flow is on
Expand Down
10 changes: 6 additions & 4 deletions docs/my-website/docs/tutorials/msft_sso.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,12 @@ Navigate to your litellm config file and set the following params

```yaml showLineNumbers title="litellm config with default_team_params"
litellm_settings:
default_team_params: # Default Params to apply when litellm auto creates a team from SSO IDP provider
max_budget: 100 # Optional[float], optional): $100 budget for the team
budget_duration: 30d # Optional[str], optional): 30 days budget_duration for the team
models: ["gpt-3.5-turbo"] # Optional[List[str]], optional): models to be used by the team
default_team_params: # Applied to all /team/new calls (including SSO auto-created teams) when the field is not explicitly set
max_budget: 100 # Optional[float]: $100 budget for the team
budget_duration: 30d # Optional[str]: 30 days budget_duration for the team
models: ["gpt-3.5-turbo"] # Optional[List[str]]: models for the team (only applied to SSO auto-created teams)
team_member_permissions: # Optional[List[str]]: permissions granted to non-admin team members
- "/team/daily/activity" # Allow members to view team usage
```

### 3.2 Auto-create a new team on LiteLLM
Expand Down
4 changes: 4 additions & 0 deletions litellm/proxy/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3809,6 +3809,10 @@ class OrganizationMemberUpdateResponse(MemberUpdateResponse):

class TeamInfoResponseObjectTeamTable(LiteLLM_TeamTable):
team_member_budget_table: Optional[LiteLLM_BudgetTable] = None
# Resources inherited from access groups (separate from direct assignments)
access_group_models: Optional[List[str]] = None
access_group_mcp_server_ids: Optional[List[str]] = None
access_group_agent_ids: Optional[List[str]] = None


class TeamInfoResponseObject(TypedDict):
Expand Down
72 changes: 46 additions & 26 deletions litellm/proxy/management_endpoints/key_management_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -4382,6 +4382,42 @@ async def list_keys(
)


async def _apply_non_admin_alias_scope(
user_api_key_dict: UserAPIKeyAuth,
prisma_client: Any,
query_params: List[Any],
where_parts: List[str],
) -> None:
"""Append SQL scope conditions so non-admin users only see aliases for
keys they own or keys belonging to teams they are members of."""
scope_conditions: List[str] = []
if user_api_key_dict.user_id:
query_params.append(user_api_key_dict.user_id)
scope_conditions.append(f"user_id = ${len(query_params)}")

# Look up the user's teams from the user table
user_teams: List[str] = []
if user_api_key_dict.user_id:
user_row = await prisma_client.db.litellm_usertable.find_unique(
where={"user_id": user_api_key_dict.user_id}
)
if user_row is not None:
user_teams = getattr(user_row, "teams", []) or []

if user_teams:
team_placeholders = ", ".join(
f"${len(query_params) + i + 1}" for i in range(len(user_teams))
)
query_params.extend(user_teams)
scope_conditions.append(f"team_id IN ({team_placeholders})")

if scope_conditions:
where_parts.append(f"({' OR '.join(scope_conditions)})")
else:
# No user_id and no teams — return nothing
where_parts.append("FALSE")


@router.get(
"/key/aliases",
tags=["key management"],
Expand All @@ -4395,6 +4431,9 @@ async def key_aliases(
search: Optional[str] = Query(
None, description="Search key aliases (case-insensitive partial match)"
),
team_id: Optional[str] = Query(
None, description="Filter aliases to keys belonging to this team"
),
) -> Dict[str, Any]:
"""
Lists key aliases with pagination and optional search.
Expand Down Expand Up @@ -4439,37 +4478,18 @@ async def key_aliases(
LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value,
]
if not is_proxy_admin:
scope_conditions: List[str] = []
if user_api_key_dict.user_id:
query_params.append(user_api_key_dict.user_id)
scope_conditions.append(f"user_id = ${len(query_params)}")

# Look up the user's teams from the user table
user_teams: List[str] = []
if user_api_key_dict.user_id:
user_row = await prisma_client.db.litellm_usertable.find_unique(
where={"user_id": user_api_key_dict.user_id}
)
if user_row is not None:
user_teams = getattr(user_row, "teams", []) or []

if user_teams:
team_placeholders = ", ".join(
f"${len(query_params) + i + 1}" for i in range(len(user_teams))
)
query_params.extend(user_teams)
scope_conditions.append(f"team_id IN ({team_placeholders})")

if scope_conditions:
where_parts.append(f"({' OR '.join(scope_conditions)})")
else:
# No user_id and no teams — return nothing
where_parts.append("FALSE")
await _apply_non_admin_alias_scope(
user_api_key_dict, prisma_client, query_params, where_parts
)

if search:
query_params.append(f"%{search}%")
where_parts.append(f"key_alias ILIKE ${len(query_params)}")

if team_id:
query_params.append(team_id)
where_parts.append(f"team_id = ${len(query_params)}")
Comment on lines +4489 to +4491

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Raw SQL for new team_id filter

The team_id filter is added using the existing raw-SQL pattern in this function. The comment at the top of the function explains why raw SQL is used here (Prisma doesn't support column-level SELECT projection on find_many), and the value is correctly parameterized, so there is no injection risk. However, CLAUDE.md explicitly states: "Do not write raw SQL for proxy DB operations. Use Prisma model methods instead of execute_raw/query_raw." The existing code already violates this rule, but extending it amplifies the concern. If a Prisma-level approach is ever adopted (e.g. returning only the key_alias column), the team_id filter should migrate with it.


where_sql = " AND ".join(where_parts)

count_sql = f'SELECT COUNT(*) AS count FROM "LiteLLM_VerificationToken" WHERE {where_sql}'
Expand Down
Loading
Loading