mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description `litellm` is a hard dependency and its metadata caps `Requires-Python >=3.10,<3.14`, so `pip install headroom-ai` is unsatisfiable on Python 3.14. But litellm is only used for model registry / pricing / non-core providers — all lazily imported behind `ImportError` guards — never on the core compression or Anthropic proxy path. Refs #956 (install half). ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) ## Changes Made - Add a `python_version < '3.14'` marker to both litellm declarations (core deps + dev extra); installs unchanged on <=3.13, skipped on 3.14 (matches the existing rapidocr/tomli marker pattern). ## Testing - [x] Unit tests pass (`pytest`) - [x] New tests added for new functionality - [x] Manual testing performed ### Test Output ```text $ pytest tests/test_litellm_optional.py -q 2 passed in 0.10s $ python3.14 -m pip install dist/headroom_ai-0.25.0-cp310-abi3-linux_x86_64.whl Successfully installed headroom-ai-0.25.0 ... # litellm NOT installed $ python3.14 -c "import importlib.util as u; print(u.find_spec('litellm') is not None)" False ``` ## Real Behavior Proof - Environment: fresh venv on CPython 3.14.5, Linux - Exact command / steps: built the abi3 wheel, `pip install` it on Python 3.14, then `import headroom` + start the proxy + send a compressible request - Observed result: install exits 0 with litellm skipped; `import headroom` works; the proxy compresses (29913 -> 27626 tokens). Stock 0.25.0 cannot install on 3.14 at all. - Not tested: litellm-backed features on 3.14 (intentionally unavailable there until litellm supports 3.14) ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
54 lines
2 KiB
Python
54 lines
2 KiB
Python
"""litellm must be optional on Python 3.14 (GH #956).
|
|
|
|
litellm's metadata pins requires-python <3.14, so a hard dependency makes
|
|
`pip install headroom-ai` unsatisfiable on 3.14. headroom only uses litellm for
|
|
model registry / pricing / non-core providers (all lazily imported and
|
|
ImportError-guarded), so every litellm requirement must carry a marker that
|
|
skips it on 3.14, and the core paths must degrade gracefully without it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
try:
|
|
import tomllib
|
|
except ModuleNotFoundError: # Python 3.10
|
|
import tomli as tomllib # type: ignore[no-redef]
|
|
|
|
from packaging.requirements import Requirement
|
|
|
|
PYPROJECT = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
|
|
|
|
def _litellm_requirements() -> list[Requirement]:
|
|
data = tomllib.loads(PYPROJECT.read_text(encoding="utf-8"))
|
|
specs = list(data["project"].get("dependencies", []))
|
|
for extra in data["project"].get("optional-dependencies", {}).values():
|
|
specs.extend(extra)
|
|
return [r for spec in specs if (r := Requirement(spec)).name == "litellm"]
|
|
|
|
|
|
def test_every_litellm_requirement_is_skipped_on_py314() -> None:
|
|
reqs = _litellm_requirements()
|
|
assert reqs, "expected litellm to be declared in pyproject"
|
|
for r in reqs:
|
|
assert r.marker is not None, f"{r}: litellm must carry a python_version marker (GH #956)"
|
|
assert not r.marker.evaluate({"python_version": "3.14"}), (
|
|
f"{r}: must be skipped on Python 3.14"
|
|
)
|
|
assert r.marker.evaluate({"python_version": "3.13"}), (
|
|
f"{r}: must still install on Python 3.13"
|
|
)
|
|
|
|
|
|
def test_proxy_cost_degrades_without_litellm(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
# With litellm absent (its state on 3.14), the proxy cost path must return
|
|
# None rather than raise.
|
|
from headroom.proxy import cost
|
|
|
|
monkeypatch.setattr(cost, "LITELLM_AVAILABLE", False)
|
|
monkeypatch.setattr(cost, "litellm", None)
|
|
assert cost._get_litellm_module() is None
|