fix(proxy): defer optional Prisma imports in SSO settings (#49771)

This commit is contained in:
yuneng-jiang 2026-11-03 12:10:15 -08:00 • committed by GitHub
parent f52e5057f9
commit 200ecdb6e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 31 additions and 2 deletions

View file

@ -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

View file

@ -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",
)

View file

@ -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),