#!/bin/bash # Pinned by full URL because no index serves it: PyPI's vllm has no macOS # wheel yet. To bump, raise VLLM_VERSION to a release that attaches one — # not every release does; v0.26.0 was the first. VLLM_VERSION="0.27.0" VLLM_WHEEL_URL="https://github.com/vllm-project/vllm/releases/download/v${VLLM_VERSION}/vllm-${VLLM_VERSION}%2Bcpu-cp312-cp312-macosx_11_0_arm64.whl" _cleanup_dirs=() register_cleanup_dir() { _cleanup_dirs+=("$1") } cleanup_tmp_dirs() { local dir if [[ ${#_cleanup_dirs[@]} -eq 0 ]]; then return fi for dir in "${_cleanup_dirs[@]}"; do rm -rf "$dir" done } fetch_latest_release() { local repo_owner="$1" local repo_name="$2" echo "Fetching latest release..." >&2 local latest_release_url="https://api.github.com/repos/${repo_owner}/${repo_name}/releases/latest" local release_data if ! release_data=$(curl -fsSL "$latest_release_url" 2>&1); then error "Failed to fetch release information." echo "Please check your internet connection and try again." >&2 exit 1 fi if [[ -z "$release_data" ]] || [[ "$release_data" == *"Not Found"* ]]; then error "No releases found for this repository." echo "Please visit https://github.com/${repo_owner}/${repo_name}/releases" >&2 exit 1 fi echo "$release_data" } extract_wheel_url() { local release_data="$1" python3 -c " import sys import json try: data = json.loads('''$release_data''', strict=False) assets = data.get('assets', []) for asset in assets: name = asset.get('name', '') if name.endswith('.whl'): print(asset.get('browser_download_url', '')) break except Exception as e: print('', file=sys.stderr) " } install_vllm() { echo "" section "Installing vLLM core" echo "Wheel: vLLM ${VLLM_VERSION} (prebuilt macOS arm64)" # The wheel's own metadata pulls torch et al. from PyPI. if ! uv pip install "$VLLM_WHEEL_URL"; then error "Failed to install vLLM core from ${VLLM_WHEEL_URL}" echo "Please check your internet connection and try again." >&2 exit 1 fi success "Installed vLLM core" } download_and_install_wheel() { local wheel_url="$1" local package_name="$2" local wheel_name wheel_name=$(basename "$wheel_url") echo "Latest release: $wheel_name" success "Found latest release" local tmp_dir tmp_dir=$(mktemp -d) register_cleanup_dir "$tmp_dir" echo "" echo "Downloading wheel..." local wheel_path="$tmp_dir/$wheel_name" if ! curl -fsSL "$wheel_url" -o "$wheel_path"; then error "Failed to download wheel." exit 1 fi success "Downloaded wheel" # Install vllm-metal package if ! uv pip install "$wheel_path"; then error "Failed to install ${package_name}." exit 1 fi success "Installed ${package_name}" } main() { set -eu -o pipefail trap cleanup_tmp_dirs EXIT local repo_owner="vllm-project" local repo_name="vllm-metal" local package_name="vllm-metal" for arg in "$@"; do case "$arg" in -h|--help) cat <<'EOF' Usage: scripts/p1-l2-install-vllm-metal.sh Options: -h, --help Show this help. EOF exit 0 ;; *) echo "Unknown argument: $arg" >&2 echo "Run with --help for usage." >&2 exit 1 ;; esac done # Source shared library functions # Try local lib.sh first (when running ./install.sh), fall back to remote (when piped from curl) local local_lib="" if [[ -n "${BASH_SOURCE[0]:-}" ]]; then local script_dir script_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-}")" && pwd)" local_lib="$script_dir/scripts/lib.sh" fi if [[ -n "$local_lib" && -f "$local_lib" ]]; then # shellcheck source=/dev/null source "$local_lib" else # Fetch from remote (curl | bash case) local lib_url="https://raw.githubusercontent.com/$repo_owner/$repo_name/main/scripts/lib.sh" local lib_tmp lib_tmp=$(mktemp) if ! curl -fsSL "$lib_url" -o "$lib_tmp"; then echo "Error: Failed to fetch lib.sh from $lib_url" >&2 rm -f "$lib_tmp" exit 1 fi # shellcheck source=/dev/null source "$lib_tmp" rm -f "$lib_tmp" fi if ! is_apple_silicon; then error "vllm-metal requires Apple Silicon arm64. Detected: $(uname -m)." exit 1 fi if ! ensure_uv; then exit 1 fi local venv="$HOME/.venv-vllm-metal" if [[ -n "$local_lib" && -f "$local_lib" ]]; then venv="$PWD/.venv-vllm-metal" fi ensure_venv "$venv" if ! require_arm64_python python; then exit 1 fi install_vllm if [[ -n "$local_lib" && -f "$local_lib" ]]; then # Local source install (running ./install.sh from a checkout). Prebuild the # native paged-attention artifacts from this tree — the _paged_ops .so and # the precompiled .metallib shaders — so the kernels load with no runtime # compile, exactly like a release wheel; otherwise get_ops() fails loud # ("Prebuilt native extension not found") the first time paged attention is # used. build_native_artifacts needs the build deps (mlx, nanobind) # importable, so the editable install pulls them in first and points the # install at this tree, where the artifacts land. The remote (curl | bash) # branch below installs a prebuilt release wheel instead and needs no # toolchain. Mirrors scripts/release.sh / scripts/test.sh. uv pip install -e . ensure_metal_toolchain build_native_artifacts else local release_data release_data=$(fetch_latest_release "$repo_owner" "$repo_name") local wheel_url wheel_url=$(extract_wheel_url "$release_data") if [[ -z "$wheel_url" ]]; then error "No wheel file found in the latest release." exit 1 fi download_and_install_wheel "$wheel_url" "$package_name" fi echo "" success "Installation complete!" echo "" echo "To use vllm, activate the virtual environment:" echo " source $venv/bin/activate" echo "" echo "Or add the venv to your PATH:" echo " export PATH=\"$venv/bin:\$PATH\"" } main "$@"