#!/usr/bin/env python3 """ Unified Single-Command Project Launcher for Brand Catalog Product LLM & RAG Intelligence Engine. Usage: python run_project.py What this script automates: 1. Checks & launches PostgreSQL via Docker Compose (`docker compose up -d`). 2. Starts FastAPI backend server on http://localhost:8000 (with background auto-seeding). 3. Starts Vite frontend dev server on http://localhost:5173 (or serves static dist). 4. Keeps both servers running concurrently with instant hot-reloading & single Ctrl+C exit. """ import os import sys import time import subprocess import signal from pathlib import Path if sys.platform == "win32": try: sys.stdout.reconfigure(encoding="utf-8", errors="replace") sys.stderr.reconfigure(encoding="utf-8", errors="replace") except Exception: pass ROOT_DIR = Path(__file__).resolve().parent BACKEND_DIR = ROOT_DIR / "backend" FRONTEND_DIR = ROOT_DIR / "frontend" VENV_PYTHON = BACKEND_DIR / "venv" / "Scripts" / "python.exe" if not VENV_PYTHON.exists(): VENV_PYTHON = Path(sys.executable) def print_banner(): print("\033[96m" + "=" * 70) print(" šŸš€ BRAND CATALOG PRODUCT LLM & RAG INTELLIGENCE ENGINE") print("=" * 70 + "\033[0m") import shutil def start_docker(): print("\nšŸ“¦ Checking PostgreSQL Docker container...") compose_file = ROOT_DIR / "docker-compose.yml" if not compose_file.exists(): compose_file = BACKEND_DIR / "docker-compose.yml" if compose_file.exists(): try: res = subprocess.run(["docker", "compose", "-f", str(compose_file), "up", "-d"], capture_output=True, text=True) if res.returncode == 0: print("āœ… PostgreSQL container is running on port 5432 (pgvector ready).") else: print("āš ļø Docker notice:", res.stderr.strip() or "Ensure Docker Desktop is running if using Docker Postgres.") except Exception as e: print(f"āš ļø Docker check skipped: {e}") def start_ollama(): print("\nšŸ¦™ Checking Ollama LLM service...") import urllib.request try: req = urllib.request.urlopen("http://localhost:11434/api/tags", timeout=2) if req.getcode() == 200: print("āœ… Ollama LLM server is running on http://localhost:11434.") return None except Exception: pass if not shutil.which("ollama"): print("ā„¹ļø Ollama CLI not installed/found. Backend will use catalog-grounded fallback responses.") return None try: p = subprocess.Popen(["ollama", "serve"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) print("šŸš€ Automatically launched Ollama LLM server background process.") return p except Exception as e: print(f"āš ļø Ollama auto-launch note: {e} (App will use catalog grounded fallback).") return None def main(): print_banner() start_docker() p_ollama = start_ollama() processes = [] if p_ollama: processes.append(p_ollama) try: # 1. Start Backend FastAPI Uvicorn print("\n⚔ Starting Backend FastAPI Server (http://localhost:8000)...") backend_cmd = [ str(VENV_PYTHON), "-m", "uvicorn", "app.main:app", "--reload", "--host", "0.0.0.0", "--port", "8000" ] env_backend = os.environ.copy() env_backend["PYTHONPATH"] = str(BACKEND_DIR) env_backend["PYTHONIOENCODING"] = "utf-8" p_backend = subprocess.Popen( backend_cmd, cwd=str(BACKEND_DIR), env=env_backend ) processes.append(p_backend) # 2. Start Frontend Vite Dev Server print("šŸŽØ Starting Frontend Vite Server (http://localhost:5173)...") npm_cmd = "npm.cmd" if sys.platform == "win32" else "npm" p_frontend = subprocess.Popen( [npm_cmd, "run", "dev"], cwd=str(FRONTEND_DIR), shell=(sys.platform == "win32") ) processes.append(p_frontend) print("\n\033[92m" + "=" * 70) print(" šŸŽ‰ APPLICATION IS LIVE AND READY!") print("=" * 70 + "\033[0m") print(" šŸ‘‰ Web App UI: \033[94mhttp://localhost:5173\033[0m") print(" šŸ‘‰ Backend API: \033[94mhttp://localhost:8000\033[0m") print(" šŸ‘‰ API Docs: \033[94mhttp://localhost:8000/docs\033[0m") print(" šŸ‘‰ System Status: \033[94mhttp://localhost:8000/api/system/status\033[0m") print("=" * 70) print(" (Press Ctrl+C to stop all servers gracefully)\n") # Monitor core servers (backend & frontend) core_processes = [p_backend, p_frontend] while True: time.sleep(1) for p in core_processes: if p.poll() is not None: print(f"āš ļø Core process PID {p.pid} exited with code {p.returncode}") return except KeyboardInterrupt: print("\nšŸ›‘ Stopping all services...") finally: for p in processes: try: p.terminate() p.wait(timeout=3) except Exception: try: p.kill() except Exception: pass print("šŸ‘‹ Goodbye! All processes terminated.") if __name__ == "__main__": main()