litellm/conftest.py
Cursor Agent ebf4346e0c
Big-bang: replace Prisma with SQLAlchemy + Prisma-compat shim
This is the rip-and-replace counterpart to the phased migration in
PR #28366. The proxy database layer is moved to SQLAlchemy + asyncpg in
a single PR. Existing prisma_client.db.<table>.<method>(...) call sites
keep working unchanged via a compatibility shim; the prisma PyPI package
is dropped from runtime dependencies.

The PR description carries the full breakage inventory.

Test pass rates after rip-and-replace:
* tests/test_litellm/proxy/db/                 247 / 263 = 93.9%
* tests/test_litellm/proxy/management_endpoints/ 1288 / 1289 = 99.9%
* tests/test_litellm/proxy/db/sqlmodel_orm/      38 / 38   = 100%
* combined                                     1573 / 1590 = 98.9%

The 17 failures are all in tests of Prisma-internal subprocess /
watchdog / RoutingPrismaWrapper mechanics that no longer apply to a
SQLAlchemy backend.

Architecture:

* litellm/proxy/db/sqlmodel/schema_parser.py
  Pure-Python parser for the subset of Prisma DSL used by schema.prisma.
  (carried forward from the Phase 1 PR.)

* litellm/proxy/db/sqlmodel/_generate.py
  Code generator that emits Black-formatted SQLModel classes from a
  parsed schema. Output uses JSONB().with_variant(JSON(), 'sqlite')
  so the model definitions are portable to SQLite test environments.

* litellm/proxy/db/sqlmodel/models.py
  SQLModel classes for all 64 Prisma models, generator-seeded.

* litellm/proxy/db/sqlmodel/engine.py
  LiteLLMDB wrapping the async SQLAlchemy engine + sessionmaker, with
  pluggable RDS IAM token rotation (engine recreate on each tick) and
  optional read-replica routing.

* litellm/proxy/db/sqlmodel/compat.py
  PrismaCompatClient exposes the historical Prisma surface --
  per-table accessors, find_unique/first/many, create/update/upsert/
  delete/update_many/delete_many/create_many/count, query_raw/
  execute_raw, batch_(), tx() -- backed by SQLAlchemy. Filter
  translation covers in/contains/startswith/endswith/lt/lte/gt/gte/has
  /AND/OR/NOT/equals plus mode:insensitive and data={...:{increment:N}}.

* litellm/proxy/db/sqlmodel/errors.py
  Native error classes (PrismaError, UniqueViolationError, ...) raised
  by the shim. SQLAlchemy IntegrityError / NoResultFound / ProgrammingError
  are translated to the corresponding LiteLLM-native types.

* tests/_prisma_compat.py + conftest.py
  Test-only stand-in for the prisma namespace so legacy
  from prisma.errors import X imports in test fixtures still resolve
  to the LiteLLM-native errors. Production code has been ported off
  from prisma imports entirely.

Wiring:

* litellm/proxy/utils.py PrismaClient.__init__ now constructs
  LiteLLMDB + PrismaCompatClient instead of Prisma + PrismaWrapper +
  RoutingPrismaWrapper. _get_engine_pid returns 0 (no subprocess).

* The 9 production files that used from prisma.errors import ... /
  import prisma are redirected to the native errors module:
  exception_handler, log_db_metrics, toolset_db, budget/config_override
  /mcp/workflow management endpoints, key_management_endpoints. The
  prisma.Json(...) marker in key_management is replaced by an
  identity shim (SQLAlchemy JSONB accepts dicts directly).

* prisma==0.11.0 dropped from extra_proxy and proxy-dev in pyproject.toml.

Known gaps (called out explicitly in the PR body):

* include= for relation eager-loading is logged-and-ignored. SQLModel
  classes don't carry SQLAlchemy relationship() defs yet; ~55
  production sites pass include={} and would see None for the
  relation attribute. Hand-written relationships per table is the next
  step.
* select=, group_by, and some/every relation filters
  raise NotImplementedError -- 3, 4, and 2 production sites respectively.
* litellm-proxy-extras Prisma migration bundle is unchanged. Alembic
  baseline + recovery path is the next mechanical step.
* schema.prisma copies, the sync workflows, helm migration jobs, and
  Dockerfile prisma-generate steps are unchanged. All rote follow-ups.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-05-20 21:54:58 +00:00

29 lines
986 B
Python

"""Repository-root pytest configuration.
After the SQLAlchemy big-bang migration, the ``prisma`` PyPI package is
no longer installed but a handful of test modules still do ``from prisma
... import ...``. We install a stand-in for the ``prisma`` namespace at
plugin-load time -- *before* pytest collects test modules -- so those
imports resolve without rewriting every test file in this PR.
The stand-in lives in ``tests/_prisma_compat.py`` and re-exports the
LiteLLM-native error classes from ``litellm.proxy.db.sqlmodel.errors``.
"""
from __future__ import annotations
import os
import sys
# Make ``tests/_prisma_compat`` importable regardless of where pytest is
# invoked from.
_TESTS_DIR = os.path.join(os.path.dirname(__file__), "tests")
if _TESTS_DIR not in sys.path:
sys.path.insert(0, _TESTS_DIR)
try:
from _prisma_compat import install as _install_prisma_compat
_install_prisma_compat()
except ImportError: # pragma: no cover -- shim is best-effort
pass