mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-06 12:22:33 +00:00
fix: return empty data array instead of 500 when no models configured (#18556)
- /v2/model/info now returns {"data": []} when llm_router is None or model_list is empty
- /model_group/info now returns {"data": []} when llm_model_list is None or empty
- Fixes UI crash on fresh installs with STORE_MODEL_IN_DB=True
- Added 4 unit tests for empty model list scenarios
This commit is contained in:
@@ -7322,13 +7322,9 @@ async def model_info_v2(
|
||||
"""
|
||||
global llm_model_list, general_settings, user_config_file_path, proxy_config, llm_router
|
||||
|
||||
if llm_router is None:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail={
|
||||
"error": f"No model list passed, models router={llm_router}. You can add a model through the config.yaml or on the LiteLLM Admin UI."
|
||||
},
|
||||
)
|
||||
# Return empty data array when no models are configured (graceful handling for fresh installs)
|
||||
if llm_router is None or not llm_router.model_list:
|
||||
return {"data": []}
|
||||
|
||||
if prisma_client is None:
|
||||
raise HTTPException(
|
||||
@@ -8228,14 +8224,9 @@ async def model_group_info(
|
||||
"""
|
||||
global llm_model_list, general_settings, user_config_file_path, proxy_config, llm_router
|
||||
|
||||
if llm_model_list is None:
|
||||
raise HTTPException(
|
||||
status_code=500, detail={"error": "LLM Model List not loaded in"}
|
||||
)
|
||||
if llm_router is None:
|
||||
raise HTTPException(
|
||||
status_code=500, detail={"error": "LLM Router is not loaded in"}
|
||||
)
|
||||
# Return empty data array when no models are configured (graceful handling for fresh installs)
|
||||
if llm_model_list is None or llm_router is None or not llm_model_list:
|
||||
return {"data": []}
|
||||
|
||||
from litellm.proxy.utils import get_available_models_for_user
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
"""
|
||||
Tests for graceful handling of empty model list scenarios.
|
||||
|
||||
These tests verify that /v2/model/info and /model_group/info endpoints
|
||||
return empty data arrays instead of 500 errors when no models are configured.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../../..")
|
||||
) # Adds the parent directory to the system-path
|
||||
|
||||
from litellm.proxy.proxy_server import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Create a test client for the FastAPI app."""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
class TestEmptyModelListHandling:
|
||||
"""Test suite for empty model list scenarios."""
|
||||
|
||||
def test_v2_model_info_returns_empty_data_when_router_is_none(
|
||||
self, client, monkeypatch
|
||||
):
|
||||
"""
|
||||
Test that /v2/model/info returns {"data": []} instead of 500
|
||||
when llm_router is None.
|
||||
"""
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_model_list", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.user_model", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {})
|
||||
|
||||
with patch(
|
||||
"litellm.proxy.auth.user_api_key_auth.user_api_key_auth",
|
||||
return_value=MagicMock(
|
||||
user_id="test-user",
|
||||
team_id=None,
|
||||
team_models=[],
|
||||
models=[],
|
||||
user_role="proxy_admin",
|
||||
),
|
||||
):
|
||||
response = client.get(
|
||||
"/v2/model/info",
|
||||
headers={"Authorization": "Bearer sk-test"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"data": []}
|
||||
|
||||
def test_v2_model_info_returns_empty_data_when_model_list_empty(
|
||||
self, client, monkeypatch
|
||||
):
|
||||
"""
|
||||
Test that /v2/model/info returns {"data": []} instead of 500
|
||||
when llm_router exists but model_list is empty.
|
||||
"""
|
||||
mock_router = MagicMock()
|
||||
mock_router.model_list = []
|
||||
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", mock_router)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_model_list", [])
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.user_model", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {})
|
||||
|
||||
with patch(
|
||||
"litellm.proxy.auth.user_api_key_auth.user_api_key_auth",
|
||||
return_value=MagicMock(
|
||||
user_id="test-user",
|
||||
team_id=None,
|
||||
team_models=[],
|
||||
models=[],
|
||||
user_role="proxy_admin",
|
||||
),
|
||||
):
|
||||
response = client.get(
|
||||
"/v2/model/info",
|
||||
headers={"Authorization": "Bearer sk-test"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"data": []}
|
||||
|
||||
def test_model_group_info_returns_empty_data_when_model_list_none(
|
||||
self, client, monkeypatch
|
||||
):
|
||||
"""
|
||||
Test that /model_group/info returns {"data": []} instead of 500
|
||||
when llm_model_list is None.
|
||||
"""
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_model_list", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.user_model", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {})
|
||||
|
||||
with patch(
|
||||
"litellm.proxy.auth.user_api_key_auth.user_api_key_auth",
|
||||
return_value=MagicMock(
|
||||
user_id="test-user",
|
||||
team_id=None,
|
||||
team_models=[],
|
||||
models=[],
|
||||
user_role="proxy_admin",
|
||||
),
|
||||
):
|
||||
response = client.get(
|
||||
"/model_group/info",
|
||||
headers={"Authorization": "Bearer sk-test"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"data": []}
|
||||
|
||||
def test_model_group_info_returns_empty_data_when_model_list_empty(
|
||||
self, client, monkeypatch
|
||||
):
|
||||
"""
|
||||
Test that /model_group/info returns {"data": []} instead of 500
|
||||
when llm_model_list is empty.
|
||||
"""
|
||||
mock_router = MagicMock()
|
||||
mock_router.model_list = []
|
||||
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", mock_router)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.llm_model_list", [])
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.user_model", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", {})
|
||||
|
||||
with patch(
|
||||
"litellm.proxy.auth.user_api_key_auth.user_api_key_auth",
|
||||
return_value=MagicMock(
|
||||
user_id="test-user",
|
||||
team_id=None,
|
||||
team_models=[],
|
||||
models=[],
|
||||
user_role="proxy_admin",
|
||||
),
|
||||
):
|
||||
response = client.get(
|
||||
"/model_group/info",
|
||||
headers={"Authorization": "Bearer sk-test"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"data": []}
|
||||
Reference in New Issue
Block a user