fix(vllm): list hosted_vllm/* models without an api key

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
mateo 2026-10-23 23:38:14 +00:00
parent bffc166cd6
commit 1d72a45be4
2 changed files with 62 additions and 5 deletions

View file

@ -1,3 +1,4 @@
from types import MappingProxyType
from typing import Final
import httpx
@ -45,16 +46,17 @@ class VLLMModelInfo(BaseLLMModelInfo):
@staticmethod
def get_api_base(api_base: str | None = None) -> str | None:
api_base = api_base or get_secret_str("VLLM_API_BASE")
api_base = api_base or get_secret_str("HOSTED_VLLM_API_BASE") or get_secret_str("VLLM_API_BASE")
if api_base is None:
raise ValueError(
"VLLM_API_BASE is not set. Please set the environment variable, to use VLLM's pass-through - `{LITELLM_API_BASE}/vllm/{endpoint}`."
"HOSTED_VLLM_API_BASE or VLLM_API_BASE is not set. Please set an environment variable to use "
"VLLM's pass-through - `{LITELLM_API_BASE}/vllm/{endpoint}`."
)
return api_base
@staticmethod
def get_api_key(api_key: str | None = None) -> str | None:
return None
return api_key or get_secret_str("HOSTED_VLLM_API_KEY")
@staticmethod
def get_base_model(model: str) -> str | None:
@ -64,14 +66,17 @@ class VLLMModelInfo(BaseLLMModelInfo):
api_base = VLLMModelInfo.get_api_base(api_base)
api_key = VLLMModelInfo.get_api_key(api_key)
endpoint: Final = "/v1/models"
if api_base is None or api_key is None:
if api_base is None:
raise ValueError(
"VLLM_API_BASE or VLLM_API_KEY is not set. Please set the environment variable, to query VLLM's `/models` endpoint."
"HOSTED_VLLM_API_BASE or VLLM_API_BASE is not set. Please set an environment variable to query "
"VLLM's `/models` endpoint."
)
url: Final = add_path_to_api_base(api_base, endpoint)
headers: Final = MappingProxyType({"Authorization": f"Bearer {api_key}"}) if api_key is not None else None
response: Final = litellm.module_level_client.get(
url=url,
headers=headers,
)
response.raise_for_status()

View file

@ -0,0 +1,52 @@
import httpx
import pytest
import respx
from litellm.llms.vllm.common_utils import VLLMModelInfo
MODELS_URL = "http://vllm.example/v1/models"
MODEL_RESPONSE = {"data": [{"id": "Qwen/Qwen3-8B"}]}
def test_get_models_without_api_key_sends_no_authorization_header(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("HOSTED_VLLM_API_KEY", raising=False)
with respx.mock:
route = respx.get(MODELS_URL).mock(return_value=httpx.Response(200, json=MODEL_RESPONSE))
models = VLLMModelInfo().get_models(api_base="http://vllm.example/v1")
assert models == ["Qwen/Qwen3-8B"]
assert "authorization" not in route.calls[0].request.headers
def test_get_models_sends_api_key_as_bearer_token() -> None:
with respx.mock:
route = respx.get(MODELS_URL).mock(return_value=httpx.Response(200, json=MODEL_RESPONSE))
models = VLLMModelInfo().get_models(
api_key="vllm-test-key",
api_base="http://vllm.example/v1",
)
assert models == ["Qwen/Qwen3-8B"]
assert route.calls[0].request.headers["authorization"] == "Bearer vllm-test-key"
def test_get_models_requires_api_base(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("HOSTED_VLLM_API_BASE", raising=False)
monkeypatch.delenv("VLLM_API_BASE", raising=False)
with pytest.raises(ValueError, match="HOSTED_VLLM_API_BASE"):
VLLMModelInfo().get_models(api_key="vllm-test-key")
def test_get_api_key_falls_back_to_hosted_vllm_environment_variable(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("HOSTED_VLLM_API_KEY", "hosted-vllm-test-key")
assert VLLMModelInfo.get_api_key() == "hosted-vllm-test-key"
def test_get_api_base_prefers_hosted_vllm_environment_variable(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("HOSTED_VLLM_API_BASE", "http://hosted-vllm.example/v1")
monkeypatch.setenv("VLLM_API_BASE", "http://vllm.example/v1")
assert VLLMModelInfo.get_api_base() == "http://hosted-vllm.example/v1"