Hannia67 commited on
Commit
73bac42
·
verified ·
1 Parent(s): 1039a63

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -0
app.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel, Field
4
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
5
+ from typing import Dict, List
6
+ from contextlib import asynccontextmanager
7
+ import torch
8
+ import logging
9
+ from datetime import datetime, timezone
10
+
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger("sentiment-api")
13
+
14
+ MODEL_ID = "Hannia67/my-sentiment-analyzer"
15
+ ml_models: Dict[str, object] = {}
16
+
17
+ @asynccontextmanager
18
+ async def lifespan(app: FastAPI):
19
+ logger.info(f"Loading model: {MODEL_ID}")
20
+ try:
21
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
22
+ model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
23
+ model.eval()
24
+ ml_models["tokenizer"] = tokenizer
25
+ ml_models["model"] = model
26
+ logger.info("Model loaded successfully.")
27
+ except Exception as e:
28
+ logger.error(f"Failed to load model: {e}")
29
+ yield
30
+ ml_models.clear()
31
+
32
+ app = FastAPI(
33
+ title="Sentiment Analysis API",
34
+ description="A DistilBERT-based sentiment classifier.",
35
+ version="1.0.0",
36
+ lifespan=lifespan,
37
+ )
38
+
39
+ app.add_middleware(
40
+ CORSMiddleware,
41
+ allow_origins=["*"],
42
+ allow_credentials=True,
43
+ allow_methods=["*"],
44
+ allow_headers=["*"],
45
+ )
46
+
47
+ class TextRequest(BaseModel):
48
+ text: str = Field(..., min_length=1, max_length=5000)
49
+
50
+ class BatchRequest(BaseModel):
51
+ texts: List[str] = Field(..., min_length=1, max_length=50)
52
+
53
+ def predict(text):
54
+ tokenizer = ml_models["tokenizer"]
55
+ model = ml_models["model"]
56
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128)
57
+ with torch.no_grad():
58
+ outputs = model(**inputs)
59
+ probs = torch.softmax(outputs.logits, dim=-1)
60
+ label = "POSITIVE" if probs[0][1] > probs[0][0] else "NEGATIVE"
61
+ confidence = round(probs[0].max().item(), 4)
62
+ other = round(1 - confidence, 4)
63
+ return label, confidence, other
64
+
65
+ @app.get("/")
66
+ async def root():
67
+ return {"message": "Sentiment Analysis API", "docs": "/docs", "health": "/health"}
68
+
69
+ @app.get("/health")
70
+ async def health_check():
71
+ return {
72
+ "status": "healthy" if ml_models.get("model") is not None else "degraded",
73
+ "model_loaded": ml_models.get("model") is not None,
74
+ "model_id": MODEL_ID,
75
+ "timestamp": datetime.now(timezone.utc).isoformat(),
76
+ }
77
+
78
+ @app.post("/predict")
79
+ async def predict_sentiment(request: TextRequest):
80
+ if ml_models.get("model") is None:
81
+ raise HTTPException(status_code=503, detail="Model not loaded.")
82
+ try:
83
+ label, confidence, other = predict(request.text)
84
+ other_label = "NEGATIVE" if label == "POSITIVE" else "POSITIVE"
85
+ return {
86
+ "text": request.text[:200],
87
+ "label": label,
88
+ "confidence": confidence,
89
+ "probabilities": {label: confidence, other_label: other},
90
+ "timestamp": datetime.now(timezone.utc).isoformat(),
91
+ }
92
+ except Exception as e:
93
+ logger.error(f"Prediction error: {e}")
94
+ raise HTTPException(status_code=500, detail="Prediction failed.")
95
+
96
+ @app.post("/predict/batch")
97
+ async def predict_batch(request: BatchRequest):
98
+ if ml_models.get("model") is None:
99
+ raise HTTPException(status_code=503, detail="Model not loaded.")
100
+ try:
101
+ results = []
102
+ for text in request.texts:
103
+ label, confidence, _ = predict(text)
104
+ results.append({"text": text[:100], "label": label, "confidence": confidence})
105
+ return {"results": results, "total": len(results)}
106
+ except Exception as e:
107
+ logger.error(f"Batch error: {e}")
108
+ raise HTTPException(status_code=500, detail="Batch prediction failed.")