AldawsariNLP's picture
pushing alst changes ...2
f917987
"""
Hugging Face Spaces entry point
This file serves both the FastAPI backend and React frontend
"""
import os
import subprocess
import sys
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from fastapi.middleware.cors import CORSMiddleware
from dotenv import load_dotenv
from backend.main import app as backend_app, initialize_rag_system
# def run_files_upload_script(script_path: Path) -> None:
# """Run the files_upload.py helper to ensure documents are available."""
# if not script_path.exists():
# print(f"[root_app] Upload script not found at {script_path}")
# return
# print(f"[root_app] Running upload script: {script_path}")
# try:
# completed = subprocess.run(
# [sys.executable, str(script_path)],
# capture_output=True,
# text=True,
# check=True,
# )
# if completed.stdout:
# print(f"[root_app] upload script output:\n{completed.stdout}")
# if completed.stderr:
# print(f"[root_app] upload script warnings:\n{completed.stderr}")
# except subprocess.CalledProcessError as exc:
# print(f"[root_app] WARNING: upload script failed with code {exc.returncode}")
# if exc.stdout:
# print(f"[root_app] upload script stdout:\n{exc.stdout}")
# if exc.stderr:
# print(f"[root_app] upload script stderr:\n{exc.stderr}")
# raise
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Lifespan context manager for FastAPI startup and shutdown"""
# Startup
print("[root_app] Startup event triggered")
# Set flag to indicate parent app handles initialization (prevents double init)
os.environ["RAG_INIT_BY_PARENT"] = "true"
print("[root_app] Set RAG_INIT_BY_PARENT flag to prevent double initialization")
# Initialize RAG system here to ensure it runs
# The backend's lifespan will skip initialization when this flag is set
print("[root_app] Initializing RAG system...")
try:
# Set up paths (same as backend/main.py)
# Calculate paths relative to app.py (project root)
project_root = Path(__file__).parent
documents_dir = project_root / "documents"
processed_json = project_root / "processed_documents.json"
# Load environment variables from .env file (if it exists)
env_path = project_root / ".env"
# print(f"[root_app] .env file path: {env_path}")
# print(f"[root_app] .env file exists? {env_path.exists()}")
if env_path.exists():
load_dotenv(env_path, override=True)
# print("[root_app] Loaded .env file")
else:
# Try loading anyway in case it's in a different location
load_dotenv(env_path, override=True)
# print("[root_app] .env file not found, using environment variables")
# Check for API keys (from .env or environment variables)
openai_key = os.getenv("OPENAI_API_KEY")
hf_token = os.getenv("HF_TOKEN")
# if openai_key:
# print(f"[root_app] OPENAI_API_KEY found (length: {len(openai_key)} characters)")
# else:
# print("[root_app] WARNING: OPENAI_API_KEY not found in environment")
# if hf_token:
# print(f"[root_app] HF_TOKEN found (length: {len(hf_token)} characters)")
# Set global variables in backend.main module so initialize_rag_system can use them
import backend.main as backend_main
backend_main.PROJECT_ROOT = project_root
backend_main.DOCUMENTS_DIR = documents_dir
backend_main.PROCESSED_JSON = processed_json
# print(f"[root_app] PROJECT_ROOT: {project_root}")
# print(f"[root_app] DOCUMENTS_DIR: {documents_dir} (exists: {documents_dir.exists()})")
# print(f"[root_app] PROCESSED_JSON: {processed_json} (exists: {processed_json.exists()})")
# Run the upload script before initializing the RAG system
# run_files_upload_script(upload_script)
# Initialize RAG system with timeout protection
# print("[root_app] Calling initialize_rag_system()...")
initialize_rag_system()
print("[root_app] ✓ RAG system initialization completed successfully")
except Exception as e:
print(f"[root_app] ERROR: RAG system initialization failed: {e}")
import traceback
traceback.print_exc()
# Don't raise - allow app to start even if RAG init fails
# The /ask endpoint will return 503 if RAG is not ready
print("[root_app] Continuing startup despite RAG initialization failure")
print("[root_app] Mounting backend application at /api")
yield
# Shutdown (if needed in the future)
print("[root_app] FastAPI lifespan shutdown")
# Create a new FastAPI app that will serve both backend and frontend
app = FastAPI(lifespan=lifespan)
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Mount the backend API
app.mount("/api", backend_app)
# Serve React frontend
frontend_path = Path(__file__).parent / "frontend" / "build"
if frontend_path.exists():
# Serve static files (JS, CSS, images, etc.)
static_path = frontend_path / "static"
if static_path.exists():
app.mount("/static", StaticFiles(directory=str(static_path)), name="static")
# Serve index.html for root and all other routes (React Router)
@app.get("/")
async def serve_index():
index_file = frontend_path / "index.html"
if index_file.exists():
return FileResponse(str(index_file))
return {"message": "Frontend not built. Please run 'npm run build' in the frontend directory."}
@app.get("/{full_path:path}")
async def serve_react_app(full_path: str, request: Request):
# Don't interfere with API routes
if full_path.startswith("api"):
return
# Don't interfere with static files
if full_path.startswith("static"):
return
index_file = frontend_path / "index.html"
if index_file.exists():
return FileResponse(str(index_file))
return {"message": "Frontend not built."}
else:
@app.get("/")
async def root():
return {"message": "Frontend not built. Please run 'npm run build' in the frontend directory."}
if __name__ == "__main__":
import uvicorn
port = int(os.getenv("PORT", 7860))# 7860
uvicorn.run(app, host="0.0.0.0", port=port)