from fastapi import FastAPI, Request, Header, HTTPException, UploadFile, File from fastapi.responses import HTMLResponse, JSONResponse from fastapi.openapi.utils import get_openapi from fastapi.openapi.docs import get_swagger_ui_html from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from transformers import pipeline from io import StringIO import os, csv, logging from model import summarize_review, smart_summarize from typing import Optional app = FastAPI( title="🧠 NeuroPulse AI", description="Multilingual GenAI for smarter feedback — summarization, sentiment, emotion, aspects, Q&A and tags.", version="2025.1.0", openapi_url="/openapi.json", docs_url=None, redoc_url="/redoc" ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/docs", include_in_schema=False) def custom_swagger_ui(): return get_swagger_ui_html( openapi_url=app.openapi_url, title="🧠 Swagger UI - NeuroPulse AI", swagger_favicon_url="https://cdn-icons-png.flaticon.com/512/3794/3794616.png", swagger_js_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.18.3/swagger-ui-bundle.js", swagger_css_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.18.3/swagger-ui.css", ) @app.get("/", response_class=HTMLResponse) def root(): return open("app/static/index.html").read() class ReviewInput(BaseModel): text: str model: str = "distilbert-base-uncased-finetuned-sst-2-english" industry: Optional[str] = None aspects: bool = False follow_up: Optional[str] = None product_category: Optional[str] = None device: Optional[str] = None intelligence: Optional[bool] = False verbosity: Optional[str] = "detailed" explain: Optional[bool] = False class BulkReviewInput(BaseModel): reviews: list[str] model: str = "distilbert-base-uncased-finetuned-sst-2-english" industry: Optional[list[str]] = None aspects: bool = False product_category: Optional[list[str]] = None device: Optional[list[str]] = None VALID_API_KEY = "my-secret-key" logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6") emotion_model = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base", top_k=1) sentiment_pipelines = { "distilbert-base-uncased-finetuned-sst-2-english": pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english"), "nlptown/bert-base-multilingual-uncased-sentiment": pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment") } def auto_fill(value: Optional[str], default: str = "Generic") -> str: if not value or value.strip().lower() == "auto-detect": return default return value @app.post("/bulk/") async def bulk(data: BulkReviewInput, x_api_key: str = Header(None)): if x_api_key != VALID_API_KEY: raise HTTPException(status_code=401, detail="Invalid or missing API key") sentiment_pipeline = sentiment_pipelines[data.model] summaries = summarizer(data.reviews, max_length=80, min_length=20, truncation=True) sentiments = sentiment_pipeline(data.reviews) emotions = emotion_model(data.reviews) results = [] for i, review in enumerate(data.reviews): label = sentiments[i]["label"] if "star" in label: stars = int(label[0]) label = "NEGATIVE" if stars <= 2 else "NEUTRAL" if stars == 3 else "POSITIVE" result = { "review": review, "summary": summaries[i]["summary_text"], "sentiment": label, "emotion": emotions[i][0]["label"], "aspects": [], "product_category": auto_fill(data.product_category[i]) if data.product_category else None, "device": auto_fill(data.device[i], "Web") if data.device else None, "industry": auto_fill(data.industry[i]) if data.industry else None, } results.append(result) return {"results": results} @app.post("/analyze/") async def analyze(request: Request, data: ReviewInput, x_api_key: str = Header(None)): if x_api_key != VALID_API_KEY: raise HTTPException(status_code=401, detail="Invalid or missing API key") sentiment_pipeline = sentiment_pipelines.get(data.model) summary = smart_summarize(data.text) if request.query_params.get("smart") == "1" else summarize_review(data.text) sentiment = sentiment_pipeline(data.text)[0] label = sentiment["label"] if "star" in label: stars = int(label[0]) label = "NEGATIVE" if stars <= 2 else "NEUTRAL" if stars == 3 else "POSITIVE" emotion = emotion_model(data.text)[0][0]["label"] aspects_list = [] if data.aspects: for asp in ["battery", "price", "camera"]: if asp in data.text.lower(): asp_result = sentiment_pipeline(asp + " " + data.text)[0] aspects_list.append({ "aspect": asp, "sentiment": asp_result["label"], "score": asp_result["score"] }) follow_up_response = None if data.follow_up and data.intelligence: follow_up_response = f"[Mocked Detailed Answer in {data.verbosity} mode]" explanation = "This summary was generated using transformer-based sequence modeling and contextual keyword expansion." if data.explain else None return { "summary": summary, "sentiment": {"label": label, "score": sentiment["score"]}, "emotion": emotion, "aspects": aspects_list, "follow_up": follow_up_response, "explanation": explanation, "product_category": auto_fill(data.product_category), "device": auto_fill(data.device, "Web"), "industry": auto_fill(data.industry) } def custom_openapi(): if app.openapi_schema: return app.openapi_schema openapi_schema = get_openapi( title=app.title, version=app.version, description=""" NeuroPulse AI · Smart GenAI Feedback Engine
Summarize reviews, detect sentiment/emotion, extract aspects, tag metadata, and ask GPT follow-ups. """, routes=app.routes ) openapi_schema["openapi"] = "3.0.0" app.openapi_schema = openapi_schema return app.openapi_schema app.openapi = custom_openapi