Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel, Field | |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification | |
| from typing import Dict, List | |
| from contextlib import asynccontextmanager | |
| import torch | |
| import logging | |
| from datetime import datetime, timezone | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger("sentiment-api") | |
| MODEL_ID = "Whoawaisahmad/my-sentiment-analyzer" | |
| ml_models: Dict[str, object] = {} | |
| async def lifespan(app: FastAPI): | |
| logger.info(f"Loading model: {MODEL_ID}") | |
| try: | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) | |
| model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID) | |
| model.eval() | |
| ml_models["tokenizer"] = tokenizer | |
| ml_models["model"] = model | |
| logger.info("Model loaded successfully.") | |
| except Exception as e: | |
| logger.error(f"Failed to load model: {e}") | |
| yield | |
| ml_models.clear() | |
| app = FastAPI( | |
| title="Sentiment Analysis API", | |
| description="A DistilBERT-based sentiment classifier.", | |
| version="1.0.0", | |
| lifespan=lifespan, | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| class TextRequest(BaseModel): | |
| text: str = Field(..., min_length=1, max_length=5000) | |
| class BatchRequest(BaseModel): | |
| texts: List[str] = Field(..., min_length=1, max_length=50) | |
| def predict(text): | |
| tokenizer = ml_models["tokenizer"] | |
| model = ml_models["model"] | |
| inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128) | |
| with torch.no_grad(): | |
| outputs = model(**inputs) | |
| probs = torch.softmax(outputs.logits, dim=-1) | |
| label = "POSITIVE" if probs[0][1] > probs[0][0] else "NEGATIVE" | |
| confidence = round(probs[0].max().item(), 4) | |
| other = round(1 - confidence, 4) | |
| return label, confidence, other | |
| async def root(): | |
| return {"message": "Sentiment Analysis API", "docs": "/docs", "health": "/health"} | |
| async def health_check(): | |
| return { | |
| "status": "healthy" if ml_models.get("model") is not None else "degraded", | |
| "model_loaded": ml_models.get("model") is not None, | |
| "model_id": MODEL_ID, | |
| "timestamp": datetime.now(timezone.utc).isoformat(), | |
| } | |
| async def predict_sentiment(request: TextRequest): | |
| if ml_models.get("model") is None: | |
| raise HTTPException(status_code=503, detail="Model not loaded.") | |
| try: | |
| label, confidence, other = predict(request.text) | |
| other_label = "NEGATIVE" if label == "POSITIVE" else "POSITIVE" | |
| return { | |
| "text": request.text[:200], | |
| "label": label, | |
| "confidence": confidence, | |
| "probabilities": {label: confidence, other_label: other}, | |
| "timestamp": datetime.now(timezone.utc).isoformat(), | |
| } | |
| except Exception as e: | |
| logger.error(f"Prediction error: {e}") | |
| raise HTTPException(status_code=500, detail="Prediction failed.") | |
| async def predict_batch(request: BatchRequest): | |
| if ml_models.get("model") is None: | |
| raise HTTPException(status_code=503, detail="Model not loaded.") | |
| try: | |
| results = [] | |
| for text in request.texts: | |
| label, confidence, _ = predict(text) | |
| results.append({"text": text[:100], "label": label, "confidence": confidence}) | |
| return {"results": results, "total": len(results)} | |
| except Exception as e: | |
| logger.error(f"Batch error: {e}") | |
| raise HTTPException(status_code=500, detail="Batch prediction failed.") |