Rameen191 commited on
Commit
97ed999
·
verified ·
1 Parent(s): 748b53b

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +12 -0
  2. app.py +125 -0
  3. requirements.txt +6 -0
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY app.py .
9
+
10
+ EXPOSE 7860
11
+
12
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel, Field
4
+ from transformers import pipeline
5
+ from typing import Dict, List
6
+ from contextlib import asynccontextmanager
7
+ import torch
8
+ import logging
9
+ import time
10
+ from datetime import datetime, timezone
11
+
12
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
13
+ logger = logging.getLogger("sentiment-api")
14
+
15
+ MODEL_ID = "Rameen191/sentiment-tutorial" # <-- apna HF username dalen
16
+
17
+ ml_models: Dict[str, object] = {}
18
+
19
+ @asynccontextmanager
20
+ async def lifespan(app: FastAPI):
21
+ logger.info(f"Loading model: {MODEL_ID} ...")
22
+ try:
23
+ ml_models["classifier"] = pipeline(
24
+ "sentiment-analysis",
25
+ model=MODEL_ID,
26
+ device=0 if torch.cuda.is_available() else -1,
27
+ )
28
+ logger.info("✅ Model loaded successfully.")
29
+ except Exception as e:
30
+ logger.error(f"❌ Failed to load model: {e}")
31
+ ml_models["classifier"] = None
32
+ yield
33
+ ml_models.clear()
34
+
35
+ app = FastAPI(
36
+ title="Sentiment Analysis API",
37
+ description="A DistilBERT-based sentiment classifier.",
38
+ version="1.0.0",
39
+ lifespan=lifespan,
40
+ )
41
+
42
+ app.add_middleware(
43
+ CORSMiddleware,
44
+ allow_origins=["*"],
45
+ allow_credentials=True,
46
+ allow_methods=["*"],
47
+ allow_headers=["*"],
48
+ )
49
+
50
+ class TextRequest(BaseModel):
51
+ text: str = Field(..., min_length=1, max_length=5000)
52
+
53
+ class BatchRequest(BaseModel):
54
+ texts: List[str] = Field(..., min_length=1, max_length=50)
55
+
56
+ class SentimentResponse(BaseModel):
57
+ text: str
58
+ label: str
59
+ confidence: float
60
+ probabilities: Dict[str, float]
61
+ timestamp: str
62
+
63
+ @app.middleware("http")
64
+ async def log_requests(request, call_next):
65
+ start = time.time()
66
+ response = await call_next(request)
67
+ duration = time.time() - start
68
+ logger.info(f'{request.method} {request.url.path} -> {response.status_code} ({duration:.3f}s)')
69
+ return response
70
+
71
+ @app.get("/")
72
+ async def root():
73
+ return {"message": "Sentiment Analysis API", "docs": "/docs", "health": "/health"}
74
+
75
+ @app.get("/health")
76
+ async def health_check():
77
+ return {
78
+ "status": "healthy" if ml_models.get("classifier") is not None else "degraded",
79
+ "model_loaded": ml_models.get("classifier") is not None,
80
+ "model_id": MODEL_ID,
81
+ "timestamp": datetime.now(timezone.utc).isoformat(),
82
+ }
83
+
84
+ @app.post("/predict", response_model=SentimentResponse)
85
+ async def predict_sentiment(request: TextRequest):
86
+ classifier = ml_models.get("classifier")
87
+ if classifier is None:
88
+ raise HTTPException(status_code=503, detail="Model not loaded.")
89
+ try:
90
+ result = classifier(request.text)[0]
91
+ probs = {result['label']: round(result['score'], 4)}
92
+ other_label = 'NEGATIVE' if result['label'] == 'POSITIVE' else 'POSITIVE'
93
+ probs[other_label] = round(1 - result['score'], 4)
94
+ return SentimentResponse(
95
+ text=request.text[:200],
96
+ label=result['label'],
97
+ confidence=round(result['score'], 4),
98
+ probabilities=probs,
99
+ timestamp=datetime.now(timezone.utc).isoformat(),
100
+ )
101
+ except Exception as e:
102
+ logger.error(f"Prediction error: {e}")
103
+ raise HTTPException(status_code=500, detail="Internal error during prediction.")
104
+
105
+ @app.post("/predict/batch")
106
+ async def predict_batch(request: BatchRequest):
107
+ classifier = ml_models.get("classifier")
108
+ if classifier is None:
109
+ raise HTTPException(status_code=503, detail="Model not loaded.")
110
+ try:
111
+ results = classifier(request.texts)
112
+ return {
113
+ "results": [
114
+ {"text": text[:100], "label": r['label'], "confidence": round(r['score'], 4)}
115
+ for text, r in zip(request.texts, results)
116
+ ],
117
+ "total": len(results),
118
+ }
119
+ except Exception as e:
120
+ logger.error(f"Batch prediction error: {e}")
121
+ raise HTTPException(status_code=500, detail="Internal error during batch prediction.")
122
+
123
+ if __name__ == "__main__":
124
+ import uvicorn
125
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi==0.111.0
2
+ uvicorn[standard]==0.30.1
3
+ transformers==4.41.2
4
+ torch==2.3.1
5
+ pydantic==2.7.4
6
+ huggingface_hub==0.23.4