From 99f2528bd8b7472b8e27ad5afbf4b2e2f3154aee Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 12 Jun 2026 13:36:34 +0530 Subject: [PATCH] fix(proxy): always return direct_access on /v1/model/info Set direct_access to true or false on every enriched model so clients can filter without treating a missing field as ambiguous. Co-authored-by: Cursor --- litellm/proxy/proxy_server.py | 5 +-- .../test_team_model_name_translation.py | 36 ++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b3a7b03b09..8faae82116 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -11091,10 +11091,11 @@ async def _populate_team_access_on_models( model_id, [] ) + direct_access_model_ids = set(direct_access_models) for _model in all_models: model_id = _model.get("model_info", {}).get("id", None) - if model_id is not None and model_id in direct_access_models: - _model["model_info"]["direct_access"] = True + if model_id is not None: + _model["model_info"]["direct_access"] = model_id in direct_access_model_ids return all_models diff --git a/tests/test_litellm/proxy/proxy_server/test_team_model_name_translation.py b/tests/test_litellm/proxy/proxy_server/test_team_model_name_translation.py index 9014736eca..58f1dffda4 100644 --- a/tests/test_litellm/proxy/proxy_server/test_team_model_name_translation.py +++ b/tests/test_litellm/proxy/proxy_server/test_team_model_name_translation.py @@ -374,6 +374,7 @@ async def test_model_info_v1_populates_access_via_team_ids(monkeypatch): model_id = model["model_info"]["id"] if model_id == "byok-id-1": model["model_info"]["access_via_team_ids"] = [team_id] + model["model_info"]["direct_access"] = False elif model_id == "global-id-1": model["model_info"]["direct_access"] = True return kwargs["all_models"] @@ -394,4 +395,37 @@ async def test_model_info_v1_populates_access_via_team_ids(monkeypatch): by_id = {m["model_info"]["id"]: m for m in resp["data"]} assert by_id["byok-id-1"]["model_info"]["access_via_team_ids"] == [team_id] - assert by_id["global-id-1"]["model_info"].get("direct_access") is True + assert by_id["byok-id-1"]["model_info"]["direct_access"] is False + assert by_id["global-id-1"]["model_info"]["direct_access"] is True + + +@pytest.mark.asyncio +async def test_populate_team_access_sets_direct_access_false_by_default(monkeypatch): + """Team-accessible models without direct access must return direct_access=false.""" + team_row = _team_row() + global_row = { + "model_name": "gpt-4o", + "litellm_params": {"model": "gpt-4o"}, + "model_info": {"id": "global-id-1", "db_model": False}, + } + router = MagicMock() + router.get_model_ids.return_value = ["global-id-1"] + monkeypatch.setattr( + ps, + "get_all_team_models", + AsyncMock(return_value={"byok-id-1": ["team-abc-123"]}), + ) + + admin = UserAPIKeyAuth( + user_id="u", user_role=LitellmUserRoles.PROXY_ADMIN, team_models=[] + ) + result = await ps._populate_team_access_on_models( + user_api_key_dict=admin, + prisma_client=MagicMock(), + llm_router=router, + all_models=[team_row, global_row], + ) + + by_id = {m["model_info"]["id"]: m for m in result} + assert by_id["byok-id-1"]["model_info"]["direct_access"] is False + assert by_id["global-id-1"]["model_info"]["direct_access"] is True