diff --git a/.github/workflows/build-reusable.yml b/.github/workflows/build-reusable.yml index 2833a17..db01cfc 100644 --- a/.github/workflows/build-reusable.yml +++ b/.github/workflows/build-reusable.yml @@ -108,7 +108,6 @@ jobs: if: runner.os == 'macOS' run: | # Don't install LLVM, use system clang for better compatibility - # abseil is automatically installed as a dependency of protobuf brew install libomp boost protobuf zeromq - name: Install build dependencies @@ -123,20 +122,17 @@ jobs: - name: Set macOS environment variables if: runner.os == 'macOS' run: | - # Detect Homebrew installation path and set environment variables - if [ -d "/opt/homebrew/opt/libomp" ]; then - echo "HOMEBREW_PREFIX=/opt/homebrew" >> $GITHUB_ENV - echo "OpenMP_ROOT=/opt/homebrew/opt/libomp" >> $GITHUB_ENV - echo "CMAKE_PREFIX_PATH=/opt/homebrew/opt/libomp:/opt/homebrew/opt/boost:/opt/homebrew/opt/protobuf:/opt/homebrew/opt/zeromq:/opt/homebrew/opt/abseil" >> $GITHUB_ENV - echo "LDFLAGS=-L/opt/homebrew/opt/libomp/lib" >> $GITHUB_ENV - echo "CPPFLAGS=-I/opt/homebrew/opt/libomp/include -I/opt/homebrew/opt/abseil/include" >> $GITHUB_ENV - elif [ -d "/usr/local/opt/libomp" ]; then - echo "HOMEBREW_PREFIX=/usr/local" >> $GITHUB_ENV - echo "OpenMP_ROOT=/usr/local/opt/libomp" >> $GITHUB_ENV - echo "CMAKE_PREFIX_PATH=/usr/local/opt/libomp:/usr/local/opt/boost:/usr/local/opt/protobuf:/usr/local/opt/zeromq:/usr/local/opt/abseil" >> $GITHUB_ENV - echo "LDFLAGS=-L/usr/local/opt/libomp/lib" >> $GITHUB_ENV - echo "CPPFLAGS=-I/usr/local/opt/libomp/include -I/usr/local/opt/abseil/include" >> $GITHUB_ENV - fi + # 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: | @@ -150,12 +146,10 @@ jobs: # Build HNSW backend cd packages/leann-backend-hnsw if [[ "${{ matrix.os }}" == macos-* ]]; then - # Use system clang instead of homebrew LLVM for better compatibility + # Use system clang for better compatibility export CC=clang export CXX=clang++ export MACOSX_DEPLOYMENT_TARGET=11.0 - # Ensure CMake can find all Homebrew packages including abseil for protobuf - export CMAKE_ARGS="-DOpenMP_ROOT=${OpenMP_ROOT} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -Dabsl_DIR=${HOMEBREW_PREFIX}/opt/abseil/lib/cmake/absl" uv build --wheel --python python else uv build --wheel --python python @@ -165,13 +159,11 @@ jobs: # Build DiskANN backend cd packages/leann-backend-diskann if [[ "${{ matrix.os }}" == macos-* ]]; then - # Use system clang instead of homebrew LLVM for better compatibility + # 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 - # Ensure CMake can find all Homebrew packages including abseil for protobuf - export CMAKE_ARGS="-DOpenMP_ROOT=${OpenMP_ROOT} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -Dabsl_DIR=${HOMEBREW_PREFIX}/opt/abseil/lib/cmake/absl" uv build --wheel --python python else uv build --wheel --python python diff --git a/packages/leann-backend-hnsw/leann_backend_hnsw/hnsw_embedding_server.py b/packages/leann-backend-hnsw/leann_backend_hnsw/hnsw_embedding_server.py index 331477f..9275ddc 100644 --- a/packages/leann-backend-hnsw/leann_backend_hnsw/hnsw_embedding_server.py +++ b/packages/leann-backend-hnsw/leann_backend_hnsw/hnsw_embedding_server.py @@ -10,6 +10,7 @@ import sys import threading import time from pathlib import Path +from typing import Union import msgpack import numpy as np @@ -33,7 +34,7 @@ if not logger.handlers: def create_hnsw_embedding_server( - passages_file: str | None = None, + passages_file: Union[str, None] = None, zmq_port: int = 5555, model_name: str = "sentence-transformers/all-mpnet-base-v2", distance_metric: str = "mips", diff --git a/packages/leann-core/src/leann/chat.py b/packages/leann-core/src/leann/chat.py index 2d69bec..4fbf5eb 100644 --- a/packages/leann-core/src/leann/chat.py +++ b/packages/leann-core/src/leann/chat.py @@ -8,7 +8,7 @@ import difflib import logging import os from abc import ABC, abstractmethod -from typing import Any +from typing import Any, Union import torch @@ -309,7 +309,7 @@ def search_hf_models(query: str, limit: int = 10) -> list[str]: return search_hf_models_fuzzy(query, limit) -def validate_model_and_suggest(model_name: str, llm_type: str) -> str | None: +def validate_model_and_suggest(model_name: str, llm_type: str) -> Union[str, None]: """Validate model name and provide suggestions if invalid""" if llm_type == "ollama": available_models = check_ollama_models() @@ -683,7 +683,7 @@ class HFChat(LLMInterface): class OpenAIChat(LLMInterface): """LLM interface for OpenAI models.""" - def __init__(self, model: str = "gpt-4o", api_key: str | None = None): + def __init__(self, model: str = "gpt-4o", api_key: Union[str, None] = None): self.model = model self.api_key = api_key or os.getenv("OPENAI_API_KEY") diff --git a/packages/leann-core/src/leann/cli.py b/packages/leann-core/src/leann/cli.py index 787cadd..5c57f83 100644 --- a/packages/leann-core/src/leann/cli.py +++ b/packages/leann-core/src/leann/cli.py @@ -1,6 +1,7 @@ import argparse import asyncio from pathlib import Path +from typing import Union from llama_index.core import SimpleDirectoryReader from llama_index.core.node_parser import SentenceSplitter @@ -270,7 +271,7 @@ Examples: print(f' leann search {example_name} "your query"') print(f" leann ask {example_name} --interactive") - def load_documents(self, docs_dir: str, custom_file_types: str | None = None): + def load_documents(self, docs_dir: str, custom_file_types: Union[str, None] = None): print(f"Loading documents from {docs_dir}...") if custom_file_types: print(f"Using custom file types: {custom_file_types}") diff --git a/packages/leann-core/src/leann/interface.py b/packages/leann-core/src/leann/interface.py index d63078b..b98c28d 100644 --- a/packages/leann-core/src/leann/interface.py +++ b/packages/leann-core/src/leann/interface.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Literal +from typing import Any, Literal, Union import numpy as np @@ -34,7 +34,9 @@ class LeannBackendSearcherInterface(ABC): pass @abstractmethod - def _ensure_server_running(self, passages_source_file: str, port: int | None, **kwargs) -> int: + def _ensure_server_running( + self, passages_source_file: str, port: Union[int, None], **kwargs + ) -> int: """Ensure server is running""" pass @@ -48,7 +50,7 @@ class LeannBackendSearcherInterface(ABC): prune_ratio: float = 0.0, recompute_embeddings: bool = False, pruning_strategy: Literal["global", "local", "proportional"] = "global", - zmq_port: int | None = None, + zmq_port: Union[int, None] = None, **kwargs, ) -> dict[str, Any]: """Search for nearest neighbors @@ -74,7 +76,7 @@ class LeannBackendSearcherInterface(ABC): self, query: str, use_server_if_available: bool = True, - zmq_port: int | None = None, + zmq_port: Union[int, None] = None, ) -> np.ndarray: """Compute embedding for a query string diff --git a/pyproject.toml b/pyproject.toml index d3b42e3..62b52f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ leann-backend-diskann = { path = "packages/leann-backend-diskann", editable = tr leann-backend-hnsw = { path = "packages/leann-backend-hnsw", editable = true } [tool.ruff] -target-version = "py310" +target-version = "py39" line-length = 100 extend-exclude = [ "third_party",