Skip to content

Commit 1604f87

Browse files
install prisma migration files - connects litellm proxy to litellm's prisma migration files (#9637)
* build(README.md): initial commit adding a separate folder for additional proxy files. Meant to reduce size of core package * build(litellm-proxy-extras/): new pip package for storing migration files allows litellm proxy to use migration files, without adding them to core repo * build(litellm-proxy-extras/): cleanup pyproject.toml * build: move prisma migration files inside new proxy extras package * build(run_migration.py): update script to write to correct folder * build(proxy_cli.py): load in migration files from litellm-proxy-extras Closes #9558 * build: add MIT license to litellm-proxy-extras * test: update test * fix: fix schema * bump: version 0.1.0 → 0.1.1 * build(publish-proxy-extras.sh): add script for publishing new proxy-extras version * build(liccheck.ini): add litellm-proxy-extras to authorized packages * fix(litellm-proxy-extras/utils.py): move prisma migrate logic inside extra proxy pkg easier since migrations folder already there * build(pre-commit-config.yaml): add litellm_proxy_extras to ci tests * docs(config_settings.md): document new env var * build(pyproject.toml): bump relevant files when litellm-proxy-extras version changed * build(pre-commit-config.yaml): run poetry check on litellm-proxy-extras as well
1 parent aa2489d commit 1604f87

File tree

27 files changed

+229
-96
lines changed

27 files changed

+229
-96
lines changed

.pre-commit-config.yaml

+6-17
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,35 @@ repos:
66
entry: pyright
77
language: system
88
types: [python]
9-
files: ^litellm/
9+
files: ^(litellm/|litellm_proxy_extras/)
1010
- id: isort
1111
name: isort
1212
entry: isort
1313
language: system
1414
types: [python]
15-
files: litellm/.*\.py
15+
files: (litellm/|litellm_proxy_extras/).*\.py
1616
exclude: ^litellm/__init__.py$
1717
- id: black
1818
name: black
1919
entry: poetry run black
2020
language: system
2121
types: [python]
22-
files: litellm/.*\.py
22+
files: (litellm/|litellm_proxy_extras/).*\.py
2323
- repo: https://github.com/pycqa/flake8
2424
rev: 7.0.0 # The version of flake8 to use
2525
hooks:
2626
- id: flake8
2727
exclude: ^litellm/tests/|^litellm/proxy/tests/|^litellm/tests/litellm/|^tests/litellm/
2828
additional_dependencies: [flake8-print]
29-
files: litellm/.*\.py
30-
# - id: flake8
31-
# name: flake8 (router.py function length)
32-
# files: ^litellm/router\.py$
33-
# args: [--max-function-length=40]
34-
# # additional_dependencies: [flake8-functions]
29+
files: (litellm/|litellm_proxy_extras/).*\.py
3530
- repo: https://github.com/python-poetry/poetry
3631
rev: 1.8.0
3732
hooks:
3833
- id: poetry-check
34+
files: ^(pyproject.toml|litellm-proxy-extras/pyproject.toml)$
3935
- repo: local
4036
hooks:
4137
- id: check-files-match
4238
name: Check if files match
4339
entry: python3 ci_cd/check_files_match.py
44-
language: system
45-
# - id: check-file-length
46-
# name: Check file length
47-
# entry: python check_file_length.py
48-
# args: ["10000"] # set your desired maximum number of lines
49-
# language: python
50-
# files: litellm/.*\.py
51-
# exclude: ^litellm/tests/
40+
language: system

ci_cd/publish-proxy-extras.sh

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
3+
# Exit on error
4+
set -e
5+
6+
echo "🚀 Building and publishing litellm-proxy-extras"
7+
8+
# Navigate to litellm-proxy-extras directory
9+
cd "$(dirname "$0")/../litellm-proxy-extras"
10+
11+
# Build the package
12+
echo "📦 Building package..."
13+
poetry build
14+
15+
# Publish to PyPI
16+
echo "🌎 Publishing to PyPI..."
17+
poetry publish
18+
19+
echo "✅ Done! Package published successfully"

ci_cd/run_migration.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
def create_migration(migration_name: str = None):
1010
"""
11-
Create a new migration SQL file in deploy/migrations directory by comparing
11+
Create a new migration SQL file in the migrations directory by comparing
1212
current database state with schema
1313
1414
Args:
@@ -17,8 +17,7 @@ def create_migration(migration_name: str = None):
1717
try:
1818
# Get paths
1919
root_dir = Path(__file__).parent.parent
20-
deploy_dir = root_dir / "deploy"
21-
migrations_dir = deploy_dir / "migrations"
20+
migrations_dir = root_dir / "litellm-proxy-extras" / "litellm_proxy_extras" / "migrations"
2221
schema_path = root_dir / "schema.prisma"
2322

2423
# Create temporary PostgreSQL database

docs/my-website/docs/proxy/config_settings.md

+1
Original file line numberDiff line numberDiff line change
@@ -515,4 +515,5 @@ router_settings:
515515
| UPSTREAM_LANGFUSE_RELEASE | Release version identifier for upstream Langfuse
516516
| UPSTREAM_LANGFUSE_SECRET_KEY | Secret key for upstream Langfuse authentication
517517
| USE_AWS_KMS | Flag to enable AWS Key Management Service for encryption
518+
| USE_PRISMA_MIGRATE | Flag to use prisma migrate instead of prisma db push. Recommended for production environments.
518519
| WEBHOOK_URL | URL for receiving webhooks from external services

litellm-proxy-extras/LICENSE

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Portions of this software are licensed as follows:
2+
3+
* All content that resides under the "enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "enterprise/LICENSE".
4+
* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below.
5+
---
6+
MIT License
7+
8+
Copyright (c) 2023 Berri AI
9+
10+
Permission is hereby granted, free of charge, to any person obtaining a copy
11+
of this software and associated documentation files (the "Software"), to deal
12+
in the Software without restriction, including without limitation the rights
13+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
copies of the Software, and to permit persons to whom the Software is
15+
furnished to do so, subject to the following conditions:
16+
17+
The above copyright notice and this permission notice shall be included in all
18+
copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+
SOFTWARE.

litellm-proxy-extras/README.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Additional files for the proxy. Reduces the size of the main litellm package.
2+
3+
Currently, only stores the migration.sql files for litellm-proxy.
4+
5+
To install, run:
6+
7+
```bash
8+
pip install litellm-proxy-extras
9+
```
10+
OR
11+
12+
```bash
13+
pip install litellm[proxy] # installs litellm-proxy-extras and other proxy dependencies.
14+
```
15+
16+
To use the migrations, run:
17+
18+
```bash
19+
litellm --use_prisma_migrate
20+
```
21+
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

litellm-proxy-extras/litellm_proxy_extras/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import logging
2+
3+
# Set up package logger
4+
logger = logging.getLogger("litellm_proxy_extras")
5+
if not logger.handlers: # Only add handler if none exists
6+
handler = logging.StreamHandler()
7+
formatter = logging.Formatter(
8+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
9+
)
10+
handler.setFormatter(formatter)
11+
logger.addHandler(handler)
12+
logger.setLevel(logging.INFO)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import os
2+
import random
3+
import subprocess
4+
import time
5+
from typing import Optional
6+
7+
from litellm_proxy_extras._logging import logger
8+
9+
10+
def str_to_bool(value: Optional[str]) -> bool:
11+
if value is None:
12+
return False
13+
return value.lower() in ("true", "1", "t", "y", "yes")
14+
15+
16+
class ProxyExtrasDBManager:
17+
@staticmethod
18+
def setup_database(schema_path: str, use_migrate: bool = False) -> bool:
19+
"""
20+
Set up the database using either prisma migrate or prisma db push
21+
Uses migrations from litellm-proxy-extras package
22+
23+
Args:
24+
schema_path (str): Path to the Prisma schema file
25+
use_migrate (bool): Whether to use prisma migrate instead of db push
26+
27+
Returns:
28+
bool: True if setup was successful, False otherwise
29+
"""
30+
use_migrate = str_to_bool(os.getenv("USE_PRISMA_MIGRATE")) or use_migrate
31+
for attempt in range(4):
32+
original_dir = os.getcwd()
33+
schema_dir = os.path.dirname(schema_path)
34+
os.chdir(schema_dir)
35+
36+
try:
37+
if use_migrate:
38+
logger.info("Running prisma migrate deploy")
39+
try:
40+
# Set migrations directory for Prisma
41+
subprocess.run(
42+
["prisma", "migrate", "deploy"],
43+
timeout=60,
44+
check=True,
45+
capture_output=True,
46+
text=True,
47+
)
48+
logger.info("prisma migrate deploy completed")
49+
return True
50+
except subprocess.CalledProcessError as e:
51+
logger.info(f"prisma db error: {e.stderr}, e: {e.stdout}")
52+
if (
53+
"P3005" in e.stderr
54+
and "database schema is not empty" in e.stderr
55+
):
56+
logger.info("Error: Database schema is not empty")
57+
return False
58+
else:
59+
# Use prisma db push with increased timeout
60+
subprocess.run(
61+
["prisma", "db", "push", "--accept-data-loss"],
62+
timeout=60,
63+
check=True,
64+
)
65+
return True
66+
except subprocess.TimeoutExpired:
67+
logger.info(f"Attempt {attempt + 1} timed out")
68+
time.sleep(random.randrange(5, 15))
69+
except subprocess.CalledProcessError as e:
70+
attempts_left = 3 - attempt
71+
retry_msg = (
72+
f" Retrying... ({attempts_left} attempts left)"
73+
if attempts_left > 0
74+
else ""
75+
)
76+
logger.info(f"The process failed to execute. Details: {e}.{retry_msg}")
77+
time.sleep(random.randrange(5, 15))
78+
finally:
79+
os.chdir(original_dir)
80+
return False

litellm-proxy-extras/pyproject.toml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[tool.poetry]
2+
name = "litellm-proxy-extras"
3+
version = "0.1.1"
4+
description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package."
5+
authors = ["BerriAI"]
6+
readme = "README.md"
7+
8+
9+
[tool.poetry.urls]
10+
homepage = "https://litellm.ai"
11+
Homepage = "https://litellm.ai"
12+
repository = "https://github.com/BerriAI/litellm"
13+
Repository = "https://github.com/BerriAI/litellm"
14+
documentation = "https://docs.litellm.ai"
15+
Documentation = "https://docs.litellm.ai"
16+
17+
[tool.poetry.dependencies]
18+
python = ">=3.8.1,<4.0, !=3.9.7"
19+
20+
[build-system]
21+
requires = ["poetry-core"]
22+
build-backend = "poetry.core.masonry.api"
23+
24+
[tool.commitizen]
25+
version = "0.1.1"
26+
version_files = [
27+
"pyproject.toml:version",
28+
"../requirements.txt:litellm-proxy-extras==",
29+
"../pyproject.toml:litellm-proxy-extras = {version = \""
30+
]

litellm-proxy-extras/tests/__init__.py

Whitespace-only changes.

litellm/proxy/db/prisma_client.py

+11-70
Original file line numberDiff line numberDiff line change
@@ -179,42 +179,6 @@ def _create_baseline_migration(schema_path: str) -> bool:
179179
verbose_proxy_logger.warning(f"Error creating baseline migration: {e}")
180180
return False
181181

182-
@staticmethod
183-
def _copy_spend_tracking_migrations(prisma_dir: str) -> bool:
184-
import shutil
185-
from pathlib import Path
186-
187-
"""
188-
Check for and copy over spend tracking migrations if they exist in the deploy directory.
189-
Returns True if migrations were found and copied, False otherwise.
190-
"""
191-
try:
192-
# Get the current file's directory
193-
current_dir = Path(__file__).parent
194-
195-
# Check for migrations in the deploy directory (../../deploy/migrations)
196-
deploy_migrations_dir = (
197-
current_dir.parent.parent.parent / "deploy" / "migrations"
198-
)
199-
200-
# Local migrations directory
201-
local_migrations_dir = Path(prisma_dir + "/migrations")
202-
203-
if deploy_migrations_dir.exists():
204-
# Create local migrations directory if it doesn't exist
205-
local_migrations_dir.mkdir(parents=True, exist_ok=True)
206-
207-
# Copy all migration files
208-
# Copy entire migrations folder recursively
209-
shutil.copytree(
210-
deploy_migrations_dir, local_migrations_dir, dirs_exist_ok=True
211-
)
212-
213-
return True
214-
return False
215-
except Exception:
216-
return False
217-
218182
@staticmethod
219183
def _get_migration_names(migrations_dir: str) -> list:
220184
"""Get all migration directory names from the migrations folder"""
@@ -251,51 +215,28 @@ def setup_database(use_migrate: bool = False) -> bool:
251215
bool: True if setup was successful, False otherwise
252216
"""
253217

218+
use_migrate = str_to_bool(os.getenv("USE_PRISMA_MIGRATE")) or use_migrate
254219
for attempt in range(4):
255220
original_dir = os.getcwd()
256221
prisma_dir = PrismaManager._get_prisma_dir()
257222
schema_path = prisma_dir + "/schema.prisma"
258223
os.chdir(prisma_dir)
259224
try:
260225
if use_migrate:
261-
PrismaManager._copy_spend_tracking_migrations(
262-
prisma_dir
263-
) # place a migration in the migrations directory
264-
verbose_proxy_logger.info("Running prisma migrate deploy")
265226
try:
266-
subprocess.run(
267-
["prisma", "migrate", "deploy"],
268-
timeout=60,
269-
check=True,
270-
capture_output=True,
271-
text=True,
227+
from litellm_proxy_extras.utils import ProxyExtrasDBManager
228+
except ImportError as e:
229+
verbose_proxy_logger.error(
230+
f"\033[1;31mLiteLLM: Failed to import proxy extras. Got {e}\033[0m"
272231
)
273-
verbose_proxy_logger.info("prisma migrate deploy completed")
232+
return False
274233

275-
# Resolve all migrations in the migrations directory
276-
migrations_dir = os.path.join(prisma_dir, "migrations")
277-
PrismaManager._resolve_all_migrations(migrations_dir)
234+
prisma_dir = PrismaManager._get_prisma_dir()
235+
schema_path = prisma_dir + "/schema.prisma"
278236

279-
return True
280-
except subprocess.CalledProcessError as e:
281-
verbose_proxy_logger.warning(
282-
f"prisma db error: {e.stderr}, e: {e.stdout}"
283-
)
284-
if (
285-
"P3005" in e.stderr
286-
and "database schema is not empty" in e.stderr
287-
):
288-
verbose_proxy_logger.info("Creating baseline migration")
289-
if PrismaManager._create_baseline_migration(schema_path):
290-
verbose_proxy_logger.info(
291-
"Resolving all migrations after baseline"
292-
)
293-
294-
# Resolve all migrations after baseline
295-
migrations_dir = os.path.join(prisma_dir, "migrations")
296-
PrismaManager._resolve_all_migrations(migrations_dir)
297-
298-
return True
237+
return ProxyExtrasDBManager.setup_database(
238+
schema_path=schema_path, use_migrate=use_migrate
239+
)
299240
else:
300241
# Use prisma db push with increased timeout
301242
subprocess.run(

litellm/proxy/proxy_cli.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ def run_server( # noqa: PLR0915
672672
LiteLLMDatabaseConnectionPool.database_connection_pool_limit.value,
673673
)
674674
db_connection_timeout = general_settings.get(
675-
"database_connection_timeout",
675+
"database_connection_pool_timeout",
676676
LiteLLMDatabaseConnectionPool.database_connection_pool_timeout.value,
677677
)
678678
if database_url and database_url.startswith("os.environ/"):

0 commit comments

Comments
 (0)