build: migrate packaging metadata to uv

This commit is contained in:
user 2026-04-02 04:56:27 +00:00
parent 7250cba3db
commit e103d3dcc6
No known key found for this signature in database
11 changed files with 11017 additions and 8651 deletions

View file

@ -1,7 +0,0 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
package = []
[metadata]
lock-version = "2.0"
python-versions = ">=3.8.1,<4.0, !=3.9.7"
content-hash = "2cf39473e67ff0615f0a61c9d2ac9f02b38cc08cbb1bdb893d89bee002646623"

View file

@ -1,30 +1,29 @@
[tool.poetry]
[project]
name = "litellm-enterprise"
version = "0.1.35"
description = "Package for LiteLLM Enterprise features"
authors = ["BerriAI"]
readme = "README.md"
requires-python = ">=3.9"
license-files = ["LICENSE.md"]
authors = [
{ name = "BerriAI" },
]
[tool.poetry.urls]
homepage = "https://litellm.ai"
[project.urls]
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"
requires = ["uv_build>=0.10.7,<0.11"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-root = ""
[tool.commitizen]
version = "0.1.35"
version_files = [
"pyproject.toml:version",
"../requirements.txt:litellm-enterprise==",
"../pyproject.toml:litellm-enterprise = {version = \""
]
"pyproject.toml:^version",
"../pyproject.toml:litellm-enterprise==",
]

View file

@ -1,7 +0,0 @@
# This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand.
package = []
[metadata]
lock-version = "2.1"
python-versions = ">=3.8.1,<4.0, !=3.9.7"
content-hash = "2cf39473e67ff0615f0a61c9d2ac9f02b38cc08cbb1bdb893d89bee002646623"

View file

@ -1,30 +1,29 @@
[tool.poetry]
[project]
name = "litellm-proxy-extras"
version = "0.4.62"
description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package."
authors = ["BerriAI"]
readme = "README.md"
requires-python = ">=3.9"
license-files = ["LICENSE"]
authors = [
{ name = "BerriAI" },
]
[tool.poetry.urls]
homepage = "https://litellm.ai"
[project.urls]
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"
requires = ["uv_build>=0.10.7,<0.11"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-root = ""
[tool.commitizen]
version = "0.4.62"
version_files = [
"pyproject.toml:version",
"../requirements.txt:litellm-proxy-extras==",
"../pyproject.toml:litellm-proxy-extras = {version = \""
]
"pyproject.toml:^version",
"../pyproject.toml:litellm-proxy-extras==",
]

8312
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,189 +1,231 @@
[tool.poetry]
[project]
name = "litellm"
version = "1.83.0"
description = "Library to easily interface with LLM API providers"
authors = ["BerriAI"]
license = "MIT"
readme = "README.md"
packages = [
{ include = "litellm" },
{ include = "litellm/py.typed"},
requires-python = ">=3.9"
license = "MIT"
license-files = ["LICENSE"]
authors = [
{ name = "BerriAI" },
]
dependencies = [
"fastuuid==0.14.0",
"httpx==0.28.1",
"openai==2.24.0",
"python-dotenv==1.0.1",
"tiktoken==0.12.0",
"importlib-metadata==8.5.0",
"tokenizers==0.22.2",
"click==8.1.8",
"jinja2==3.1.6",
"aiohttp==3.13.3",
"pydantic==2.12.5",
"jsonschema==4.23.0",
]
[tool.poetry.urls]
homepage = "https://litellm.ai"
[project.urls]
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"
# Dependencies pinned from `pip install litellm[proxy]==1.83.0` PyPI resolution.
# Docker builds use requirements.txt (different pins). These two paths are independent.
[tool.poetry.dependencies]
python = ">=3.9,<4.0"
fastuuid = "0.14.0"
httpx = "0.28.1"
openai = "2.30.0"
python-dotenv = "1.0.1"
tiktoken = "0.12.0"
importlib-metadata = "8.5.0"
tokenizers = "0.22.2"
click = "8.1.8"
jinja2 = "3.1.6"
aiohttp = "3.13.5"
pydantic = "2.12.5"
jsonschema = "4.23.0"
numpydoc = {version = "1.8.0", optional = true} # not in Docker or PyPI proxy extra
uvicorn = {version = "0.33.0", optional = true}
uvloop = {version = "0.21.0", optional = true, markers="sys_platform != 'win32'"}
gunicorn = {version = "23.0.0", optional = true}
fastapi = {version = "0.124.4", optional = true}
backoff = {version = "2.2.1", optional = true}
pyyaml = {version = "6.0.3", optional = true}
rq = {version = "2.7.0", optional = true}
orjson = {version = "3.10.15", optional = true}
apscheduler = {version = "3.11.2", optional = true}
fastapi-sso = { version = "0.16.0", optional = true }
PyJWT = { version = "2.12.1", optional = true, python = ">=3.9" }
python-multipart = { version = "0.0.20", optional = true}
cryptography = {version = "43.0.3", optional = true} # Docker uses 46.0.5; pyproject uses 43.0.3 for Poetry Python 3.9.0/3.9.1 compat
prisma = {version = "0.11.0", optional = true}
azure-identity = {version = "1.25.3", optional = true, python = ">=3.9"}
azure-keyvault-secrets = {version = "4.10.0", optional = true}
azure-storage-blob = {version = "12.28.0", optional = true}
google-cloud-kms = {version = "2.24.2", optional = true} # not in PyPI proxy extra
google-cloud-iam = {version = "2.19.1", optional = true}
google-cloud-aiplatform = {version = "1.133.0", optional = true}
resend = {version = "2.23.0", optional = true} # not in PyPI proxy extra
pynacl = {version = "1.6.2", optional = true}
websockets = {version = "15.0.1", optional = true}
boto3 = { version = "1.42.80", optional = true }
redisvl = {version = "0.4.1", optional = true, markers = "python_version >= '3.9' and python_version < '3.14'"}
mcp = {version = "1.26.0", optional = true, python = ">=3.10"}
a2a-sdk = {version = "0.3.25", optional = true, python = ">=3.10"}
litellm-proxy-extras = {version = "0.4.62", optional = true}
rich = {version = "13.9.4", optional = true}
litellm-enterprise = {version = "0.1.35", optional = true}
diskcache = {version = "5.6.3", optional = true}
polars = {version = "1.39.3", optional = true, python = ">=3.10"}
semantic-router = {version = "0.1.12", optional = true, python = ">=3.9,<3.14"}
mlflow = {version = "3.9.0", optional = true, python = ">=3.10"} # not in PyPI proxy extra
soundfile = {version = "0.12.1", optional = true}
pyroscope-io = {version = "0.8.16", optional = true, markers = "sys_platform != 'win32'"}
# grpcio: pinned to 1.80.0 (past reconnect bug #38290 in 1.68.x, has Python 3.14 wheels)
grpcio = {version = "1.80.0", optional = true}
[tool.poetry.extras]
# Dependencies pinned from the published `litellm[proxy]==1.83.0` resolution.
# Docker and CI should prefer `uv.lock` rather than maintaining parallel installers.
[project.optional-dependencies]
proxy = [
"gunicorn",
"uvicorn",
"uvloop",
"fastapi",
"backoff",
"pyyaml",
"rq",
"orjson",
"apscheduler",
"fastapi-sso",
"PyJWT",
"python-multipart",
"cryptography",
"pynacl",
"websockets",
"boto3",
"azure-identity",
"azure-storage-blob",
"mcp",
"litellm-proxy-extras",
"litellm-enterprise",
"rich",
"polars",
"soundfile",
"pyroscope-io",
"gunicorn==23.0.0",
"uvicorn==0.33.0",
"uvloop==0.21.0; sys_platform != 'win32'",
"fastapi==0.124.4",
"backoff==2.2.1",
"pyyaml==6.0.3",
"rq==2.7.0",
"orjson==3.10.15",
"apscheduler==3.11.2",
"fastapi-sso==0.16.0",
"PyJWT==2.11.0; python_version >= '3.9'",
"python-multipart==0.0.20",
# Docker uses 46.0.5; pyproject uses 43.0.3 for Python 3.9.0/3.9.1 compat.
"cryptography==43.0.3",
"pynacl==1.6.2",
"websockets==15.0.1",
"boto3==1.42.59",
"azure-identity==1.25.2; python_version >= '3.9'",
"azure-storage-blob==12.28.0",
"mcp==1.26.0; python_version >= '3.10'",
"litellm-proxy-extras==0.4.62",
"litellm-enterprise==0.1.35",
"rich==13.9.4",
"polars==1.38.1; python_version >= '3.10'",
"soundfile==0.12.1",
"pyroscope-io==0.8.16; sys_platform != 'win32'",
]
extra_proxy = [
"prisma",
"azure-identity",
"azure-keyvault-secrets",
"google-cloud-kms",
"google-cloud-iam",
"resend",
"redisvl",
"a2a-sdk"
"prisma==0.11.0",
"nodejs-wheel-binaries==24.13.1",
"azure-identity==1.25.2; python_version >= '3.9'",
"azure-keyvault-secrets==4.10.0",
# Not in PyPI proxy extra.
"google-cloud-kms==2.24.2",
"google-cloud-iam==2.19.1",
# Not in PyPI proxy extra.
"resend==2.23.0",
"redisvl==0.4.1; python_version >= '3.9' and python_version < '3.14'",
"a2a-sdk==0.3.24; python_version >= '3.10'",
]
utils = [
"numpydoc",
# Not in Docker or PyPI proxy extra.
"numpydoc==1.8.0",
]
caching = ["diskcache==5.6.3"]
semantic-router = [
"semantic-router==0.1.12; python_version >= '3.9' and python_version < '3.14'",
"aurelio-sdk==0.0.19; python_version >= '3.9' and python_version < '3.14'",
]
mlflow = ["mlflow==3.9.0; python_version >= '3.10'"]
grpc = [
# Newest non-yanked release older than the 30-day cutoff.
"grpcio==1.78.0",
]
google = ["google-cloud-aiplatform==1.133.0"]
proxy-runtime = [
# Historically bundled in the proxy Docker images via requirements.txt.
# Keep these in a dedicated extra so uv-based images preserve the same
# feature surface without forcing the base SDK install to grow.
"google-cloud-aiplatform==1.133.0",
"google-genai==1.37.0",
"anthropic[vertex]==0.84.0",
"grpcio==1.78.0",
"prometheus-client==0.20.0",
"langfuse==2.59.7",
"opentelemetry-api==1.28.0",
"opentelemetry-sdk==1.28.0",
"opentelemetry-exporter-otlp==1.28.0",
"ddtrace==2.19.0",
"sentry-sdk==2.21.0",
"mangum==0.17.0",
"azure-ai-contentsafety==1.0.0",
"azure-storage-file-datalake==12.20.0",
"pypdf==6.7.5; python_version < '3.14'",
"llm-sandbox==0.3.31; python_version >= '3.10'",
"detect-secrets==1.5.0",
]
[project.scripts]
litellm = "litellm:run_server"
litellm-proxy = "litellm.proxy.client.cli:cli"
[dependency-groups]
dev = [
"diff-cover==9.7.2",
"flake8==7.3.0",
"black==24.10.0",
"mypy==1.19.0",
"pytest==8.3.5",
"pytest-mock==3.15.1",
"pytest-asyncio==1.2.0",
"pytest-postgresql==7.0.2",
"pytest-xdist==3.8.0",
"requests-mock==1.12.1",
"responses==0.26.0",
"respx==0.22.0",
"ruff==0.15.3",
"types-requests==2.32.4.20260107; python_version >= '3.10'",
"types-setuptools==75.8.0.20250225",
"types-redis==4.6.0.20241004",
"types-PyYAML==6.0.12.20250915",
"opentelemetry-api==1.28.0",
"opentelemetry-sdk==1.28.0",
"opentelemetry-exporter-otlp==1.28.0",
"langfuse==2.59.7",
"fastapi-offline==1.7.6",
"fakeredis==2.34.1",
"pytest-rerunfailures==15.1",
"pytest-cov==5.0.0",
"parameterized==0.9.0",
"openapi-core==0.22.0; python_version < '3.14'",
"pytest-timeout==2.4.0",
]
proxy-dev = [
"prisma==0.11.0",
"hypercorn==0.17.3",
"prometheus-client==0.20.0",
"opentelemetry-api==1.28.0",
"opentelemetry-sdk==1.28.0",
"opentelemetry-exporter-otlp==1.28.0",
"azure-identity==1.25.2; python_version >= '3.9'",
"a2a-sdk==0.3.24; python_version >= '3.10'",
"nodejs-wheel-binaries==24.13.1",
]
ci = [
# pytest-postgresql imports psycopg v3 during pytest startup. Keep the base
# package and the binary wheel explicit so uv sync yields a usable driver
# without requiring contributors to install libpq separately.
"psycopg==3.2.13; python_version < '3.10'",
"psycopg==3.3.3; python_version >= '3.10'",
"psycopg-binary==3.2.13; python_version < '3.10'",
"psycopg-binary==3.3.3; python_version >= '3.10'",
# Azure batch E2E tests still import psycopg2 directly.
"psycopg2-binary==2.9.11",
"pytest-codspeed==4.3.0",
"tenacity==8.5.0",
"pytest-retry==1.7.0",
"google-generativeai==0.8.6",
"pyarrow==21.0.0; python_version < '3.10'",
"pyarrow==22.0.0; python_version >= '3.10'",
"langchain==0.3.27; python_version < '3.10'",
"langchain==1.2.10; python_version >= '3.10'",
"lunary==1.0.36; python_version < '3.10'",
"lunary==1.4.36; python_version == '3.10'",
"lunary==1.4.37; python_version >= '3.11'",
"logfire==4.6.0",
"traceloop-sdk==0.33.12",
"detect-secrets==1.5.0",
"PyGithub==2.8.1",
"aiodynamo==24.7",
"argon2-cffi==25.1.0",
"Pillow==11.3.0; python_version < '3.10'",
"Pillow==12.1.1; python_version >= '3.10'",
"assemblyai==0.52.4",
"jsonlines==4.0.0",
"anthropic==0.84.0",
"blockbuster==1.5.26",
"beautifulsoup4==4.14.3",
"pylint==3.3.9; python_version < '3.10'",
"pylint==4.0.5; python_version >= '3.10'",
"pyright==1.1.408",
"langchain-mcp-adapters==0.2.1; python_version >= '3.10'",
"langchain-openai==1.1.10; python_version >= '3.10'",
"langgraph==1.0.10; python_version >= '3.10'",
"claude-agent-sdk==0.1.44; python_version >= '3.10'",
]
caching = ["diskcache"]
[build-system]
requires = ["uv_build>=0.10.7,<0.11"]
build-backend = "uv_build"
semantic-router = ["semantic-router"]
[tool.uv]
default-groups = ["dev"]
exclude-newer = "30 days"
mlflow = ["mlflow"]
[tool.uv.sources]
litellm-proxy-extras = { workspace = true }
litellm-enterprise = { workspace = true }
grpc = ["grpcio"]
[tool.uv.workspace]
members = ["enterprise", "litellm-proxy-extras"]
google = ["google-cloud-aiplatform"]
[tool.uv.build-backend]
module-root = ""
source-exclude = ["litellm/proxy/enterprise"]
[tool.isort]
profile = "black"
[tool.poetry.scripts]
litellm = 'litellm:run_server'
litellm-proxy = 'litellm.proxy.client.cli:cli'
[tool.poetry.group.dev.dependencies]
diff-cover = "9.7.2"
flake8 = "7.3.0"
black = "24.10.0"
mypy = "1.19.0"
pytest = "8.3.5"
pytest-mock = "3.15.1"
pytest-asyncio = "1.2.0"
pytest-postgresql = "7.0.2"
pytest-xdist = "3.8.0"
requests-mock = "1.12.1"
responses = "0.26.0"
respx = "0.22.0"
ruff = "0.15.3"
types-requests = {version = "2.32.4.20260107", python = ">=3.10"}
types-setuptools = "75.8.0.20250225"
types-redis = "4.6.0.20241004"
types-PyYAML = "6.0.12.20250915"
opentelemetry-api = "1.28.0"
opentelemetry-sdk = "1.28.0"
opentelemetry-exporter-otlp = "1.28.0"
langfuse = "2.59.7"
fastapi-offline = "1.7.6"
fakeredis = "2.34.1"
pytest-rerunfailures = "15.1"
pytest-cov = "5.0.0"
parameterized = "0.9.0"
[tool.poetry.group.proxy-dev.dependencies]
prisma = "0.11.0"
hypercorn = "0.17.3"
prometheus-client = "0.20.0"
opentelemetry-api = "1.28.0"
opentelemetry-sdk = "1.28.0"
opentelemetry-exporter-otlp = "1.28.0"
azure-identity = {version = "1.25.3", python = ">=3.9"}
a2a-sdk = {version = "0.3.25", python = ">=3.10"}
[build-system]
requires = ["poetry-core", "wheel"]
build-backend = "poetry.core.masonry.api"
[tool.commitizen]
version = "1.83.0"
version_files = [
"pyproject.toml:^version"
"pyproject.toml:^version",
]
[tool.mypy]

View file

@ -1,88 +0,0 @@
# LITELLM PROXY DEPENDENCIES #
# Security: explicit pins for transitive deps (CVE fixes)
urllib3==2.6.3 # CVE-2025-66471, CVE-2025-66418, CVE-2026-21441
tornado==6.5.5 # CVE-2025-67725, CVE-2025-67726, CVE-2025-67724, CVE-2026-31958, GHSA-78cv-mqj4-43f7
filelock==3.25.2 # CVE-2025-68146
h11==0.16.0 # CVE-2025-43859, GHSA-vqfr-h8mv-ghfj — HTTP request smuggling
wheel==0.46.3 # CVE-2026-24049 — path traversal
Pillow==12.1.1 #GHSA-cfh3-3jmp-rvhc
cryptography==46.0.5 #GHSA-r6ph-v2qm-q3c2
anyio==4.8.0 # openai + http req.
httpx==0.28.1
openai==2.30.0 # openai req.
fastapi==0.124.4 # server dep
starlette==0.49.1 # starlette fastapi dep
backoff==2.2.1 # server dep
pyyaml==6.0.3 # server dep
uvicorn==0.33.0 # server dep
gunicorn==23.0.0 # server dep
fastuuid==0.14.0 # for uuid4
uvloop==0.21.0 # uvicorn dep, gives us much better performance under load
boto3==1.42.80 # aws bedrock/sagemaker calls
redis==5.2.1 # redis caching
redisvl==0.4.1 ## redis semantic caching
prisma==0.11.0 # for db
nodejs-wheel-binaries==24.13.1 ## required by prisma for migrations, prevents runtime download (updated from nodejs-bin for security fixes)
mangum==0.17.0 # for aws lambda functions
pynacl==1.6.2 # for encrypting keys
google-cloud-aiplatform==1.133.0 # for vertex ai calls
google-cloud-iam==2.19.1 # for GCP IAM Redis authentication
google-genai==1.37.0
anthropic[vertex]==0.54.0
mcp==1.26.0 ; python_version >= "3.10" # for MCP server
# google-generativeai removed - deprecated, replaced by google-genai (line 21)
async_generator==1.10.0 # for async ollama calls
langfuse==2.59.7 # for langfuse self-hosted logging
prometheus_client==0.20.0 # for /metrics endpoint on proxy
ddtrace==2.19.0 # for advanced DD tracing / profiling
orjson==3.10.15 # fast /embedding responses
polars==1.39.3 # for data processing
apscheduler==3.11.2 # for resetting budget in background
fastapi-sso==0.16.0 # admin UI, SSO
pyjwt[crypto]==2.12.1 ; python_version >= "3.9"
python-multipart==0.0.20 # admin UI
jaraco.context==6.1.2
azure-ai-contentsafety==1.0.0 # for azure content safety
azure-identity==1.25.3 ; python_version >= "3.9" # for azure content safety
azure-keyvault==4.2.0 # for azure KMS integration
azure-storage-file-datalake==12.20.0 # for azure buck storage logging
opentelemetry-api==1.28.0
opentelemetry-sdk==1.28.0
opentelemetry-exporter-otlp==1.28.0
a2a-sdk==0.3.25 ; python_version >= "3.10"
# grpcio: pinned to 1.80.0 (past reconnect bug #38290 in 1.68.x, has Python 3.14 wheels)
grpcio==1.80.0
sentry_sdk==2.21.0 # for sentry error handling
detect-secrets==1.5.0 # Enterprise - secret detection / masking in LLM requests
tzdata==2025.1 # IANA time zone database
litellm-proxy-extras==0.4.62 # for proxy extras - e.g. prisma migrations
llm-sandbox==0.3.31 # for skill execution in sandbox
### LITELLM PACKAGE DEPENDENCIES
python-dotenv==1.0.1 # for env
tiktoken==0.12.0 # for calculating usage
importlib-metadata==8.5.0 # for random utils
tokenizers==0.22.2 # for calculating usage
click==8.1.8 # for proxy cli
rich==13.9.4 # for litellm proxy cli
jinja2==3.1.6 # for prompt templates
aiohttp==3.13.5 # for network calls
tenacity==8.5.0 # for retrying requests, when litellm.num_retries set
pydantic==2.12.5 # proxy + openai req. + mcp
jsonschema==4.23.0 # validating json schema - aligned with openapi-core + mcp
websockets==15.0.1 # for realtime API
soundfile==0.12.1 # for audio file processing
openapi-core==0.21.0 # for OpenAPI compliance tests
pypdf==6.9.2 # for PDF text extraction in RAG ingestion (CVE-2026-27888)
# Transitive deps pinned to prevent floating between builds
aiofiles==24.1.0 # transitive dep (langfuse)
colorlog==6.10.1 # transitive dep (ddtrace)
grpc-google-iam-v1==0.14.3 # transitive dep (google-cloud-iam)
hf-xet==1.4.2 # transitive dep (huggingface_hub)
requests-toolbelt==1.0.0 # transitive dep (langfuse)
########################
# LITELLM ENTERPRISE DEPENDENCIES
########################
litellm-enterprise==0.1.35

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python3
import sys
import tomllib
import requests
from packaging.requirements import Requirement
@ -52,6 +53,11 @@ class LicenseChecker:
# Track package results
self.package_results: List[PackageLicense] = []
@staticmethod
def _normalize_package_name(package_name: str) -> str:
"""Canonicalize package names so '-', '_' and '.' compare equivalently."""
return re.sub(r"[-_.]+", "-", package_name).lower()
def _parse_license_list(self, section: str, option: str) -> Set[str]:
"""Parse license list from config, handling comments and whitespace."""
if not self.config.has_option(section, option):
@ -70,7 +76,7 @@ class LicenseChecker:
if self.config.has_section("Authorized Packages"):
for package, spec in self.config.items("Authorized Packages"):
if not package.startswith("#"):
package = package.strip().lower()
package = self._normalize_package_name(package.strip())
parts = spec.split("#", 1)
version_spec = parts[0].strip()
comment = parts[1].strip() if len(parts) > 1 else ""
@ -127,7 +133,7 @@ class LicenseChecker:
def check_package(self, package_name: str, version: Optional[str] = None) -> bool:
"""Check if a specific package version is compliant."""
package_lower = package_name.lower()
package_lower = self._normalize_package_name(package_name)
# Check if package is in authorized packages list
if package_lower in self.authorized_packages:
@ -207,19 +213,45 @@ class LicenseChecker:
return is_acceptable
def check_requirements(self, requirements_file: Path) -> bool:
"""Check all packages in a requirements file."""
print(f"\nChecking licenses for packages in {requirements_file}...")
def _load_requirements(
self, requirements_file: Optional[Path] = None
) -> List[Requirement]:
"""Load pinned requirements from a file or from the root pyproject.toml."""
try:
if requirements_file is not None:
with open(requirements_file) as f:
requirement_lines = f.readlines()
else:
with open("pyproject.toml", "rb") as f:
pyproject = tomllib.load(f)
requirement_lines = list(pyproject["project"].get("dependencies", []))
for extra_reqs in pyproject["project"].get(
"optional-dependencies", {}
).values():
requirement_lines.extend(extra_reqs)
# Preserve declaration order while removing duplicates.
requirement_lines = list(dict.fromkeys(requirement_lines))
return [
Requirement(line.split("#")[0].strip())
for line in requirement_lines
if line.split("#")[0].strip() and not line.startswith("#")
]
except Exception as e:
source = requirements_file or "uv export"
raise RuntimeError(f"Error parsing requirements from {source}: {str(e)}") from e
def check_requirements(self, requirements_file: Optional[Path] = None) -> bool:
"""Check all packages from a requirements file or the root pyproject."""
source = requirements_file or "pyproject.toml"
print(f"\nChecking licenses for packages in {source}...")
try:
with open(requirements_file) as f:
requirements = [
Requirement(line.split("#")[0].strip())
for line in f
if line.split("#")[0].strip() and not line.startswith("#")
]
except Exception as e:
print(f"Error parsing {requirements_file}: {str(e)}")
requirements = self._load_requirements(requirements_file)
except RuntimeError as e:
print(str(e))
return False
all_compliant = True
@ -243,12 +275,11 @@ class LicenseChecker:
def main():
# req_file = "../../requirements.txt" ## LOCAL TESTING
req_file = "./requirements.txt"
req_file = Path(sys.argv[1]) if len(sys.argv) > 1 else None
checker = LicenseChecker()
# Check requirements
if not checker.check_requirements(Path(req_file)):
if not checker.check_requirements(req_file):
# Get lists of problematic packages
unverified = [p for p in checker.package_results if not p.license_type]
invalid = [
@ -272,7 +303,7 @@ def main():
unhandled_packages = [
p
for p in (unverified + invalid)
if p.name.lower() not in checker.authorized_packages
if checker._normalize_package_name(p.name) not in checker.authorized_packages
]
if unhandled_packages:

View file

@ -136,7 +136,10 @@ pydantic: >=2.10.2 # Unknown license
jsonschema: >=4.22.0 # Unknown license
websockets: >=13.1.0 # Unknown license
polars: >=1.31.0 # Unknown license, the license.md allows free of charge use
semantic_router: >=0.1.10 # Unknown license
rq: >=2.7.0 # BSD-2-Clause License
resend: >=2.23.0 # MIT License
semantic_router: >=0.1.10 # MIT License
aurelio-sdk: >=0.0.19 # MIT License
pondpond: >=1.4.1 # Apache 2.0 License
fastuuid: >=0.13.0 # BSD-3-Clause license
llm-sandbox: >=0.3.31 # MIT License - https://github.com/vndee/llm-sandbox

View file

@ -18,14 +18,12 @@ def test_using_litellm():
print("litellm imported successfully")
except Exception as e:
pytest.fail(
f"Error occurred: {e}. Installing litellm on python3.8 failed please retry"
)
pytest.fail(f"Error occurred: {e}. Installing litellm failed please retry")
def test_litellm_proxy_server():
# Install the local litellm[proxy] package in development mode
subprocess.run(["pip", "install", "-e", ".[proxy]"])
# Sync the local litellm[proxy] dependencies into the project environment
subprocess.run(["uv", "sync", "--frozen", "--extra", "proxy"], check=True)
# Import the proxy_server module
try:
@ -39,11 +37,12 @@ def test_litellm_proxy_server():
def test_package_dependencies():
"""
Test that all optional dependencies are correctly specified in extras.
Test that all optional dependency entries are exposed via project optional-dependencies.
"""
try:
import pathlib
import litellm
from packaging.requirements import Requirement
# Try to import tomllib (Python 3.11+) or tomli (older versions)
try:
@ -62,28 +61,22 @@ def test_package_dependencies():
with open(pyproject_path, "rb") as f:
pyproject = tomli.load(f)
# Get all optional dependencies from poetry.dependencies
poetry_deps = pyproject["tool"]["poetry"]["dependencies"]
optional_deps = {
name.lower()
for name, value in poetry_deps.items()
if isinstance(value, dict) and value.get("optional", False)
}
print(optional_deps)
# Get all packages listed in extras
extras = pyproject["tool"]["poetry"]["extras"]
all_extra_deps = set()
for extra_group in extras.values():
all_extra_deps.update(dep.lower() for dep in extra_group)
print(all_extra_deps)
# Check that all optional dependencies are in some extras group
missing_from_extras = optional_deps - all_extra_deps
assert (
not missing_from_extras
), f"Optional dependencies missing from extras: {missing_from_extras}"
optional_deps = pyproject["project"]["optional-dependencies"]
assert optional_deps, "Expected project.optional-dependencies to be defined"
parsed_requirements = set()
for extra_name, requirements in optional_deps.items():
assert requirements, f"Optional dependency group '{extra_name}' is empty"
for requirement in requirements:
assert isinstance(
requirement, str
), f"Expected string requirement in extra '{extra_name}'"
parsed = Requirement(requirement)
parsed_requirements.add(parsed.name.lower())
print(parsed_requirements)
print(
f"All {len(optional_deps)} optional dependencies are correctly specified in extras"
f"Validated {len(parsed_requirements)} optional dependencies across {len(optional_deps)} extras groups"
)
except Exception as e:
@ -102,11 +95,13 @@ import requests
def test_litellm_proxy_server_config_no_general_settings():
# Install the local litellm packages in development mode
# Sync the local litellm packages into the project environment
server_process = None
try:
subprocess.run(["pip", "install", "-e", ".[proxy]"])
subprocess.run(["pip", "install", "-e", ".[extra_proxy]"])
subprocess.run(
["uv", "sync", "--frozen", "--group", "proxy-dev", "--extra", "proxy", "--extra", "extra_proxy"],
check=True,
)
# Ensure Prisma client is generated
try:
@ -115,7 +110,7 @@ def test_litellm_proxy_server_config_no_general_settings():
print(f"Running prisma generate from: {project_root}")
result = subprocess.run(
["prisma", "generate"],
["uv", "run", "--no-sync", "prisma", "generate"],
capture_output=True,
text=True,
check=True,

10713
uv.lock generated

File diff suppressed because it is too large Load diff