Programmer140/Hackathon
0
1from fastapi import FastAPI, HTTPException2from fastapi.middleware.cors import CORSMiddleware3from pydantic import BaseModel4import sys5import os6import logging7 8# Add the parent directory to sys.path so we can import the chat functionality9sys.path.append(os.path.dirname(os.path.abspath(__file__)))10 11# Import and validate configuration on startup12from secure_config import validate_config13validate_config()14 15from chat_openai import query_qdrant, format_response16from ingest_local_docs import ingest_local_docs # <- only import the main function, no circular import17 18# Create FastAPI app19app = FastAPI(20 title="AI Assistant Backend",21 description="Backend API for Docusaurus AI Assistant"22)23 24# Add CORS middleware25app.add_middleware(26 CORSMiddleware,27 allow_origins=[28 "http://localhost:3000",29 "http://localhost:5173",30 "http://127.0.0.1:5173",31 "http://localhost:3001",32 "http://127.0.0.1:8000",33 "http://localhost:8000",34 "https://*.vercel.app"35 ],36 allow_credentials=True,37 allow_methods=["*"],38 allow_headers=["*"],39)40 41# Define request/response models42class ChatRequest(BaseModel):43 query: str44 45class ChatResponse(BaseModel):46 response: str47 48# ======================49# Startup Event50# ======================51@app.on_event("startup")52async def startup_event():53 """Ensure Qdrant collection exists and ingest docs if empty"""54 try:55 print("Starting ingestion process on startup...")56 # This will safely create collection if needed and ingest docs57 documents_indexed = ingest_local_docs()58 print(f"Ingestion completed. Total chunks stored: {documents_indexed}")59 except Exception as e:60 logging.error(f"Startup ingestion failed: {e}")61 # Continue running backend even if ingestion fails62 63# ======================64# Basic endpoints65# ======================66@app.get("/")67def read_root():68 return {"message": "AI Assistant Backend is running!"}69 70@app.get("/health")71def health_check():72 return {"status": "ok", "service": "backend", "errors": False}73 74# ======================75# Chat endpoint76# ======================77@app.post("/chat", response_model=ChatResponse)78async def chat_endpoint(request: ChatRequest):79 try:80 query = request.query81 if not query:82 raise HTTPException(status_code=400, detail="Query is required")83 84 # Query Qdrant database85 try:86 search_results = query_qdrant(query)87 except Exception as e:88 logging.error(f"Error querying Qdrant: {str(e)}")89 raise HTTPException(90 status_code=500,91 detail=f"Unable to connect to the knowledge base: {str(e)}"92 )93 94 # Format the response95 try:96 response = format_response(query, search_results)97 except Exception as e:98 logging.error(f"Error formatting response: {str(e)}")99 raise HTTPException(100 status_code=500,101 detail=f"Error generating response: {str(e)}"102 )103 104 return ChatResponse(response=response)105 106 except HTTPException:107 raise108 except Exception as e:109 logging.error(f"Unexpected error in chat endpoint: {str(e)}")110 raise HTTPException(111 status_code=500,112 detail=f"An unexpected error occurred: {str(e)}"113 )114 115# ======================116# Admin endpoints117# ======================118@app.post("/admin/ingest")119async def admin_ingest():120 """Manual ingestion endpoint"""121 try:122 documents_indexed = ingest_local_docs()123 return {124 "status": "success",125 "message": "Documents ingested successfully",126 "documents_indexed": documents_indexed127 }128 except Exception as e:129 logging.error(f"Error during manual ingestion: {str(e)}")130 raise HTTPException(status_code=500, detail=f"Error during ingestion: {str(e)}")131 132@app.get("/admin/status")133async def admin_status():134 """Check collection status"""135 try:136 from qdrant_client import QdrantClient137 from config import QDRANT_URL, QDRANT_API_KEY, COLLECTION_NAME138 139 qdrant_client = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY, prefer_grpc=False)140 collection_info = qdrant_client.get_collection(collection_name=COLLECTION_NAME)141 point_count = getattr(collection_info, "points_count", 0)142 return {"collection_exists": True, "point_count": point_count, "status": "healthy"}143 except Exception as e:144 return {"collection_exists": False, "point_count": 0, "status": "missing", "error": str(e)}145 146@app.get("/debug/docs")147async def debug_docs():148 """Check if docs directory exists and its contents"""149 docs_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'docs')150 if os.path.exists(docs_path):151 files = os.listdir(docs_path)152 return {"docs_exists": True, "file_count": len(files), "files": files}153 else:154 return {"docs_exists": False, "file_count": 0, "files": []}155 156# ======================157# Run Uvicorn158# ======================159if __name__ == "__main__":160 import uvicorn161 port = int(os.environ.get("PORT", 8000))162 uvicorn.run(app, host="0.0.0.0", port=port)163 