From b253bb92f76c0bd343e784fff97d56a4c0db1e21 Mon Sep 17 00:00:00 2001 From: Vladimir Yatulchik Date: Tue, 18 Aug 2026 14:33:45 +0400 Subject: [PATCH] fix(proxy): accept slashes in the model id on GET /v1/models/{model_id} A model group named provider/model is listed by GET /v1/models under that exact id, but retrieving it came back 404 from the routing layer because the default path convertor matches a single segment and the ASGI path is decoded before routing, so a percent encoded slash cannot escape the separator either. Use the path convertor, as /v1/fine_tuning/jobs/{fine_tuning_job_id:path} already does, so retrieve accepts every id the listing hands out. --- litellm/proxy/proxy_server.py | 4 +-- .../proxy/proxy_server/test_routes_models.py | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 5d4a306a73e..af44cd99434 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -9694,12 +9694,12 @@ async def model_list( @router.get( - "/v1/models/{model_id}", + "/v1/models/{model_id:path}", dependencies=[Depends(user_api_key_auth)], tags=["model management"], ) @router.get( - "/models/{model_id}", + "/models/{model_id:path}", dependencies=[Depends(user_api_key_auth)], tags=["model management"], ) diff --git a/tests/test_litellm/proxy/proxy_server/test_routes_models.py b/tests/test_litellm/proxy/proxy_server/test_routes_models.py index 2b126b1ea95..62d2938012e 100644 --- a/tests/test_litellm/proxy/proxy_server/test_routes_models.py +++ b/tests/test_litellm/proxy/proxy_server/test_routes_models.py @@ -265,3 +265,32 @@ def test_anthropic_format_returns_public_team_model_name( assert response.status_code == 200 assert [m["id"] for m in response.json()["data"]] == ["gpt-4-team"] assert internal_name not in response.text + + +@pytest.mark.parametrize("prefix", ["/v1/models", "/models"]) +def test_get_model_by_id_accepts_slash_in_model_name( + client, auth_as, patched_models, monkeypatch, prefix +): + """Regression: a model group named ``provider/model`` is listed under that id, + so retrieve has to match the whole id rather than its first path segment.""" + from litellm.proxy import utils as proxy_utils + + model_id = "mistralai/mistral-7b-instruct" + + async def _fake_get_available_models_for_user(**kwargs): + return [model_id] + + monkeypatch.setattr( + proxy_utils, + "get_available_models_for_user", + _fake_get_available_models_for_user, + ) + patched_models.get_model_names = MagicMock(return_value=[model_id]) + + with auth_as(): + listed = client.get(prefix) + retrieved = client.get(f"{prefix}/{model_id}") + + assert [entry["id"] for entry in listed.json()["data"]] == [model_id] + assert retrieved.status_code == 200 + assert retrieved.json()["id"] == model_id