Review-RAG / api /main.py
HariHaran9597
Initial commit
1d70196
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import sys
import os
import pandas as pd
# Ensure src modules can be imported
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'src')))
try:
from inference import ABSAPredictor
from rag_engine import ReviewRAGEngine
except ImportError as e:
print(f"Error importing core modules: {e}")
ABSAPredictor = None
ReviewRAGEngine = None
app = FastAPI(
title="Aspect-Based Review Intelligence API",
description="Backend for ABSA predictions and RAG-based Review Q&A",
version="1.0"
)
# Initialize engines lazily on startup
absa_engine = None
rag_engine = None
@app.on_event("startup")
def load_engines():
global absa_engine, rag_engine
print("Loading models into memory...")
if ABSAPredictor:
absa_engine = ABSAPredictor(model_path='models/absa-roberta-final')
if ReviewRAGEngine:
rag_engine = ReviewRAGEngine(vectorstore_dir='vectorstore')
class ReviewRequest(BaseModel):
text: str
class QuestionRequest(BaseModel):
question: str
top_k: int = 15
@app.get("/")
def health_check():
return {
"status": "online",
"absa_loaded": absa_engine is not None and absa_engine.is_loaded,
"rag_loaded": rag_engine is not None and rag_engine.index is not None
}
@app.post("/predict")
def predict_aspects(req: ReviewRequest):
if not absa_engine:
raise HTTPException(status_code=503, detail="ABSA engine failed to initialize")
if not req.text.strip():
raise HTTPException(status_code=400, detail="Review text cannot be empty")
results = absa_engine.predict(req.text)
return {
"review_text": req.text,
"aspect_sentiments": results
}
@app.post("/ask")
def ask_question(req: QuestionRequest):
if not rag_engine:
raise HTTPException(status_code=503, detail="RAG engine failed to initialize")
if not req.question.strip():
raise HTTPException(status_code=400, detail="Question cannot be empty")
answer = rag_engine.answer_question(req.question, top_k=req.top_k)
return {
"question": req.question,
"answer": answer
}
@app.get("/stats")
def get_aggregate_stats():
"""Returns basic aggregate statistics from the FAISS metadata database for dashboard plots"""
if not rag_engine or rag_engine.metadata_df is None:
return {"error": "Metadata database not found in vectorstore."}
df = rag_engine.metadata_df
aspect_counts = {
'food': {'positive': 0, 'negative': 0, 'neutral': 0, 'conflict': 0},
'service': {'positive': 0, 'negative': 0, 'neutral': 0, 'conflict': 0},
'ambiance': {'positive': 0, 'negative': 0, 'neutral': 0, 'conflict': 0},
'price': {'positive': 0, 'negative': 0, 'neutral': 0, 'conflict': 0},
'anecdotes/miscellaneous': {'positive': 0, 'negative': 0, 'neutral': 0, 'conflict': 0}
}
import json
for aspects_str in df.get('predicted_aspects', []):
if pd.isna(aspects_str) or not aspects_str:
continue
try:
aspect_dict = json.loads(aspects_str)
for aspect, data in aspect_dict.items():
if aspect in aspect_counts and data['sentiment'] in aspect_counts[aspect]:
aspect_counts[aspect][data['sentiment']] += 1
except:
continue
return {
"total_reviews": len(df),
"aspect_sentiment_counts": aspect_counts
}
if __name__ == "__main__":
import uvicorn
# Typically run with: uvicorn api.main:app --reload
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)