Files
Catalogue_pos_nearle/run_project.py
2026-08-11 17:30:29 +05:30

218 lines
7.9 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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. Waits until backend port 8000 is live & ready to receive connections.
4. Starts Vite frontend dev server on http://localhost:5173.
5. Keeps both servers running concurrently with instant hot-reloading & single Ctrl+C exit.
"""
import os
import sys
import time
import subprocess
import signal
import shutil
import urllib.request
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")
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 not shutil.which("docker"):
print("ℹ️ Docker CLI not found. Skipping container management.")
return
# 1. Check if container is already running
try:
check_running = subprocess.run(
["docker", "ps", "--filter", "name=catalog_rag_postgres", "--format", "{{.Names}}"],
capture_output=True, text=True
)
if "catalog_rag_postgres" in check_running.stdout:
print("✅ PostgreSQL container 'catalog_rag_postgres' is running on port 5432 (pgvector ready).")
return
except Exception:
pass
# 2. Check if stopped container exists and start it
try:
check_all = subprocess.run(
["docker", "ps", "-a", "--filter", "name=catalog_rag_postgres", "--format", "{{.Names}}"],
capture_output=True, text=True
)
if "catalog_rag_postgres" in check_all.stdout:
print("🔄 Starting existing 'catalog_rag_postgres' container...")
start_res = subprocess.run(["docker", "start", "catalog_rag_postgres"], capture_output=True, text=True)
if start_res.returncode == 0:
print("✅ PostgreSQL container started on port 5432 (pgvector ready).")
return
except Exception:
pass
# 3. Fallback to Docker Compose
if compose_file.exists():
try:
res = subprocess.run(
["docker", "compose", "-f", str(compose_file), "up", "-d", "--remove-orphans"],
capture_output=True, text=True
)
if res.returncode == 0:
print("✅ PostgreSQL container is running on port 5432 (pgvector ready).")
else:
stderr_msg = res.stderr.strip()
if "Conflict" in stderr_msg or "already in use" in stderr_msg:
print("⚠️ Container name conflict detected. Automatically resetting container...")
subprocess.run(["docker", "rm", "-f", "catalog_rag_postgres"], capture_output=True, text=True)
retry_res = subprocess.run(
["docker", "compose", "-f", str(compose_file), "up", "-d"],
capture_output=True, text=True
)
if retry_res.returncode == 0:
print("✅ PostgreSQL container successfully recreated on port 5432 (pgvector ready).")
return
print("⚠️ Docker notice:", stderr_msg 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...")
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 wait_for_backend_ready():
print("⏳ Waiting for backend API to initialize on port 8000...")
for _ in range(30):
try:
req = urllib.request.urlopen("http://127.0.0.1:8000/api/system/status", timeout=1)
if req.getcode() == 200:
print("✅ Backend API is live and accepting connections on port 8000.")
return True
except Exception:
time.sleep(0.5)
return False
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. Wait until Backend is ready before starting Vite frontend
wait_for_backend_ready()
# 3. 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 processes (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()