mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
Some checks are pending
CodSpeed Benchmarks / benchmarks (push) Waiting to run
Cost map pins recheck / rerun-cost-map-guard (push) Waiting to run
Publish lint base counts / publish (basedpyright, scripts/type_check_gate.py) (push) Waiting to run
Publish lint base counts / publish (openapi-docs, scripts/openapi_docs_gate.py) (push) Waiting to run
Publish lint base counts / publish (ruff-strict, scripts/ruff_strict_gate.py) (push) Waiting to run
Publish lint base counts / publish (test-quality, scripts/test_quality_gate.py) (push) Waiting to run
Publish lint base counts / publish (type-discipline, scripts/type_discipline_gate.py) (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
LiteLLM Rust DB / db-queries (push) Waiting to run
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Waiting to run
Terraform Modules / fmt, validate, test (gcp) (push) Waiting to run
unit / enterprise-package (push) Blocked by required conditions
unit / integrations (push) Blocked by required conditions
unit / llms-anthropic (push) Blocked by required conditions
unit / llms-bedrock (push) Blocked by required conditions
unit / llms-openai-meta (push) Blocked by required conditions
unit / llms-providers (push) Blocked by required conditions
unit / llms-vertex-ai (push) Blocked by required conditions
unit / mcp-elicitation (push) Blocked by required conditions
unit / mcp-server (push) Blocked by required conditions
unit / proxy-auth (push) Blocked by required conditions
unit / proxy-endpoints (push) Blocked by required conditions
unit / proxy-extras (push) Blocked by required conditions
unit / proxy-feature-endpoints (push) Blocked by required conditions
unit / proxy-hooks-client (push) Blocked by required conditions
unit / proxy-infra-db-spend (push) Blocked by required conditions
unit / proxy-infra-root (push) Blocked by required conditions
unit / proxy-infra-routing (push) Blocked by required conditions
unit / router (push) Blocked by required conditions
unit / rust-bridge-harness (push) Blocked by required conditions
unit / auth-checks (push) Blocked by required conditions
unit / budgets (push) Blocked by required conditions
unit / custom-logging (push) Blocked by required conditions
unit / key-generation (push) Blocked by required conditions
unit / mcp-oauth (push) Blocked by required conditions
unit / proxy-runtime (push) Blocked by required conditions
unit / proxy-server-core (push) Blocked by required conditions
unit / root (push) Blocked by required conditions
unit / enterprise-repositories-secrets (push) Blocked by required conditions
unit / proxy-server (push) Blocked by required conditions
unit / lens-python-310 (push) Blocked by required conditions
unit / caching-local (push) Blocked by required conditions
unit / core-utils (push) Blocked by required conditions
unit / endpoints (push) Blocked by required conditions
unit / ui-unit (push) Waiting to run
unit / responses-caching-types (push) Blocked by required conditions
unit / db-and-spend (push) Blocked by required conditions
unit / endpoints-and-responses (push) Blocked by required conditions
unit / enterprise-routing (push) Blocked by required conditions
unit / guardrails-hooks (push) Blocked by required conditions
unit / jwt-and-keys (push) Blocked by required conditions
unit / logging-misc (push) Blocked by required conditions
unit / proxy-utils (push) Blocked by required conditions
unit / rust-bridge (push) Waiting to run
unit / assert-shard-coverage (push) Waiting to run
unit / enterprise-managed-files (push) Blocked by required conditions
unit / docs (push) Waiting to run
unit / helm (push) Waiting to run
unit / OpenAI 2.20.0 compatibility (push) Blocked by required conditions
unit / OpenAI 3.0.0 compatibility (push) Blocked by required conditions
unit / OpenAI 3.25.0 compatibility (push) Blocked by required conditions
unit / redis-py 5.3.1 compatibility (push) Blocked by required conditions
unit / redis-py 6.4.0 compatibility (push) Blocked by required conditions
unit / redis-py 7.4.1 compatibility (push) Blocked by required conditions
unit / redis-py 8.0.1 compatibility (push) Blocked by required conditions
unit / coverage (push) Blocked by required conditions
unit / unit passed (push) Blocked by required conditions
GitHub Actions Security Analysis / zizmor (push) Waiting to run
* fix(proxy): add tzdata to the proxy extra so a legacy TZ doesn't stop startup * style(proxy): drop the inline comment on the tzdata requirement * test(proxy): check tzdata is installed with the proxy extra on every platform * fix(proxy): set the first spend report run in UTC so a scheduler zone that differs from the system clock can't skip it --------- Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
140 lines
5.3 KiB
Python
140 lines
5.3 KiB
Python
import sys
|
|
from pathlib import Path
|
|
from typing import Final
|
|
|
|
import pytest
|
|
from packaging.requirements import Requirement
|
|
from packaging.specifiers import SpecifierSet
|
|
from packaging.utils import NormalizedName, canonicalize_name
|
|
from packaging.version import Version
|
|
|
|
if sys.version_info >= (3, 11):
|
|
import tomllib
|
|
else:
|
|
import tomli as tomllib
|
|
|
|
PROJECT_ROOT: Final = Path(__file__).resolve().parents[2]
|
|
PYPROJECT: Final = tomllib.loads((PROJECT_ROOT / "pyproject.toml").read_text())
|
|
LOCK: Final = tomllib.loads((PROJECT_ROOT / "uv.lock").read_text())
|
|
OPTIONAL_DEPENDENCIES: Final = PYPROJECT["project"]["optional-dependencies"]
|
|
AIOHTTP_POOL_POISONING_RANGE: Final = SpecifierSet(">=3.14.0,<3.14.2")
|
|
AIOHTTP_POOL_POISONING_RELEASES: Final = ("3.14.0", "3.14.1")
|
|
AIOHTTP_POOL_POISONING_SOURCE: Final = "aio-libs/aiohttp#12953, fixed in aiohttp 3.14.2"
|
|
ANYIO_STRANDED_LOCK_WAITER_FIX: Final = Version("4.14.0")
|
|
ANYIO_STRANDED_LOCK_WAITER_SOURCE: Final = "agronholm/anyio#1145, fixed in anyio 4.14.0 released 2026-06-15"
|
|
CLI_RUNTIME_DEPENDENCIES: Final = frozenset({"rich", "pyyaml", "requests"})
|
|
PROXY_INSTALL_PLATFORMS: Final = (
|
|
("linux", "Linux", "posix"),
|
|
("darwin", "Darwin", "posix"),
|
|
("win32", "Windows", "nt"),
|
|
)
|
|
PROXY_SERVER_ONLY_DEPENDENCIES: Final = frozenset(
|
|
{
|
|
"fastapi",
|
|
"uvicorn",
|
|
"gunicorn",
|
|
"granian",
|
|
"starlette",
|
|
"boto3",
|
|
"polars",
|
|
"soundfile",
|
|
"mcp",
|
|
"cryptography",
|
|
"apscheduler",
|
|
"rq",
|
|
"litellm-enterprise",
|
|
"litellm-proxy-extras",
|
|
}
|
|
)
|
|
|
|
|
|
def _locked_package(name: str) -> dict[str, object]:
|
|
return next(package for package in LOCK["package"] if canonicalize_name(package["name"]) == name)
|
|
|
|
|
|
def _requirement_names(requirements: list[str]) -> frozenset[NormalizedName]:
|
|
return frozenset(canonicalize_name(Requirement(requirement).name) for requirement in requirements)
|
|
|
|
|
|
def _proxy_requirements_installed_on(sys_platform: str, platform_system: str, os_name: str) -> frozenset[NormalizedName]:
|
|
environment: Final = {
|
|
"sys_platform": sys_platform,
|
|
"platform_system": platform_system,
|
|
"os_name": os_name,
|
|
"extra": "proxy",
|
|
}
|
|
return frozenset(
|
|
canonicalize_name(requirement.name)
|
|
for requirement in map(Requirement, OPTIONAL_DEPENDENCIES["proxy"])
|
|
if requirement.marker is None or requirement.marker.evaluate(environment)
|
|
)
|
|
|
|
|
|
def test_importing_litellm_reports_the_declared_release_version() -> None:
|
|
import litellm
|
|
|
|
assert litellm._version.version == PYPROJECT["project"]["version"]
|
|
|
|
|
|
def test_every_declared_extra_is_locked_with_its_requirements() -> None:
|
|
declared: Final = {
|
|
canonicalize_name(extra): _requirement_names(requirements)
|
|
for extra, requirements in OPTIONAL_DEPENDENCIES.items()
|
|
}
|
|
locked: Final = _locked_package("litellm")
|
|
locked_extras: Final = {
|
|
canonicalize_name(extra): frozenset(canonicalize_name(entry["name"]) for entry in entries)
|
|
for extra, entries in locked["optional-dependencies"].items()
|
|
}
|
|
provided: Final = frozenset(canonicalize_name(extra) for extra in locked["metadata"]["provides-extras"])
|
|
assert declared
|
|
assert all(declared.values())
|
|
assert frozenset(declared) == provided
|
|
assert declared == locked_extras
|
|
|
|
|
|
def test_cli_extra_is_a_thin_client_install() -> None:
|
|
cli: Final = _requirement_names(OPTIONAL_DEPENDENCIES["cli"])
|
|
assert CLI_RUNTIME_DEPENDENCIES - cli == frozenset()
|
|
assert cli & PROXY_SERVER_ONLY_DEPENDENCIES == frozenset()
|
|
|
|
|
|
@pytest.mark.parametrize(("sys_platform", "platform_system", "os_name"), PROXY_INSTALL_PLATFORMS)
|
|
def test_proxy_extra_installs_tzdata_beside_its_scheduler_on_every_platform(
|
|
sys_platform: str, platform_system: str, os_name: str
|
|
) -> None:
|
|
installed: Final = _proxy_requirements_installed_on(sys_platform, platform_system, os_name)
|
|
assert canonicalize_name("apscheduler") in installed
|
|
assert canonicalize_name("tzdata") in installed
|
|
|
|
|
|
def test_declared_aiohttp_floor_excludes_pool_poisoning_releases() -> None:
|
|
declared: Final = tuple(
|
|
Requirement(requirement)
|
|
for requirement in PYPROJECT["project"]["dependencies"]
|
|
if canonicalize_name(Requirement(requirement).name) == "aiohttp"
|
|
)
|
|
assert len(declared) == 1
|
|
assert (
|
|
tuple(release for release in AIOHTTP_POOL_POISONING_RELEASES if declared[0].specifier.contains(release)) == ()
|
|
), AIOHTTP_POOL_POISONING_SOURCE
|
|
|
|
|
|
def test_locked_aiohttp_version_is_not_pool_poisoning() -> None:
|
|
locked: Final = _locked_package("aiohttp")["version"]
|
|
assert isinstance(locked, str)
|
|
assert not AIOHTTP_POOL_POISONING_RANGE.contains(locked), AIOHTTP_POOL_POISONING_SOURCE
|
|
|
|
|
|
def test_declared_anyio_floor_is_at_or_above_stranded_lock_waiter_fix() -> None:
|
|
declared: Final = tuple(
|
|
Requirement(requirement)
|
|
for requirement in PYPROJECT["project"]["dependencies"]
|
|
if canonicalize_name(Requirement(requirement).name) == "anyio"
|
|
)
|
|
assert len(declared) == 1, ANYIO_STRANDED_LOCK_WAITER_SOURCE
|
|
assert declared[0].marker is None, ANYIO_STRANDED_LOCK_WAITER_SOURCE
|
|
assert any(
|
|
specifier.operator == ">=" and Version(specifier.version) >= ANYIO_STRANDED_LOCK_WAITER_FIX
|
|
for specifier in declared[0].specifier
|
|
), ANYIO_STRANDED_LOCK_WAITER_SOURCE
|