From 301459adf7195132defd5a1308ca1abc2ea81746 Mon Sep 17 00:00:00 2001 From: Joaquin Hui Gomez Date: Mon, 15 Jun 2026 23:24:06 +0100 Subject: [PATCH] fix(proxy): apply default_team_params.models on team creation new_team applied default_team_params only for fields whose request default is None (max_budget, tpm_limit, etc.). The models field defaults to an empty list, so it was never covered by the None-guard loop and default_team_params.models was silently dropped for teams created via API/SCIM/UI (it only worked on the SSO path). Add a dedicated check that applies the configured default when the request specifies no models. Fixes #30478 --- .../management_endpoints/team_endpoints.py | 8 +++ .../test_team_default_params.py | 54 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/litellm/proxy/management_endpoints/team_endpoints.py b/litellm/proxy/management_endpoints/team_endpoints.py index ae7da0d29f2..469a017affc 100644 --- a/litellm/proxy/management_endpoints/team_endpoints.py +++ b/litellm/proxy/management_endpoints/team_endpoints.py @@ -1043,6 +1043,14 @@ async def new_team( # noqa: PLR0915 if default_value is not None: setattr(data, field, default_value) + # `models` defaults to an empty list rather than None on the request, so it + # needs its own check: apply the configured default when the request did not + # specify any models (matches SSO-provisioned team behavior). + if not getattr(data, "models", None): + default_models = _get_default_team_param("models") + if default_models is not None: + data.models = default_models + # Legacy fallback: apply max_budget from default_team_settings (YAML config) # if still not set after checking default_team_params. if data.max_budget is None: diff --git a/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py b/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py index 443089b5f01..33f5f15091c 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py +++ b/tests/test_litellm/proxy/management_endpoints/test_team_default_params.py @@ -288,6 +288,60 @@ class TestNewTeamDefaultParamsApplied: assert data.tpm_limit == 200 # default applied assert data.rpm_limit == 500 # default applied + @pytest.mark.asyncio + async def test_models_default_applied_when_not_provided(self, monkeypatch): + """default_team_params.models applies to teams created without models (#30478). + + `models` defaults to an empty list rather than None, so it is not covered + by the None-guard loop and needs its own handling. + """ + from litellm.proxy.management_endpoints.team_endpoints import new_team + + monkeypatch.setattr( + litellm, + "default_team_params", + {"models": ["gpt-4", "gpt-3.5-turbo"]}, + ) + + data = NewTeamRequest(team_alias="my-team") # models defaults to [] + auth = self._make_admin_auth() + + try: + await new_team( + data=data, + user_api_key_dict=auth, + http_request=MagicMock(), + ) + except Exception: + pass + + assert data.models == ["gpt-4", "gpt-3.5-turbo"] + + @pytest.mark.asyncio + async def test_models_explicit_not_overridden(self, monkeypatch): + """Explicit models on the request are not overridden by the default.""" + from litellm.proxy.management_endpoints.team_endpoints import new_team + + monkeypatch.setattr( + litellm, + "default_team_params", + {"models": ["gpt-4"]}, + ) + + data = NewTeamRequest(team_alias="my-team", models=["claude-3-5-sonnet"]) + auth = self._make_admin_auth() + + try: + await new_team( + data=data, + user_api_key_dict=auth, + http_request=MagicMock(), + ) + except Exception: + pass + + assert data.models == ["claude-3-5-sonnet"] + @pytest.mark.asyncio async def test_no_defaults_when_config_is_none(self, monkeypatch): """When default_team_params is None, no defaults applied."""