updates frontend and backend
This commit is contained in:
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,12 +0,0 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.pytest_cache/
|
||||
*.log
|
||||
backend.log
|
||||
backend.err.log
|
||||
.env
|
||||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
79
README.md
79
README.md
@@ -1,79 +0,0 @@
|
||||
# RAG-Powered Brand Product Search Engine
|
||||
|
||||
This is the v2.0 upgrade of the Indian FMCG product catalog project: the
|
||||
same discovery/enrichment pipeline (Ollama + pgvector + S3 image
|
||||
storage), now with a **Retrieval-Augmented Generation (RAG) layer** and a
|
||||
**React UI** in place of the old Streamlit app.
|
||||
|
||||
Two independent pieces:
|
||||
|
||||
- **`backend/`** - FastAPI service. Browse/search/chat endpoints over a
|
||||
pgvector-backed catalog; a RAG chat endpoint that retrieves relevant
|
||||
products and asks a local Ollama model to answer grounded in them.
|
||||
- **`frontend/`** - React + Vite single-page app: product search/browse
|
||||
grid, a conversational "Ask AI" panel with source citations, and an
|
||||
admin page to ingest new brands.
|
||||
|
||||
See **`docs/`** for the full architecture write-up, setup guide, and API
|
||||
reference. The two component READMEs (`backend/README.md`,
|
||||
`frontend/README.md`) are quick local references once you've read the
|
||||
main docs once.
|
||||
|
||||
> **v3.0 update:** this project now also includes a **Multi-Store
|
||||
> Intelligence** layer - 5 simulated stores with independent pricing
|
||||
> and inventory, ML-based dynamic discounts, trending-product
|
||||
> detection, a hybrid recommendation engine, and a full analytics
|
||||
> dashboard. See **`docs/CHANGES.docx`** for the complete write-up
|
||||
> (architecture decisions, database schema, API reference, and setup
|
||||
> guide), or jump straight to `backend/scripts/seed_store_intelligence.py`
|
||||
> and `backend/scripts/train_ml_models.py` to try it.
|
||||
|
||||
> **v3.1 update:** added an **AI Nutritional Intelligence** module -
|
||||
> verified nutrition facts retrieved from Open Food Facts (never LLM-
|
||||
> generated), transparent/configurable health & nutrition scoring,
|
||||
> allergen and diet-compatibility detection, ML-based nutritional
|
||||
> similarity and clustering, healthier-alternative suggestions,
|
||||
> personalized nutrition recommendations, and a nutrition analytics
|
||||
> dashboard. See **`docs/NUTRITION_MODULE.docx`** for the complete
|
||||
> write-up, or jump straight to `backend/scripts/enrich_nutrition.py`
|
||||
> and `backend/scripts/train_nutrition_models.py` to try it.
|
||||
|
||||
## Fastest Path to Execute Project (Instant 1-Command Startup)
|
||||
|
||||
The project now includes a **unified single-command launcher** and **automated background initialization**. You no longer need to manually run multiple seed scripts or manage separate terminals:
|
||||
|
||||
```bash
|
||||
# Single command from project root (starts Docker, Backend, and Frontend):
|
||||
python run_project.py
|
||||
|
||||
# Or on Windows, double-click:
|
||||
start_app.bat
|
||||
```
|
||||
|
||||
The system automatically:
|
||||
1. Starts the PostgreSQL container via Docker Desktop.
|
||||
2. Checks database status and skips redundant seeding for instant boot (< 2 seconds).
|
||||
3. Launches Backend API (`http://localhost:8000`) and Frontend UI (`http://localhost:5173`) concurrently.
|
||||
4. Auto-seeds missing catalogs or store intelligence in the background if the database is empty.
|
||||
|
||||
### Individual Commands (Optional / Advanced)
|
||||
- Backend: `start_backend.bat` or `python -m uvicorn app.main:app --reload --port 8000`
|
||||
- Frontend: `start_frontend.bat` or `npm run dev`
|
||||
- System Status API: `http://localhost:8000/api/system/status`
|
||||
|
||||
## Why this exists (what changed from v1.0)
|
||||
|
||||
The original project (`Project_LLM`) could discover, enrich, and store
|
||||
products with embeddings in pgvector - but nothing ever read those
|
||||
embeddings back out. There was no similarity-search function, no API
|
||||
layer (`app/api/` was an empty folder), and the UI was a Streamlit app
|
||||
that only ever queried the catalog by exact brand/category, never by
|
||||
meaning. This project adds the missing retrieval layer
|
||||
(`vector_store.semantic_search`), a RAG orchestration service
|
||||
(`rag_service.py`), a full FastAPI surface, and a React UI to use it.
|
||||
|
||||
It also fixes a real security issue: `app/infrastructure/settings.py`
|
||||
used to hard-code a live database host/port/password as Python literal
|
||||
fallbacks. Every credential now comes only from environment
|
||||
variables/`.env` - see `backend/app/infrastructure/settings.py` for
|
||||
details, and `docs/` for the full write-up.
|
||||
@@ -12,6 +12,16 @@ documentation (`docs/`). This file is just a fast local reference.
|
||||
|
||||
## Quick start
|
||||
|
||||
### One-click (Windows)
|
||||
|
||||
Double-click **`start_backend.bat`**. It picks up `venv\Scripts\python.exe`
|
||||
if present (else falls back to system `python`), starts/creates the
|
||||
`catalog_rag_postgres` Docker container from `docker-compose.yml`,
|
||||
launches Ollama in the background if it's installed but not running,
|
||||
seeds sample data if needed, then starts uvicorn on port 8000.
|
||||
|
||||
### Manual
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
python3 -m venv .venv
|
||||
@@ -23,7 +33,7 @@ cp .env.example .env # then edit DB_PASSWORD etc.
|
||||
# Option A: already have a Postgres+pgvector catalog from the old project?
|
||||
# Just point .env at it (DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASSWORD) - done.
|
||||
# Option B: starting fresh locally?
|
||||
docker compose -f ../docker-compose.yml up -d
|
||||
docker compose -f docker-compose.yml up -d
|
||||
python scripts/seed_sample_data.py # loads bundled sample catalogs instantly
|
||||
|
||||
uvicorn app.main:app --reload --port 8000
|
||||
|
||||
@@ -9,6 +9,14 @@ is a fast local reference.
|
||||
|
||||
## Quick start
|
||||
|
||||
### One-click (Windows)
|
||||
|
||||
Double-click **`start_frontend.bat`**. It checks whether the backend is
|
||||
reachable on port 8000 (just a warning, not a blocker) and starts the
|
||||
Vite dev server.
|
||||
|
||||
### Manual
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
@@ -17,7 +25,8 @@ npm run dev
|
||||
|
||||
Open http://localhost:5173 - the Vite dev server proxies `/api/*` to
|
||||
`http://localhost:8000` automatically (see `vite.config.js`), so make
|
||||
sure the backend is running there first (`../backend/README.md`).
|
||||
sure the backend is running there first (`../backend/README.md`,
|
||||
`../backend/start_backend.bat`).
|
||||
|
||||
If your backend runs somewhere else, set `VITE_API_BASE_URL` in a local
|
||||
`.env` file (copy `.env.example`) instead of relying on the proxy.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
@echo off
|
||||
set "PATH=C:\Users\srira\AppData\Local\Programs\Git\cmd;%PATH%"
|
||||
echo Checking Git version...
|
||||
git --version
|
||||
echo.
|
||||
echo Staging and committing changes...
|
||||
git add .
|
||||
git commit -m "Initial commit and updates"
|
||||
echo.
|
||||
echo Pushing to remote repository (https://gitapp.workolik.com/nearle_daily/Catalogue_pos_nearle.git)...
|
||||
git push -u origin main
|
||||
echo.
|
||||
echo Done!
|
||||
pause
|
||||
217
run_project.py
217
run_project.py
@@ -1,217 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,8 +0,0 @@
|
||||
@echo off
|
||||
title Kirana AI - Backend FastAPI Server
|
||||
echo Starting Backend API (http://localhost:8000)...
|
||||
cd /d "%~dp0backend"
|
||||
if exist "venv\Scripts\activate.bat" call venv\Scripts\activate.bat
|
||||
python scripts\seed_sample_data.py --skip-if-seeded
|
||||
python -m uvicorn app.main:app --reload --port 8000
|
||||
pause
|
||||
@@ -1,5 +0,0 @@
|
||||
@echo off
|
||||
echo Starting Kirana AI Frontend (React + Vite)...
|
||||
cd /d "%~dp0frontend"
|
||||
npm run dev
|
||||
pause
|
||||
Reference in New Issue
Block a user