Hackathon / api.py
Programmer140's picture
Update api.py
3f9fb93 verified
Raw
History Blame Contribute Delete
5.26 kB
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
# ======================
@app.on_event("startup")
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
# ======================
@app.get("/")
def read_root():
return {"message": "AI Assistant Backend is running!"}
@app.get("/health")
def health_check():
return {"status": "ok", "service": "backend", "errors": False}
# ======================
# Chat endpoint
# ======================
@app.post("/chat", response_model=ChatResponse)
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
# ======================
@app.post("/admin/ingest")
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)}")
@app.get("/admin/status")
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)}
@app.get("/debug/docs")
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)