Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| 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) | |
| 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."} | |
| 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: | |
| 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) | |