diff --git a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py index 6d31fa3f226..30df407e853 100644 --- a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py +++ b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py @@ -6,6 +6,7 @@ from collections import Counter from collections.abc import Mapping, MutableMapping, Sequence from types import MappingProxyType from typing import ( + TYPE_CHECKING, Annotated, Final, Literal, @@ -16,7 +17,6 @@ from typing import ( from urllib.parse import urlparse from fastapi import APIRouter, Body, Depends, File, HTTPException, UploadFile -from prisma.errors import PrismaError from pydantic import BaseModel, ConfigDict, JsonValue, TypeAdapter, ValidationError, create_model, field_validator from pydantic.fields import FieldInfo, PydanticUndefined from typing_extensions import NotRequired, ReadOnly, TypedDict @@ -63,6 +63,9 @@ from litellm.types.proxy.management_endpoints.ui_sso import ( SSOConfig, ) +if TYPE_CHECKING: + from prisma.errors import PrismaError + router: Final = APIRouter() _DECODED_JSON: Final = TypeAdapter(object) @@ -1260,7 +1263,9 @@ def _parse_environment_variables_param(param_value: str | Mapping[str, object] | async def _load_environment_variables_param( repo: ConfigRepository, -) -> Mapping[str, object] | None | PrismaError | ValueError: +) -> "Mapping[str, object] | None | PrismaError | ValueError": + from prisma.errors import PrismaError + try: entry: Final = await _config_param_db(repo).find_unique(where={"param_name": "environment_variables"}) param_value: Final = None if entry is None else _parse_environment_variables_param(entry.param_value) @@ -1314,6 +1319,8 @@ async def update_sso_settings( detail={"error": "Set `'STORE_MODEL_IN_DB='True'` in your env to enable this feature."}, ) + from prisma.errors import PrismaError + # Read the existing SSO row first so the audit log captures a real # before/after diff. Stored values are encrypted; decrypt them so the # before-snapshot has the same shape as after_value, and rely on diff --git a/scripts/check_mcp_sdk_install.py b/scripts/check_mcp_sdk_install.py index f2c2891bfd8..f5261bc22f8 100644 --- a/scripts/check_mcp_sdk_install.py +++ b/scripts/check_mcp_sdk_install.py @@ -13,6 +13,7 @@ IMPORTED_MODULES: Final[tuple[str, ...]] = ( "litellm.proxy.mcp_server.server", "litellm.proxy.mcp_server.mcp_server_manager", "litellm.proxy.mcp_server.rest_endpoints", + "litellm.proxy.proxy_server", ) diff --git a/tests/unit/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py b/tests/unit/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py index e66050877df..4087c3e6413 100644 --- a/tests/unit/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py +++ b/tests/unit/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py @@ -18,6 +18,27 @@ from litellm.types.proxy.management_endpoints.ui_sso import ( client = TestClient(app) +@pytest.mark.asyncio +async def test_sso_settings_without_prisma_reports_database_not_connected(monkeypatch: pytest.MonkeyPatch) -> None: + import runpy + import sys + + from fastapi import HTTPException + + from litellm.proxy import proxy_server + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.ui_crud_endpoints import proxy_setting_endpoints + + monkeypatch.setattr(proxy_server, "prisma_client", None) + with patch.dict(sys.modules, {"prisma": None, "prisma.errors": None}): + endpoints: Final = runpy.run_path(proxy_setting_endpoints.__file__) + with pytest.raises(HTTPException) as error: + await endpoints["update_sso_settings"](SSOConfig(), UserAPIKeyAuth()) + + assert error.value.status_code == 500 + assert error.value.detail == {"error": "Database not connected. Please connect a database."} + + @pytest.mark.asyncio @pytest.mark.parametrize("config_value,stored_value,expected", [ (None, None, False), (None, False, False), (None, True, True),