diff --git a/.buildkite/check-torch-abi.py b/.buildkite/check-torch-abi.py new file mode 100644 index 0000000000..493952c33e --- /dev/null +++ b/.buildkite/check-torch-abi.py @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project + +"""Audit vLLM compiled libraries for PyTorch stable ABI compliance.""" + +import fnmatch +import sys +from pathlib import Path + +from torch_abi_audit import inspect_package +from torch_abi_audit.report import ExtensionReport, PackageReport + +# Temporary allowlist of extensions not yet on the stable ABI. +# Shrink and remove over time. +ALLOWED_UNSTABLE_LIBRARIES: tuple[str, ...] = ( + "vllm_flash_attn/_vllm_fa2_C.abi3.so", + "vllm_flash_attn/_vllm_fa3_C.abi3.so", + "third_party/deep_gemm/_C*.so", +) + + +def _relative_path(lib: ExtensionReport, package_root: Path) -> str: + try: + return lib.path.relative_to(package_root).as_posix() + except ValueError: + return lib.path.name + + +def _is_torch_unstable(lib: ExtensionReport) -> bool: + return lib.error is None and lib.torch.uses_torch and not lib.torch.stable + + +def _matches_allowlist(rel_path: str, patterns: tuple[str, ...]) -> bool: + return any(fnmatch.fnmatch(rel_path, pattern) for pattern in patterns) + + +def _iter_libs(report: PackageReport) -> tuple[ExtensionReport, ...]: + return (*report.extensions, *report.bundled_libs) + + +def _collect_unstable(report: PackageReport) -> list[str]: + return sorted( + _relative_path(lib, report.root) + for lib in _iter_libs(report) + if _is_torch_unstable(lib) + ) + + +def _find_stale_allowlist_entries( + report: PackageReport, patterns: tuple[str, ...] +) -> list[str]: + """Allowlist patterns that match a built library which is no longer unstable.""" + stale: list[str] = [] + for pattern in patterns: + for lib in _iter_libs(report): + if lib.error is not None: + continue + if not fnmatch.fnmatch(_relative_path(lib, report.root), pattern): + continue + if not _is_torch_unstable(lib): + stale.append(pattern) + break + return stale + + +def check_torch_abi( + package: str = "vllm", + patterns: tuple[str, ...] = ALLOWED_UNSTABLE_LIBRARIES, +) -> int: + report = inspect_package(package) + if report.error: + print(f"error: failed to inspect {package!r}: {report.error}", file=sys.stderr) + return 2 + + unstable = _collect_unstable(report) + unexpected = [ + rel_path for rel_path in unstable if not _matches_allowlist(rel_path, patterns) + ] + stale = _find_stale_allowlist_entries(report, patterns) + + if unexpected or stale: + if unexpected: + print( + "Not allowed: torch-unstable libraries outside " + f"ALLOWED_UNSTABLE_LIBRARIES: {', '.join(unexpected)}", + file=sys.stderr, + ) + if stale: + print( + "Not allowed: stale ALLOWED_UNSTABLE_LIBRARIES entries: " + f"{', '.join(stale)}", + file=sys.stderr, + ) + return 1 + + print("Torch stable ABI check passed.") + return 0 + + +if __name__ == "__main__": + print(">>> Auditing vLLM extension modules for PyTorch stable ABI compliance") + sys.exit(check_torch_abi()) diff --git a/.buildkite/ci_config.yaml b/.buildkite/ci_config.yaml index 21ffa1b9b8..9e1e46db67 100644 --- a/.buildkite/ci_config.yaml +++ b/.buildkite/ci_config.yaml @@ -14,6 +14,7 @@ run_all_patterns: - "setup.py" - "csrc/" - "cmake/" + - ".buildkite/check-torch-abi.py" run_all_exclude_patterns: - "docker/Dockerfile." - "csrc/cpu/" diff --git a/.buildkite/test_areas/torch_abi.yaml b/.buildkite/test_areas/torch_abi.yaml new file mode 100644 index 0000000000..eaef355166 --- /dev/null +++ b/.buildkite/test_areas/torch_abi.yaml @@ -0,0 +1,14 @@ +group: Torch ABI +depends_on: + - image-build +steps: +- label: Torch Stable ABI Audit + key: torch-stable-abi-audit + timeout_in_minutes: 5 + source_file_dependencies: + - .buildkite/check-torch-abi.py + - csrc/ + - cmake/ + - setup.py + commands: + - python3 /vllm-workspace/.buildkite/check-torch-abi.py diff --git a/requirements/test/cpu.txt b/requirements/test/cpu.txt index 3cb251a308..923b54bb14 100644 --- a/requirements/test/cpu.txt +++ b/requirements/test/cpu.txt @@ -1,5 +1,7 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements/test/cuda.in -o requirements/test/cpu.txt --index-strategy unsafe-best-match --torch-backend cpu --python-platform x86_64-manylinux_2_28 --python-version 3.12 +abi3info==2025.11.29 + # via torch-abi-audit absl-py==2.1.0 # via rouge-score accelerate==1.13.0 @@ -763,6 +765,8 @@ pycparser==2.22 # via cffi pycryptodomex==3.22.0 # via blobfile +pycxxfilt==0.1.0 + # via torch-abi-audit pydantic==2.12.0 # via # -r requirements/test/../common.txt @@ -1127,6 +1131,8 @@ torch==2.13.0+cpu # vector-quantize-pytorch # vocos # xgrammar +torch-abi-audit==0.0.1 + # via -r requirements/test/cuda.in torchaudio==2.11.0+cpu # via # -r requirements/test/cuda.in diff --git a/requirements/test/cuda.in b/requirements/test/cuda.in index b33257250e..377224eac3 100644 --- a/requirements/test/cuda.in +++ b/requirements/test/cuda.in @@ -45,7 +45,7 @@ schemathesis>=4.0.0 # Required for openai schema test. # quantization bitsandbytes==0.49.2 buildkite-test-collector==0.1.9 - +torch-abi-audit # CI check for PyTorch stable ABI compliance genai_perf>=0.0.8 tritonclient>=2.51.0 diff --git a/requirements/test/cuda.txt b/requirements/test/cuda.txt index 6490502cdd..6c155d89bd 100644 --- a/requirements/test/cuda.txt +++ b/requirements/test/cuda.txt @@ -1,5 +1,7 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements/test/cuda.in -c requirements/cuda.txt -o requirements/test/cuda.txt --index-strategy unsafe-best-match --torch-backend cu130 --python-platform x86_64-manylinux_2_28 --python-version 3.12 +abi3info==2025.11.29 + # via torch-abi-audit absl-py==2.1.0 # via rouge-score accelerate==1.13.0 @@ -850,6 +852,8 @@ pycparser==2.22 # via cffi pycryptodomex==3.22.0 # via blobfile +pycxxfilt==0.1.0 + # via torch-abi-audit pydantic==2.12.0 # via # -c requirements/common.txt @@ -1225,6 +1229,8 @@ torch==2.13.0+cu130 # vector-quantize-pytorch # vocos # xgrammar +torch-abi-audit==0.0.1 + # via -r requirements/test/cuda.in torchaudio==2.11.0+cu130 # via # -c requirements/cuda.txt