# ECE Compliance RAG — Developer Workflow # # Usage: # make check Ollama running? Model loaded? Corpus & indexes OK? # make corpus Build all domain compilations from sources # make index Build all PageIndex trees (LLM-heavy) # make test Staged pipeline validation # make app Launch Streamlit UI # make help Show all targets .PHONY: help check corpus corpus-legislation corpus-licensing corpus-reform \ index index-legislation index-licensing index-ero index-cross-regulator index-reform \ test benchmark benchmark-quick app app-qwen app-gemma mlx-stop download rebuild SHELL := /bin/bash export PYTHONUNBUFFERED := 1 UV := /opt/homebrew/bin/uv run python # ── Help ──────────────────────────────────────────────────────────────────── help: @echo "" @echo "ECE Compliance RAG — Developer Workflow" @echo "========================================" @echo "" @echo "Health checks (fast, no LLM):" @echo " make check Corpus, indexes, mlx-lm server" @echo " make check-ollama Just Ollama connectivity (legacy)" @echo "" @echo "Source acquisition (network, no LLM):" @echo " make download Fetch HTML/PDF sources" @echo "" @echo "Corpus building (local, no LLM):" @echo " make corpus Build all 5 domain compilations" @echo " make corpus-legislation Just rebuild legislation.md from HTML" @echo " make corpus-licensing Just rebuild licensing-criteria.md" @echo " make corpus-reform Just rebuild reform-context.md" @echo "" @echo "Index building (LLM-heavy — slow):" @echo " make index Build all 5 PageIndex trees (auto-runs propagate-urls)" @echo " make index-legislation Just rebuild legislation index" @echo " make index-licensing Just rebuild licensing-criteria index" @echo " make index-ero Just rebuild ero index" @echo " make index-cross-regulator" @echo " make index-reform Just rebuild reform-context index" @echo " make propagate-urls Propagate Source: URLs down index trees (no LLM)" @echo "" @echo "Testing:" @echo " make test Staged pipeline test (1 LLM call)" @echo " make benchmark Full 20-question benchmark (~30 min)" @echo " make benchmark-quick 3 questions (~5 min)" @echo "" @echo "App:" @echo " make app Launch Streamlit UI (default: Qwen 2.5 14B)" @echo " make app-qwen Launch with Qwen 2.5 14B (~9 GB, local)" @echo " make app-gemma Launch with Gemma 4 26B MoE (~17 GB, local)" @echo " make app-sonnet Launch with Claude Sonnet (API, fast)" @echo " make mlx-stop Stop the mlx-lm server" @echo "" @echo "Full rebuild:" @echo " make rebuild corpus → index → test (end to end)" @echo "" # ── Health checks ─────────────────────────────────────────────────────────── check: @$(UV) scripts/check.py check-ollama: @$(UV) scripts/check.py --ollama # ── Source acquisition ────────────────────────────────────────────────────── download: @echo "Downloading legislation HTML sources..." @$(UV) scripts/convert_legislation_html.py @echo "" @echo "Converting PDF sources to Markdown..." @$(UV) scripts/convert_sources.py # ── Corpus building ──────────────────────────────────────────────────────── corpus: corpus-legislation corpus-licensing corpus-reform corpus-ero @echo "" @echo "All corpus files built." corpus-legislation: @echo "Building legislation corpus from HTML..." @$(UV) scripts/convert_legislation_html.py corpus-licensing: @echo "Building licensing-criteria corpus from education.govt.nz..." @$(UV) scripts/build_licensing_html.py corpus-reform: @echo "Building reform-context corpus..." @$(UV) scripts/build_reform_compilation.py corpus-ero: @echo "Building ERO corpus from HTML sources..." @$(UV) scripts/build_ero_compilation.py # ── Index building ────────────────────────────────────────────────────────── dry-run: @$(UV) scripts/build_indexes.py --dry-run index: @$(UV) scripts/build_indexes.py @$(MAKE) --no-print-directory propagate-urls index-legislation: @$(UV) scripts/build_indexes.py legislation @$(UV) scripts/propagate_source_urls.py --apply legislation index-licensing: @$(UV) scripts/build_indexes.py licensing-criteria @$(UV) scripts/propagate_source_urls.py --apply licensing-criteria index-ero: @$(UV) scripts/build_indexes.py ero @$(UV) scripts/propagate_source_urls.py --apply ero index-cross-regulator: @$(UV) scripts/build_indexes.py cross-regulator @$(UV) scripts/propagate_source_urls.py --apply cross-regulator index-reform: @$(UV) scripts/build_indexes.py reform-context @$(UV) scripts/propagate_source_urls.py --apply reform-context # Propagate Source: URLs down every index tree so deep nodes inherit # the nearest ancestor's URL. Zero API cost. Run automatically after # `make index` but can be re-run standalone on existing indexes. propagate-urls: @$(UV) scripts/propagate_source_urls.py --apply # ── Testing ───────────────────────────────────────────────────────────────── test: @$(UV) test_pipeline.py benchmark: @$(UV) benchmark/run_benchmark.py benchmark-quick: @$(UV) benchmark/run_benchmark.py --ids 1,4,5 benchmark-compare: @$(UV) benchmark/run_benchmark.py --compare qwen,sonnet # ── App ───────────────────────────────────────────────────────────────────── # Model presets — override with: make app MLX_MODEL=your-model-here MODEL_QWEN := mlx-community/Qwen2.5-14B-Instruct-4bit MODEL_GEMMA := mlx-community/gemma-4-26b-a4b-it-4bit MLX_MODEL ?= $(MODEL_QWEN) MLX_PORT := 8080 # Stop any running mlx-lm server (useful when swapping models) mlx-stop: @echo "Stopping mlx-lm server..." @pkill -f "mlx_lm.*server" 2>/dev/null && echo "Server stopped." || echo "No server running." # Start mlx-lm server for a given model, restarting if a different model is running define start-mlx-server @RUNNING_MODEL=$$(curl -s --max-time 2 http://localhost:$(MLX_PORT)/v1/models 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['data'][0]['id'])" 2>/dev/null || echo ""); \ if [ "$$RUNNING_MODEL" = "$(1)" ]; then \ echo "mlx-lm server already running $(1)"; \ else \ if [ -n "$$RUNNING_MODEL" ]; then \ echo "Stopping server (was running $$RUNNING_MODEL)..."; \ pkill -f "mlx_lm.*server" 2>/dev/null; sleep 2; \ fi; \ echo "Starting mlx-lm server ($(1))..."; \ ECE_MLX_MODEL=$(1) /opt/homebrew/bin/uv run python -m mlx_lm server --model $(1) --port $(MLX_PORT) > /dev/null 2>&1 & \ echo "Waiting for server..."; sleep 10; \ echo "Server ready."; \ fi endef app: ## Launch with current MLX_MODEL (default: Qwen) $(call start-mlx-server,$(MLX_MODEL)) @echo "Launching Streamlit UI ($(MLX_MODEL))..." @ECE_MLX_MODEL=$(MLX_MODEL) /opt/homebrew/bin/uv run streamlit run app.py --server.headless true app-qwen: ## Launch with Qwen 2.5 14B $(call start-mlx-server,$(MODEL_QWEN)) @echo "Launching Streamlit UI (Qwen 2.5 14B)..." @ECE_MLX_MODEL=$(MODEL_QWEN) /opt/homebrew/bin/uv run streamlit run app.py --server.headless true app-gemma: ## Launch with Gemma 4 26B MoE $(call start-mlx-server,$(MODEL_GEMMA)) @echo "Launching Streamlit UI (Gemma 4 26B MoE)..." @ECE_MLX_MODEL=$(MODEL_GEMMA) /opt/homebrew/bin/uv run streamlit run app.py --server.headless true app-sonnet: ## Launch with Claude Sonnet (API — no local server needed) @echo "Launching Streamlit UI (Claude Sonnet — API)..." @ECE_MODEL=sonnet /opt/homebrew/bin/uv run streamlit run app.py --server.headless true # ── Full rebuild ──────────────────────────────────────────────────────────── rebuild: corpus index test @echo "" @echo "Full rebuild complete."