Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| import sys | |
| import os | |
| import logging | |
| # Add the parent directory to sys.path so we can import the chat functionality | |
| sys.path.append(os.path.dirname(os.path.abspath(__file__))) | |
| # Import and validate configuration on startup | |
| from secure_config import validate_config | |
| validate_config() | |
| from chat_openai import query_qdrant, format_response | |
| from ingest_local_docs import ingest_local_docs # <- only import the main function, no circular import | |
| # Create FastAPI app | |
| app = FastAPI( | |
| title="AI Assistant Backend", | |
| description="Backend API for Docusaurus AI Assistant" | |
| ) | |
| # Add CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=[ | |
| "http://localhost:3000", | |
| "http://localhost:5173", | |
| "http://127.0.0.1:5173", | |
| "http://localhost:3001", | |
| "http://127.0.0.1:8000", | |
| "http://localhost:8000", | |
| "https://*.vercel.app" | |
| ], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Define request/response models | |
| class ChatRequest(BaseModel): | |
| query: str | |
| class ChatResponse(BaseModel): | |
| response: str | |
| # ====================== | |
| # Startup Event | |
| # ====================== | |
| async def startup_event(): | |
| """Ensure Qdrant collection exists and ingest docs if empty""" | |
| try: | |
| print("Starting ingestion process on startup...") | |
| # This will safely create collection if needed and ingest docs | |
| documents_indexed = ingest_local_docs() | |
| print(f"Ingestion completed. Total chunks stored: {documents_indexed}") | |
| except Exception as e: | |
| logging.error(f"Startup ingestion failed: {e}") | |
| # Continue running backend even if ingestion fails | |
| # ====================== | |
| # Basic endpoints | |
| # ====================== | |
| def read_root(): | |
| return {"message": "AI Assistant Backend is running!"} | |
| def health_check(): | |
| return {"status": "ok", "service": "backend", "errors": False} | |
| # ====================== | |
| # Chat endpoint | |
| # ====================== | |
| async def chat_endpoint(request: ChatRequest): | |
| try: | |
| query = request.query | |
| if not query: | |
| raise HTTPException(status_code=400, detail="Query is required") | |
| # Query Qdrant database | |
| try: | |
| search_results = query_qdrant(query) | |
| except Exception as e: | |
| logging.error(f"Error querying Qdrant: {str(e)}") | |
| raise HTTPException( | |
| status_code=500, | |
| detail=f"Unable to connect to the knowledge base: {str(e)}" | |
| ) | |
| # Format the response | |
| try: | |
| response = format_response(query, search_results) | |
| except Exception as e: | |
| logging.error(f"Error formatting response: {str(e)}") | |
| raise HTTPException( | |
| status_code=500, | |
| detail=f"Error generating response: {str(e)}" | |
| ) | |
| return ChatResponse(response=response) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logging.error(f"Unexpected error in chat endpoint: {str(e)}") | |
| raise HTTPException( | |
| status_code=500, | |
| detail=f"An unexpected error occurred: {str(e)}" | |
| ) | |
| # ====================== | |
| # Admin endpoints | |
| # ====================== | |
| async def admin_ingest(): | |
| """Manual ingestion endpoint""" | |
| try: | |
| documents_indexed = ingest_local_docs() | |
| return { | |
| "status": "success", | |
| "message": "Documents ingested successfully", | |
| "documents_indexed": documents_indexed | |
| } | |
| except Exception as e: | |
| logging.error(f"Error during manual ingestion: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Error during ingestion: {str(e)}") | |
| async def admin_status(): | |
| """Check collection status""" | |
| try: | |
| from qdrant_client import QdrantClient | |
| from config import QDRANT_URL, QDRANT_API_KEY, COLLECTION_NAME | |
| qdrant_client = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY, prefer_grpc=False) | |
| collection_info = qdrant_client.get_collection(collection_name=COLLECTION_NAME) | |
| point_count = getattr(collection_info, "points_count", 0) | |
| return {"collection_exists": True, "point_count": point_count, "status": "healthy"} | |
| except Exception as e: | |
| return {"collection_exists": False, "point_count": 0, "status": "missing", "error": str(e)} | |
| async def debug_docs(): | |
| """Check if docs directory exists and its contents""" | |
| docs_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'docs') | |
| if os.path.exists(docs_path): | |
| files = os.listdir(docs_path) | |
| return {"docs_exists": True, "file_count": len(files), "files": files} | |
| else: | |
| return {"docs_exists": False, "file_count": 0, "files": []} | |
| # ====================== | |
| # Run Uvicorn | |
| # ====================== | |
| if __name__ == "__main__": | |
| import uvicorn | |
| port = int(os.environ.get("PORT", 8000)) | |
| uvicorn.run(app, host="0.0.0.0", port=port) | |