diff --git a/Makefile b/Makefile index 3f15533..13ea366 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ -.PHONY: help build up down logs shell exec pull-model test clean fireform logs-app logs-ollama logs-frontend super-clean +.PHONY: help init fireform build up down logs logs-app logs-ollama shell pull-model test clean super-clean -# The extraction model pulled into Ollama and used by src/llm.py. Override with -# `make pull-model OLLAMA_MODEL=...`. A 1.5B model keeps per-field fills fast. -OLLAMA_MODEL ?= qwen2.5:1.5b +COMPOSE = docker compose -f docker/dev/compose.yml --env-file docker/.env.dev +ENV_DEV = docker/.env.dev + +# Read OLLAMA_MODEL from .env.dev at runtime; fall back to default if file absent. +OLLAMA_MODEL = $(shell grep -E '^OLLAMA_MODEL=' $(ENV_DEV) 2>/dev/null | cut -d= -f2 | tr -d '[:space:]' || echo qwen2.5:1.5b) help: @printf '%s\n' \ @@ -13,72 +15,84 @@ help: '/_/ /_//_/ \___/ /_/ \____/_/ /_/ /_/ /_/ ' \ '' @echo "" - @echo "Fireform Development Commands" + @echo "FireForm Development Commands" @echo "==============================" - @echo "make fireform - Build and start containers, then open a shell" + @echo "make init - First-time setup: check deps, create .env.dev, pick model" + @echo "make fireform - Build images, start containers, pull Ollama model" @echo "make build - Build Docker images" - @echo "make up - Start all containers" + @echo "make up - Start all containers (detached)" @echo "make down - Stop all containers" - @echo "make logs - View container logs" - @echo "make logs-app - View API container logs" - @echo "make logs-frontend - View frontend container logs" - @echo "make logs-ollama - View Ollama container logs" - @echo "make shell - Open Python shell in app container" - @echo "make exec - Execute Python script in container" - @echo "make pull-model - Pull the extraction model ($(OLLAMA_MODEL)) into Ollama" - @echo "make test - Run tests" - @echo "make clean - Remove containers" - @echo "make super-clean - [CAUTION] Use carefully. Cleans up ALL stopped containers, networks, build cache..." - -# Fix #382 — pull-model is now part of the main setup flow -# The extraction model is pulled automatically before you need it -fireform: build up pull-model + @echo "make logs - Stream all container logs" + @echo "make logs-app - Stream app container logs" + @echo "make logs-ollama - Stream Ollama container logs" + @echo "make shell - Open shell in running app container" + @echo "make pull-model - Pull Ollama model from .env.dev ($(OLLAMA_MODEL))" + @echo "make test - Run test suite" + @echo "make clean - Stop containers (preserves volumes)" + @echo "make super-clean - [CAUTION] Stop containers, delete volumes, prune Docker" + +init: + @chmod +x scripts/check-deps.sh scripts/init-env.sh scripts/select-model.sh + @sh scripts/check-deps.sh + @sh scripts/init-env.sh + @sh scripts/select-model.sh + @printf "Build containers and pull model now? [y/N] "; \ + read answer; \ + case "$$answer" in \ + [yY]*) $(MAKE) fireform ;; \ + *) echo "Run 'make fireform' when ready." ;; \ + esac + +fireform: build up + @printf "Waiting for Ollama to be ready..." + @until $(COMPOSE) exec -T ollama ollama list > /dev/null 2>&1; do \ + printf '.'; sleep 2; \ + done + @echo " ready." + @if $(COMPOSE) exec -T ollama ollama list 2>/dev/null | grep -q "^$(OLLAMA_MODEL)"; then \ + echo " Model $(OLLAMA_MODEL) already pulled."; \ + else \ + echo " Pulling $(OLLAMA_MODEL)..."; \ + $(COMPOSE) exec -T ollama ollama pull $(OLLAMA_MODEL); \ + fi @echo "" - @echo "✅ FireForm is ready!" - @echo " Frontend: http://localhost:5173" + @echo "FireForm is ready!" @echo " API: http://localhost:8000" @echo " API Docs: http://localhost:8000/docs" @echo "" @echo "Run 'make logs' to view live logs, 'make down' to stop." build: - docker compose build + @$(COMPOSE) build --progress=quiet up: - docker compose up -d + @$(COMPOSE) up -d down: - docker compose down + @$(COMPOSE) down --remove-orphans logs: - docker compose logs -f + @$(COMPOSE) logs -f logs-app: - docker compose logs -f app + @$(COMPOSE) logs -f app logs-ollama: - docker compose logs -f ollama - -logs-frontend: - docker compose logs -f frontend + @$(COMPOSE) logs -f ollama shell: - docker compose exec app /bin/bash - -# Start the FastAPI server inside the running container -run: - docker compose exec app uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload - + @$(COMPOSE) exec app /bin/sh pull-model: - docker compose exec ollama ollama pull $(OLLAMA_MODEL) + @$(COMPOSE) exec -T ollama ollama pull $(OLLAMA_MODEL) -# Fix — correct test directory (was src/test/ which doesn't exist) test: - docker compose exec app python3 -m pytest tests/ -v + @$(COMPOSE) exec -T app python3 -m pytest tests/ -v clean: - docker compose down -v + @$(COMPOSE) down + super-clean: - docker compose down -v - docker system prune + @echo "WARNING: this will delete all volumes (database, uploads, model weights)." + @$(COMPOSE) down -v + @docker system prune -f diff --git a/scripts/check-deps.sh b/scripts/check-deps.sh new file mode 100755 index 0000000..3c4484e --- /dev/null +++ b/scripts/check-deps.sh @@ -0,0 +1,51 @@ +#!/bin/sh +set -e + +PASS=0 +FAIL=1 +errors=0 + +check() { + label="$1" + shift + if "$@" > /dev/null 2>&1; then + echo " [ok] $label" + else + echo " [!!] $label" + errors=$((errors + 1)) + fi +} + +echo "" +echo "Checking dependencies..." +echo "========================" + +# Docker daemon running +check "Docker daemon is running" docker info + +# docker compose v2 (plugin form, not legacy docker-compose) +check "docker compose v2 available" docker compose version + +# Minimum Docker version: 24 (BuildKit cache mounts stable) +DOCKER_VERSION=$(docker version --format '{{.Server.Version}}' 2>/dev/null | cut -d. -f1) +if [ -n "$DOCKER_VERSION" ] && [ "$DOCKER_VERSION" -ge 24 ] 2>/dev/null; then + echo " [ok] Docker version >= 24 (found $(docker version --format '{{.Server.Version}}' 2>/dev/null))" +else + echo " [!!] Docker version >= 24 required (found $(docker version --format '{{.Server.Version}}' 2>/dev/null || echo 'unknown'))" + echo " BuildKit cache mounts in docker/dev/Dockerfile require Docker 24+." + errors=$((errors + 1)) +fi + +echo "" + +if [ "$errors" -gt 0 ]; then + echo "$errors check(s) failed. Fix the above before continuing." + echo "" + echo " Install Docker: https://docs.docker.com/get-docker/" + echo " Upgrade Docker Desktop: https://docs.docker.com/desktop/release-notes/" + echo "" + exit 1 +fi + +echo "All checks passed." +echo "" diff --git a/scripts/init-env.sh b/scripts/init-env.sh new file mode 100755 index 0000000..c2c0804 --- /dev/null +++ b/scripts/init-env.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -e + +ENV_EXAMPLE="docker/.env.example" +ENV_DEV="docker/.env.dev" + +echo "" +echo "Setting up environment..." +echo "=========================" + +if [ ! -f "$ENV_EXAMPLE" ]; then + echo "Error: $ENV_EXAMPLE not found. Are you running from the repo root?" + exit 1 +fi + +if [ -f "$ENV_DEV" ]; then + printf " %s already exists. Overwrite? [y/N] " "$ENV_DEV" + read -r answer + case "$answer" in + [yY]*) + cp "$ENV_EXAMPLE" "$ENV_DEV" + echo " Overwritten." + ;; + *) + echo " Kept existing $ENV_DEV." + ;; + esac +else + cp "$ENV_EXAMPLE" "$ENV_DEV" + echo " Created $ENV_DEV from $ENV_EXAMPLE." +fi + +echo "" +echo " Review docker/.env.dev and adjust values if needed before running 'make fireform'." +echo "" diff --git a/scripts/select-model.sh b/scripts/select-model.sh new file mode 100755 index 0000000..4b3c9d3 --- /dev/null +++ b/scripts/select-model.sh @@ -0,0 +1,107 @@ +#!/bin/sh +set -e + +ENV_DEV="docker/.env.dev" +COMPOSE="docker compose -f docker/dev/compose.yml --env-file $ENV_DEV" + +# model name | approx size +MODELS="qwen2.5:1.5b|~1GB qwen2.5:3b|~2GB qwen2.5:7b|~4GB llama3.2:3b|~2GB mistral:7b|~4GB" + +current_model="" +if [ -f "$ENV_DEV" ]; then + current_model=$(grep -E '^OLLAMA_MODEL=' "$ENV_DEV" | cut -d= -f2 | tr -d '[:space:]') +fi + +echo "" +echo "Select Ollama model" +echo "===================" +[ -n "$current_model" ] && echo " Current: $current_model" +echo "" + +i=1 +for entry in $MODELS; do + name=$(echo "$entry" | cut -d'|' -f1) + size=$(echo "$entry" | cut -d'|' -f2) + if [ "$name" = "${current_model}" ]; then + echo " $i) $name $size [current]" + else + echo " $i) $name $size" + fi + i=$((i + 1)) +done +echo " $i) Enter custom model name" +i=$((i + 1)) +echo " $i) Keep current" +echo "" +printf "Choice [1-$i]: " +read -r choice + +total=$(echo "$MODELS" | wc -w | tr -d '[:space:]') +keep_choice=$((total + 2)) +custom_choice=$((total + 1)) + +if [ "$choice" = "$keep_choice" ] || [ -z "$choice" ]; then + echo " Keeping current model: ${current_model:-qwen2.5:1.5b}" + echo "" + exit 0 +fi + +if [ "$choice" = "$custom_choice" ]; then + printf " Enter model name (e.g. phi3:mini): " + read -r selected + if [ -z "$selected" ]; then + echo " No model entered. Keeping current." + echo "" + exit 0 + fi + selected_size="unknown size" +else + # Validate numeric choice in range + if ! echo "$choice" | grep -qE '^[0-9]+$' || [ "$choice" -lt 1 ] || [ "$choice" -gt "$total" ]; then + echo " Invalid choice. Keeping current model." + echo "" + exit 0 + fi + i=1 + for entry in $MODELS; do + if [ "$i" = "$choice" ]; then + selected=$(echo "$entry" | cut -d'|' -f1) + selected_size=$(echo "$entry" | cut -d'|' -f2) + fi + i=$((i + 1)) + done +fi + +# Patch OLLAMA_MODEL in .env.dev +tmp=$(mktemp) +sed "s|^OLLAMA_MODEL=.*|OLLAMA_MODEL=$selected|" "$ENV_DEV" > "$tmp" +mv "$tmp" "$ENV_DEV" +echo "" +echo " OLLAMA_MODEL set to: $selected" + +# If container is running, check and optionally pull immediately. +# If not running, the caller (make init) handles the pull prompt. +if $COMPOSE ps ollama 2>/dev/null | grep -q "running"; then + if $COMPOSE exec -T ollama ollama list 2>/dev/null | grep -q "^$selected"; then + echo " Model already pulled. Nothing to download." + echo "" + exit 0 + fi + + echo "" + printf " Model not yet downloaded ($selected_size). Pull now? [y/N] " + read -r pull_answer + case "$pull_answer" in + [yY]*) + echo "" + $COMPOSE exec -T ollama ollama pull "$selected" + echo "" + echo " Model ready." + ;; + *) + echo " Skipped. Run 'make pull-model' when ready." + ;; + esac +fi + +echo ""