name: Reusable Build on: workflow_call: inputs: ref: description: 'Git ref to build' required: false type: string default: '' jobs: lint: name: Lint and Format Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install uv uses: astral-sh/setup-uv@v4 - name: Install ruff run: | uv tool install ruff - name: Run ruff check run: | ruff check . - name: Run ruff format check run: | ruff format --check . build: needs: lint name: Build ${{ matrix.os }} Python ${{ matrix.python }} strategy: matrix: include: - os: ubuntu-22.04 python: '3.9' - os: ubuntu-22.04 python: '3.10' - os: ubuntu-22.04 python: '3.11' - os: ubuntu-22.04 python: '3.12' - os: ubuntu-22.04 python: '3.13' - os: macos-latest python: '3.9' - os: macos-latest python: '3.10' - os: macos-latest python: '3.11' - os: macos-latest python: '3.12' - os: macos-latest python: '3.13' - os: macos-13 python: '3.9' - os: macos-13 python: '3.10' - os: macos-13 python: '3.11' - os: macos-13 python: '3.12' - os: macos-13 python: '3.13' runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} submodules: recursive - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install uv uses: astral-sh/setup-uv@v4 - name: Install system dependencies (Ubuntu) if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y libomp-dev libboost-all-dev protobuf-compiler libzmq3-dev \ pkg-config libopenblas-dev patchelf libabsl-dev libaio-dev libprotobuf-dev # Install Intel MKL for DiskANN wget -q https://registrationcenter-download.intel.com/akdlm/IRC_NAS/79153e0f-74d7-45af-b8c2-258941adf58a/intel-onemkl-2025.0.0.940.sh sudo sh intel-onemkl-2025.0.0.940.sh -a --components intel.oneapi.lin.mkl.devel --action install --eula accept -s source /opt/intel/oneapi/setvars.sh echo "MKLROOT=/opt/intel/oneapi/mkl/latest" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=/opt/intel/oneapi/mkl/latest/lib/intel64:$LD_LIBRARY_PATH" >> $GITHUB_ENV - name: Install system dependencies (macOS) if: runner.os == 'macOS' run: | # Don't install LLVM, use system clang for better compatibility brew install libomp boost protobuf zeromq - name: Install build dependencies run: | uv pip install --system scikit-build-core numpy swig Cython pybind11 if [[ "$RUNNER_OS" == "Linux" ]]; then uv pip install --system auditwheel else uv pip install --system delocate fi - name: Set macOS environment variables if: runner.os == 'macOS' run: | # Use brew --prefix to automatically detect Homebrew installation path HOMEBREW_PREFIX=$(brew --prefix) echo "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" >> $GITHUB_ENV echo "OpenMP_ROOT=${HOMEBREW_PREFIX}/opt/libomp" >> $GITHUB_ENV # Set CMAKE_PREFIX_PATH to let CMake find all packages automatically echo "CMAKE_PREFIX_PATH=${HOMEBREW_PREFIX}" >> $GITHUB_ENV # Set compiler flags for OpenMP (required for both backends) echo "LDFLAGS=-L${HOMEBREW_PREFIX}/opt/libomp/lib" >> $GITHUB_ENV echo "CPPFLAGS=-I${HOMEBREW_PREFIX}/opt/libomp/include" >> $GITHUB_ENV - name: Build packages run: | # Build core (platform independent) if [[ "${{ matrix.os }}" == ubuntu-* ]]; then cd packages/leann-core uv build cd ../.. fi # Build HNSW backend cd packages/leann-backend-hnsw if [[ "${{ matrix.os }}" == macos-* ]]; then # Use system clang for better compatibility export CC=clang export CXX=clang++ export MACOSX_DEPLOYMENT_TARGET=11.0 uv build --wheel --python ${{ matrix.python }} else uv build --wheel --python ${{ matrix.python }} fi cd ../.. # Build DiskANN backend cd packages/leann-backend-diskann if [[ "${{ matrix.os }}" == macos-* ]]; then # Use system clang for better compatibility export CC=clang export CXX=clang++ # DiskANN requires macOS 13.3+ for sgesdd_ LAPACK function export MACOSX_DEPLOYMENT_TARGET=13.3 uv build --wheel --python ${{ matrix.python }} else uv build --wheel --python ${{ matrix.python }} fi cd ../.. # Build meta package (platform independent) if [[ "${{ matrix.os }}" == ubuntu-* ]]; then cd packages/leann uv build cd ../.. fi - name: Repair wheels (Linux) if: runner.os == 'Linux' run: | # Repair HNSW wheel cd packages/leann-backend-hnsw if [ -d dist ]; then auditwheel repair dist/*.whl -w dist_repaired rm -rf dist mv dist_repaired dist fi cd ../.. # Repair DiskANN wheel cd packages/leann-backend-diskann if [ -d dist ]; then auditwheel repair dist/*.whl -w dist_repaired rm -rf dist mv dist_repaired dist fi cd ../.. - name: Repair wheels (macOS) if: runner.os == 'macOS' run: | # Repair HNSW wheel cd packages/leann-backend-hnsw if [ -d dist ]; then delocate-wheel -w dist_repaired -v dist/*.whl rm -rf dist mv dist_repaired dist fi cd ../.. # Repair DiskANN wheel cd packages/leann-backend-diskann if [ -d dist ]; then delocate-wheel -w dist_repaired -v dist/*.whl rm -rf dist mv dist_repaired dist fi cd ../.. - name: List built packages run: | echo "📦 Built packages:" find packages/*/dist -name "*.whl" -o -name "*.tar.gz" | sort - name: Debug wheel compatibility run: | echo "🔍 Checking wheel compatibility:" uv pip debug platform-tags | head -10 echo "" echo "Built wheel details:" for wheel in packages/*/dist/*.whl; do if [ -f "$wheel" ]; then echo " $(basename $wheel)" fi done - name: Install built packages for testing run: | # Create a virtual environment uv venv source .venv/bin/activate || source .venv/Scripts/activate # Install the built wheels directly without checking PyPI # This avoids the dependency resolution issue for new platforms if [[ "${{ matrix.os }}" == ubuntu-* ]]; then uv pip install packages/leann-core/dist/*.whl || uv pip install packages/leann-core/dist/*.tar.gz uv pip install packages/leann/dist/*.whl || uv pip install packages/leann/dist/*.tar.gz fi # Install backend wheels directly uv pip install packages/leann-backend-hnsw/dist/*.whl uv pip install packages/leann-backend-diskann/dist/*.whl # Install base dependencies needed for testing (without the local packages) uv pip install numpy torch tqdm flask flask_compress datasets evaluate colorama boto3 "protobuf==4.25.3" uv pip install "PyPDF2>=3.0.0" "pdfplumber>=0.11.0" "pymupdf>=1.26.0" "pypdfium2>=4.30.0" uv pip install "llama-index>=0.12.44" "llama-index-readers-file>=0.4.0" "llama-index-vector-stores-faiss>=0.4.0" uv pip install "llama-index-embeddings-huggingface>=0.5.5" "ipykernel==6.29.5" "msgpack>=1.1.1" "psutil>=5.8.0" # Install test dependencies uv pip install "pytest>=7.0" "pytest-timeout>=2.0" "llama-index-core>=0.12.0" "python-dotenv>=1.0.0" "sentence-transformers>=2.2.0" - name: Run tests with pytest env: CI: true # Mark as CI environment to skip memory-intensive tests OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} HF_HUB_DISABLE_SYMLINKS: 1 TOKENIZERS_PARALLELISM: false PYTORCH_ENABLE_MPS_FALLBACK: 0 # Disable MPS on macOS CI to avoid memory issues OMP_NUM_THREADS: 1 # Disable OpenMP parallelism to avoid libomp crashes MKL_NUM_THREADS: 1 # Single thread for MKL operations run: | # Activate virtual environment source .venv/bin/activate || source .venv/Scripts/activate # Run all tests pytest tests/ - name: Run sanity checks (optional) run: | # Activate virtual environment source .venv/bin/activate || source .venv/Scripts/activate # Run distance function tests if available if [ -f test/sanity_checks/test_distance_functions.py ]; then echo "Running distance function sanity checks..." python test/sanity_checks/test_distance_functions.py || echo "⚠️ Distance function test failed, continuing..." fi - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: packages-${{ matrix.os }}-py${{ matrix.python }} path: packages/*/dist/