From 1604f87663f7d6081e99a266b241538bf25ca29f Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Sat, 29 Mar 2025 15:27:09 -0700 Subject: [PATCH] install prisma migration files - connects litellm proxy to litellm's prisma migration files (#9637) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 https://github.com/BerriAI/litellm/issues/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 --- .pre-commit-config.yaml | 23 ++--- ci_cd/publish-proxy-extras.sh | 19 ++++ ci_cd/run_migration.py | 5 +- docs/my-website/docs/proxy/config_settings.md | 1 + litellm-proxy-extras/LICENSE | 26 ++++++ litellm-proxy-extras/README.md | 21 +++++ ...itellm_proxy_extras-0.1.0-py3-none-any.whl | Bin 0 -> 5320 bytes .../dist/litellm_proxy_extras-0.1.0.tar.gz | Bin 0 -> 3390 bytes ...itellm_proxy_extras-0.1.1-py3-none-any.whl | Bin 0 -> 6307 bytes .../dist/litellm_proxy_extras-0.1.1.tar.gz | Bin 0 -> 4245 bytes .../litellm_proxy_extras/__init__.py | 0 .../litellm_proxy_extras/_logging.py | 12 +++ .../20250326162113_baseline/migration.sql | 0 .../migration.sql | 0 .../migration.sql | 0 .../migration.sql | 0 .../migrations/migration_lock.toml | 0 .../litellm_proxy_extras/utils.py | 80 +++++++++++++++++ litellm-proxy-extras/pyproject.toml | 30 +++++++ litellm-proxy-extras/tests/__init__.py | 0 litellm/proxy/db/prisma_client.py | 81 +++--------------- litellm/proxy/proxy_cli.py | 2 +- poetry.lock | 15 +++- pyproject.toml | 4 +- requirements.txt | 1 + tests/code_coverage_tests/liccheck.ini | 3 +- .../test_db_schema_migration.py | 2 +- 27 files changed, 229 insertions(+), 96 deletions(-) create mode 100644 ci_cd/publish-proxy-extras.sh create mode 100644 litellm-proxy-extras/LICENSE create mode 100644 litellm-proxy-extras/README.md create mode 100644 litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0-py3-none-any.whl create mode 100644 litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0.tar.gz create mode 100644 litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1-py3-none-any.whl create mode 100644 litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1.tar.gz create mode 100644 litellm-proxy-extras/litellm_proxy_extras/__init__.py create mode 100644 litellm-proxy-extras/litellm_proxy_extras/_logging.py rename {deploy => litellm-proxy-extras/litellm_proxy_extras}/migrations/20250326162113_baseline/migration.sql (100%) rename {deploy => litellm-proxy-extras/litellm_proxy_extras}/migrations/20250326171002_add_daily_user_table/migration.sql (100%) rename {deploy => litellm-proxy-extras/litellm_proxy_extras}/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql (100%) rename {deploy => litellm-proxy-extras/litellm_proxy_extras}/migrations/20250329084805_new_cron_job_table/migration.sql (100%) rename {deploy => litellm-proxy-extras/litellm_proxy_extras}/migrations/migration_lock.toml (100%) create mode 100644 litellm-proxy-extras/litellm_proxy_extras/utils.py create mode 100644 litellm-proxy-extras/pyproject.toml create mode 100644 litellm-proxy-extras/tests/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bceedb41aa..dedb37d6dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,46 +6,35 @@ repos: entry: pyright language: system types: [python] - files: ^litellm/ + files: ^(litellm/|litellm_proxy_extras/) - id: isort name: isort entry: isort language: system types: [python] - files: litellm/.*\.py + files: (litellm/|litellm_proxy_extras/).*\.py exclude: ^litellm/__init__.py$ - id: black name: black entry: poetry run black language: system types: [python] - files: litellm/.*\.py + files: (litellm/|litellm_proxy_extras/).*\.py - repo: https://github.com/pycqa/flake8 rev: 7.0.0 # The version of flake8 to use hooks: - id: flake8 exclude: ^litellm/tests/|^litellm/proxy/tests/|^litellm/tests/litellm/|^tests/litellm/ additional_dependencies: [flake8-print] - files: litellm/.*\.py - # - id: flake8 - # name: flake8 (router.py function length) - # files: ^litellm/router\.py$ - # args: [--max-function-length=40] - # # additional_dependencies: [flake8-functions] + files: (litellm/|litellm_proxy_extras/).*\.py - repo: https://github.com/python-poetry/poetry rev: 1.8.0 hooks: - id: poetry-check + files: ^(pyproject.toml|litellm-proxy-extras/pyproject.toml)$ - repo: local hooks: - id: check-files-match name: Check if files match entry: python3 ci_cd/check_files_match.py - language: system - # - id: check-file-length - # name: Check file length - # entry: python check_file_length.py - # args: ["10000"] # set your desired maximum number of lines - # language: python - # files: litellm/.*\.py - # exclude: ^litellm/tests/ \ No newline at end of file + language: system \ No newline at end of file diff --git a/ci_cd/publish-proxy-extras.sh b/ci_cd/publish-proxy-extras.sh new file mode 100644 index 0000000000..6c83d1f921 --- /dev/null +++ b/ci_cd/publish-proxy-extras.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Exit on error +set -e + +echo "🚀 Building and publishing litellm-proxy-extras" + +# Navigate to litellm-proxy-extras directory +cd "$(dirname "$0")/../litellm-proxy-extras" + +# Build the package +echo "📦 Building package..." +poetry build + +# Publish to PyPI +echo "🌎 Publishing to PyPI..." +poetry publish + +echo "✅ Done! Package published successfully" \ No newline at end of file diff --git a/ci_cd/run_migration.py b/ci_cd/run_migration.py index 69b4308e57..b11a38395c 100644 --- a/ci_cd/run_migration.py +++ b/ci_cd/run_migration.py @@ -8,7 +8,7 @@ import shutil def create_migration(migration_name: str = None): """ - Create a new migration SQL file in deploy/migrations directory by comparing + Create a new migration SQL file in the migrations directory by comparing current database state with schema Args: @@ -17,8 +17,7 @@ def create_migration(migration_name: str = None): try: # Get paths root_dir = Path(__file__).parent.parent - deploy_dir = root_dir / "deploy" - migrations_dir = deploy_dir / "migrations" + migrations_dir = root_dir / "litellm-proxy-extras" / "litellm_proxy_extras" / "migrations" schema_path = root_dir / "schema.prisma" # Create temporary PostgreSQL database diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md index 4a62184df7..05b3e0be37 100644 --- a/docs/my-website/docs/proxy/config_settings.md +++ b/docs/my-website/docs/proxy/config_settings.md @@ -515,4 +515,5 @@ router_settings: | UPSTREAM_LANGFUSE_RELEASE | Release version identifier for upstream Langfuse | UPSTREAM_LANGFUSE_SECRET_KEY | Secret key for upstream Langfuse authentication | USE_AWS_KMS | Flag to enable AWS Key Management Service for encryption +| USE_PRISMA_MIGRATE | Flag to use prisma migrate instead of prisma db push. Recommended for production environments. | WEBHOOK_URL | URL for receiving webhooks from external services diff --git a/litellm-proxy-extras/LICENSE b/litellm-proxy-extras/LICENSE new file mode 100644 index 0000000000..3bfef5bae9 --- /dev/null +++ b/litellm-proxy-extras/LICENSE @@ -0,0 +1,26 @@ +Portions of this software are licensed as follows: + +* All content that resides under the "enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "enterprise/LICENSE". +* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below. +--- +MIT License + +Copyright (c) 2023 Berri AI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/litellm-proxy-extras/README.md b/litellm-proxy-extras/README.md new file mode 100644 index 0000000000..29453f65ba --- /dev/null +++ b/litellm-proxy-extras/README.md @@ -0,0 +1,21 @@ +Additional files for the proxy. Reduces the size of the main litellm package. + +Currently, only stores the migration.sql files for litellm-proxy. + +To install, run: + +```bash +pip install litellm-proxy-extras +``` +OR + +```bash +pip install litellm[proxy] # installs litellm-proxy-extras and other proxy dependencies. +``` + +To use the migrations, run: + +```bash +litellm --use_prisma_migrate +``` + diff --git a/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0-py3-none-any.whl b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..1aff64ef585290c8856d6876ba687c4caa5efbae GIT binary patch literal 5320 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 Upg@5Fg&zz456yD*`T(E+0CEJs7ytkO literal 0 HcmV?d00001 diff --git a/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1-py3-none-any.whl b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..e2583935a4cf41ec0154a387fd9083f51caac5c7 GIT binary patch literal 6307 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 literal 0 HcmV?d00001 diff --git a/litellm-proxy-extras/litellm_proxy_extras/__init__.py b/litellm-proxy-extras/litellm_proxy_extras/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/litellm-proxy-extras/litellm_proxy_extras/_logging.py b/litellm-proxy-extras/litellm_proxy_extras/_logging.py new file mode 100644 index 0000000000..118caecf48 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/_logging.py @@ -0,0 +1,12 @@ +import logging + +# Set up package logger +logger = logging.getLogger("litellm_proxy_extras") +if not logger.handlers: # Only add handler if none exists + handler = logging.StreamHandler() + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + handler.setFormatter(formatter) + logger.addHandler(handler) + logger.setLevel(logging.INFO) diff --git a/deploy/migrations/20250326162113_baseline/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326162113_baseline/migration.sql similarity index 100% rename from deploy/migrations/20250326162113_baseline/migration.sql rename to litellm-proxy-extras/litellm_proxy_extras/migrations/20250326162113_baseline/migration.sql diff --git a/deploy/migrations/20250326171002_add_daily_user_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326171002_add_daily_user_table/migration.sql similarity index 100% rename from deploy/migrations/20250326171002_add_daily_user_table/migration.sql rename to litellm-proxy-extras/litellm_proxy_extras/migrations/20250326171002_add_daily_user_table/migration.sql diff --git a/deploy/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql similarity index 100% rename from deploy/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql rename to litellm-proxy-extras/litellm_proxy_extras/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql diff --git a/deploy/migrations/20250329084805_new_cron_job_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250329084805_new_cron_job_table/migration.sql similarity index 100% rename from deploy/migrations/20250329084805_new_cron_job_table/migration.sql rename to litellm-proxy-extras/litellm_proxy_extras/migrations/20250329084805_new_cron_job_table/migration.sql diff --git a/deploy/migrations/migration_lock.toml b/litellm-proxy-extras/litellm_proxy_extras/migrations/migration_lock.toml similarity index 100% rename from deploy/migrations/migration_lock.toml rename to litellm-proxy-extras/litellm_proxy_extras/migrations/migration_lock.toml diff --git a/litellm-proxy-extras/litellm_proxy_extras/utils.py b/litellm-proxy-extras/litellm_proxy_extras/utils.py new file mode 100644 index 0000000000..894ae34122 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/utils.py @@ -0,0 +1,80 @@ +import os +import random +import subprocess +import time +from typing import Optional + +from litellm_proxy_extras._logging import logger + + +def str_to_bool(value: Optional[str]) -> bool: + if value is None: + return False + return value.lower() in ("true", "1", "t", "y", "yes") + + +class ProxyExtrasDBManager: + @staticmethod + def setup_database(schema_path: str, use_migrate: bool = False) -> bool: + """ + Set up the database using either prisma migrate or prisma db push + Uses migrations from litellm-proxy-extras package + + Args: + schema_path (str): Path to the Prisma schema file + use_migrate (bool): Whether to use prisma migrate instead of db push + + Returns: + bool: True if setup was successful, False otherwise + """ + use_migrate = str_to_bool(os.getenv("USE_PRISMA_MIGRATE")) or use_migrate + for attempt in range(4): + original_dir = os.getcwd() + schema_dir = os.path.dirname(schema_path) + os.chdir(schema_dir) + + try: + if use_migrate: + logger.info("Running prisma migrate deploy") + try: + # Set migrations directory for Prisma + subprocess.run( + ["prisma", "migrate", "deploy"], + timeout=60, + check=True, + capture_output=True, + text=True, + ) + logger.info("prisma migrate deploy completed") + return True + except subprocess.CalledProcessError as e: + logger.info(f"prisma db error: {e.stderr}, e: {e.stdout}") + if ( + "P3005" in e.stderr + and "database schema is not empty" in e.stderr + ): + logger.info("Error: Database schema is not empty") + return False + else: + # Use prisma db push with increased timeout + subprocess.run( + ["prisma", "db", "push", "--accept-data-loss"], + timeout=60, + check=True, + ) + return True + except subprocess.TimeoutExpired: + logger.info(f"Attempt {attempt + 1} timed out") + time.sleep(random.randrange(5, 15)) + except subprocess.CalledProcessError as e: + attempts_left = 3 - attempt + retry_msg = ( + f" Retrying... ({attempts_left} attempts left)" + if attempts_left > 0 + else "" + ) + logger.info(f"The process failed to execute. Details: {e}.{retry_msg}") + time.sleep(random.randrange(5, 15)) + finally: + os.chdir(original_dir) + return False diff --git a/litellm-proxy-extras/pyproject.toml b/litellm-proxy-extras/pyproject.toml new file mode 100644 index 0000000000..c130a7fa9b --- /dev/null +++ b/litellm-proxy-extras/pyproject.toml @@ -0,0 +1,30 @@ +[tool.poetry] +name = "litellm-proxy-extras" +version = "0.1.1" +description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package." +authors = ["BerriAI"] +readme = "README.md" + + +[tool.poetry.urls] +homepage = "https://litellm.ai" +Homepage = "https://litellm.ai" +repository = "https://github.com/BerriAI/litellm" +Repository = "https://github.com/BerriAI/litellm" +documentation = "https://docs.litellm.ai" +Documentation = "https://docs.litellm.ai" + +[tool.poetry.dependencies] +python = ">=3.8.1,<4.0, !=3.9.7" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.commitizen] +version = "0.1.1" +version_files = [ + "pyproject.toml:version", + "../requirements.txt:litellm-proxy-extras==", + "../pyproject.toml:litellm-proxy-extras = {version = \"" +] \ No newline at end of file diff --git a/litellm-proxy-extras/tests/__init__.py b/litellm-proxy-extras/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/litellm/proxy/db/prisma_client.py b/litellm/proxy/db/prisma_client.py index 4e38321e91..c81dc35e0c 100644 --- a/litellm/proxy/db/prisma_client.py +++ b/litellm/proxy/db/prisma_client.py @@ -179,42 +179,6 @@ class PrismaManager: verbose_proxy_logger.warning(f"Error creating baseline migration: {e}") return False - @staticmethod - def _copy_spend_tracking_migrations(prisma_dir: str) -> bool: - import shutil - from pathlib import Path - - """ - Check for and copy over spend tracking migrations if they exist in the deploy directory. - Returns True if migrations were found and copied, False otherwise. - """ - try: - # Get the current file's directory - current_dir = Path(__file__).parent - - # Check for migrations in the deploy directory (../../deploy/migrations) - deploy_migrations_dir = ( - current_dir.parent.parent.parent / "deploy" / "migrations" - ) - - # Local migrations directory - local_migrations_dir = Path(prisma_dir + "/migrations") - - if deploy_migrations_dir.exists(): - # Create local migrations directory if it doesn't exist - local_migrations_dir.mkdir(parents=True, exist_ok=True) - - # Copy all migration files - # Copy entire migrations folder recursively - shutil.copytree( - deploy_migrations_dir, local_migrations_dir, dirs_exist_ok=True - ) - - return True - return False - except Exception: - return False - @staticmethod def _get_migration_names(migrations_dir: str) -> list: """Get all migration directory names from the migrations folder""" @@ -251,6 +215,7 @@ class PrismaManager: bool: True if setup was successful, False otherwise """ + use_migrate = str_to_bool(os.getenv("USE_PRISMA_MIGRATE")) or use_migrate for attempt in range(4): original_dir = os.getcwd() prisma_dir = PrismaManager._get_prisma_dir() @@ -258,44 +223,20 @@ class PrismaManager: os.chdir(prisma_dir) try: if use_migrate: - PrismaManager._copy_spend_tracking_migrations( - prisma_dir - ) # place a migration in the migrations directory - verbose_proxy_logger.info("Running prisma migrate deploy") try: - subprocess.run( - ["prisma", "migrate", "deploy"], - timeout=60, - check=True, - capture_output=True, - text=True, + from litellm_proxy_extras.utils import ProxyExtrasDBManager + except ImportError as e: + verbose_proxy_logger.error( + f"\033[1;31mLiteLLM: Failed to import proxy extras. Got {e}\033[0m" ) - verbose_proxy_logger.info("prisma migrate deploy completed") + return False - # Resolve all migrations in the migrations directory - migrations_dir = os.path.join(prisma_dir, "migrations") - PrismaManager._resolve_all_migrations(migrations_dir) + prisma_dir = PrismaManager._get_prisma_dir() + schema_path = prisma_dir + "/schema.prisma" - return True - except subprocess.CalledProcessError as e: - verbose_proxy_logger.warning( - f"prisma db error: {e.stderr}, e: {e.stdout}" - ) - if ( - "P3005" in e.stderr - and "database schema is not empty" in e.stderr - ): - verbose_proxy_logger.info("Creating baseline migration") - if PrismaManager._create_baseline_migration(schema_path): - verbose_proxy_logger.info( - "Resolving all migrations after baseline" - ) - - # Resolve all migrations after baseline - migrations_dir = os.path.join(prisma_dir, "migrations") - PrismaManager._resolve_all_migrations(migrations_dir) - - return True + return ProxyExtrasDBManager.setup_database( + schema_path=schema_path, use_migrate=use_migrate + ) else: # Use prisma db push with increased timeout subprocess.run( diff --git a/litellm/proxy/proxy_cli.py b/litellm/proxy/proxy_cli.py index 8196eb597e..de78247daf 100644 --- a/litellm/proxy/proxy_cli.py +++ b/litellm/proxy/proxy_cli.py @@ -672,7 +672,7 @@ def run_server( # noqa: PLR0915 LiteLLMDatabaseConnectionPool.database_connection_pool_limit.value, ) db_connection_timeout = general_settings.get( - "database_connection_timeout", + "database_connection_pool_timeout", LiteLLMDatabaseConnectionPool.database_connection_pool_timeout.value, ) if database_url and database_url.startswith("os.environ/"): diff --git a/poetry.lock b/poetry.lock index d659d66952..157291f5f3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1676,6 +1676,17 @@ files = [ importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} referencing = ">=0.31.0" +[[package]] +name = "litellm-proxy-extras" +version = "0.1.1" +description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package." +optional = true +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" +files = [ + {file = "litellm_proxy_extras-0.1.1-py3-none-any.whl", hash = "sha256:2b3c4c5474bacbde2424c1cd13b21f85c65e9c4346f6159badd49a210eedef5c"}, + {file = "litellm_proxy_extras-0.1.1.tar.gz", hash = "sha256:a1eb911ad2e3742238863d314a8bd6d02dd0cc213ba040b2c0593f132fbf3117"}, +] + [[package]] name = "markupsafe" version = "2.1.5" @@ -4119,9 +4130,9 @@ type = ["pytest-mypy"] [extras] extra-proxy = ["azure-identity", "azure-keyvault-secrets", "google-cloud-kms", "prisma", "redisvl", "resend"] -proxy = ["PyJWT", "apscheduler", "backoff", "boto3", "cryptography", "fastapi", "fastapi-sso", "gunicorn", "mcp", "orjson", "pynacl", "python-multipart", "pyyaml", "rq", "uvicorn", "uvloop", "websockets"] +proxy = ["PyJWT", "apscheduler", "backoff", "boto3", "cryptography", "fastapi", "fastapi-sso", "gunicorn", "litellm-proxy-extras", "mcp", "orjson", "pynacl", "python-multipart", "pyyaml", "rq", "uvicorn", "uvloop", "websockets"] [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4.0, !=3.9.7" -content-hash = "36792478ff4afec5c8e748caf9b2ae6bebf3dd223e78bea2626b6589ef3277e4" +content-hash = "16cbf20784776377805f5e33c6bc97dce76303132aa3d81c7e6fe743f0ee3fc1" diff --git a/pyproject.toml b/pyproject.toml index 5eb4a71160..34564bd049 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ websockets = {version = "^13.1.0", optional = true} boto3 = {version = "1.34.34", optional = true} redisvl = {version = "^0.4.1", optional = true, markers = "python_version >= '3.9' and python_version < '3.14'"} mcp = {version = "1.5.0", optional = true, python = ">=3.10"} +litellm-proxy-extras = {version = "0.1.1", optional = true} [tool.poetry.extras] proxy = [ @@ -74,7 +75,8 @@ proxy = [ "pynacl", "websockets", "boto3", - "mcp" + "mcp", + "litellm-proxy-extras" ] extra_proxy = [ diff --git a/requirements.txt b/requirements.txt index c28a2e5cf2..b3f0f1d073 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,6 +38,7 @@ sentry_sdk==2.21.0 # for sentry error handling detect-secrets==1.5.0 # Enterprise - secret detection / masking in LLM requests cryptography==43.0.1 tzdata==2025.1 # IANA time zone database +litellm-proxy-extras==0.1.1 # for proxy extras - e.g. prisma migrations ### LITELLM PACKAGE DEPENDENCIES python-dotenv==1.0.0 # for env diff --git a/tests/code_coverage_tests/liccheck.ini b/tests/code_coverage_tests/liccheck.ini index efd7dffe1c..c5ecc898f2 100644 --- a/tests/code_coverage_tests/liccheck.ini +++ b/tests/code_coverage_tests/liccheck.ini @@ -85,4 +85,5 @@ anthropic: >=0.21.3 # MIT License detect-secrets: >=1.5.0 # MIT License importlib-metadata: >=6.8.0 # Apache 2.0 License tokenizers: >=0.20.2 # Apache 2.0 License -jinja2: >=3.1.4 # BSD 3-Clause License \ No newline at end of file +jinja2: >=3.1.4 # BSD 3-Clause License +litellm-proxy-extras: >=0.1.1 # MIT License \ No newline at end of file diff --git a/tests/proxy_unit_tests/test_db_schema_migration.py b/tests/proxy_unit_tests/test_db_schema_migration.py index 089c50ebf8..b317818375 100644 --- a/tests/proxy_unit_tests/test_db_schema_migration.py +++ b/tests/proxy_unit_tests/test_db_schema_migration.py @@ -24,7 +24,7 @@ def test_aaaasschema_migration_check(schema_setup, monkeypatch): # test_db_url = "postgresql://neondb_owner:npg_JiZPS0DAhRn4@ep-delicate-wave-a55cvbuc.us-east-2.aws.neon.tech/neondb?sslmode=require" monkeypatch.setenv("DATABASE_URL", test_db_url) - deploy_dir = Path("./deploy") + deploy_dir = Path("./litellm-proxy-extras/litellm_proxy_extras") source_migrations_dir = deploy_dir / "migrations" schema_path = Path("./schema.prisma")