Spaces:
Sleeping
Sleeping
File size: 5,263 Bytes
defd965 3f9fb93 defd965 6292cce 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 defd965 3f9fb93 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | 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)
|