#!/usr/bin/env bash # LLM API — install on a clean Apple Silicon Mac. # Usage: ./scripts/install.sh [--root ~/llm-api] [--no-llama] [--no-web] [--test] set -euo pipefail ROOT="${LLM_API_ROOT:-$HOME/llm-api}" WITH_LLAMA=1 WITH_WEB=1 RUN_TEST=0 for a in "$@"; do case "$a" in --root) shift; ROOT="$1";; --root=*) ROOT="${a#--root=}";; --no-llama) WITH_LLAMA=0;; --no-web) WITH_WEB=0;; --test) RUN_TEST=1;; esac done HERE="$(cd "$(dirname "$0")/.." && pwd)" say() { printf "\033[1;34m==>\033[0m %s\n" "$*"; } warn() { printf "\033[1;33m!!\033[0m %s\n" "$*"; } # 1. platform checks ------------------------------------------------------- [ "$(uname -s)" = "Darwin" ] || { echo "macOS required"; exit 1; } [ "$(uname -m)" = "arm64" ] || { echo "Apple Silicon (arm64) required"; exit 1; } CHIP=$(sysctl -n machdep.cpu.brand_string) MEM_GB=$(( $(sysctl -n hw.memsize) / 1024 / 1024 / 1024 )) FREE_GB=$(df -g "$HOME" | awk 'NR==2 {print $4}') say "Detected $CHIP, ${MEM_GB} GB unified memory, ${FREE_GB} GB free on $HOME" [ "$MEM_GB" -ge 16 ] || warn "Less than 16 GB of memory: only small models will run." [ "$FREE_GB" -ge 120 ] || warn "Less than 120 GB free: downloads stop at the MIN_FREE_DISK_GB reserve." export PATH="/opt/homebrew/bin:$HOME/.local/bin:$PATH" # 2. toolchain --------------------------------------------------------------- xcode-select -p >/dev/null 2>&1 || { say "Installing Xcode Command Line Tools (a dialog may appear)"; xcode-select --install || true; } command -v brew >/dev/null || { say "Installing Homebrew"; /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; eval "$(/opt/homebrew/bin/brew shellenv)"; } command -v uv >/dev/null || { say "Installing uv"; brew install uv; } if [ "$WITH_WEB" = 1 ]; then command -v node >/dev/null || { say "Installing Node"; brew install node; } command -v pnpm >/dev/null || { say "Installing pnpm"; brew install pnpm; } fi if [ "$WITH_LLAMA" = 1 ]; then command -v llama-server >/dev/null || { say "Installing llama.cpp (Metal)"; brew install llama.cpp; } fi # 3. directories --------------------------------------------------------------- say "Creating $ROOT" mkdir -p "$ROOT"/{models/{mlx,gguf,embeddings,rerankers,vision,manifests},data,logs} chmod 700 "$ROOT/data" # 4. python env ----------------------------------------------------------------- cd "$HERE/server" if [ ! -x .venv/bin/python ]; then say "Creating Python 3.13 virtualenv" uv venv --python 3.13 .venv fi say "Installing Python dependencies (MLX, MLX-LM, mlx-vlm, FastAPI…)" uv pip install --python .venv/bin/python -q -e ".[mlx,test]" "mlx-vlm>=0.7" asgi-lifespan .venv/bin/python - <<'PY' import mlx.core as mx, mlx_lm print(f" mlx {mx.__version__} · mlx-lm {mlx_lm.__version__} · Metal device: {mx.default_device()}") PY # 5. web ------------------------------------------------------------------------ if [ "$WITH_WEB" = 1 ]; then say "Installing and building the dashboard" (cd "$HERE/web" && pnpm install --frozen-lockfile --silent && NEXT_TELEMETRY_DISABLED=1 pnpm build | tail -3) fi # 6. env file (never overwrite) ---------------------------------------------------- if [ ! -f "$ROOT/.env" ]; then say "Writing $ROOT/.env from .env.example (edit ADMIN_EMAIL / ADMIN_PASSWORD / HF_TOKEN)" sed "s#/Users/USERNAME/llm-api#$ROOT#" "$HERE/.env.example" > "$ROOT/.env" chmod 600 "$ROOT/.env" else say "$ROOT/.env already exists — left untouched" fi # 7. database + scan ------------------------------------------------------------------ say "Initializing database and scanning $ROOT/models" (cd "$ROOT" && "$HERE/server/.venv/bin/llm-api" scan | tail -5) # 8. optional local inference test ------------------------------------------------------- if [ "$RUN_TEST" = 1 ]; then say "Downloading a tiny test model and running one generation" "$HERE/server/.venv/bin/hf" download mlx-community/Qwen3-0.6B-4bit --local-dir "$ROOT/models/mlx/qwen/Qwen3-0.6B-4bit" >/dev/null "$HERE/server/.venv/bin/python" -m mlx_lm generate --model "$ROOT/models/mlx/qwen/Qwen3-0.6B-4bit" --prompt "Say hello in one word." --max-tokens 8 | tail -3 fi cat <