medoxz543 commited on
Commit
20f4f84
Β·
1 Parent(s): 4dd61db
Files changed (7) hide show
  1. Dockerfile +10 -10
  2. db_pool.py +23 -0
  3. logger.py +16 -0
  4. model_loader.py +16 -0
  5. requirements.txt +2 -1
  6. routes.py +43 -0
  7. server.py +29 -0
Dockerfile CHANGED
@@ -1,20 +1,20 @@
1
- # Start from the official Python image
2
  FROM python:3.9
3
 
4
- # Create a non-root user to run the app
5
  RUN useradd -m -u 1000 user
6
  USER user
7
  ENV PATH="/home/user/.local/bin:$PATH"
8
 
9
- # Set the working directory
10
  WORKDIR /app
11
 
12
- # Install dependencies
13
- COPY --chown=user ./requirements.txt requirements.txt
14
- RUN pip install --no-cache-dir --upgrade -r requirements.txt
15
 
16
- # Copy the app files
17
- COPY --chown=user . /app
18
 
19
- # Run the FastAPI app
20
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
1
+ # Use official Python image
2
  FROM python:3.9
3
 
4
+ # Create non-root user
5
  RUN useradd -m -u 1000 user
6
  USER user
7
  ENV PATH="/home/user/.local/bin:$PATH"
8
 
9
+ # Set working dir
10
  WORKDIR /app
11
 
12
+ # Install requirements
13
+ COPY --chown=user requirements.txt requirements.txt
14
+ RUN pip install --no-cache-dir -r requirements.txt
15
 
16
+ # Copy project files
17
+ COPY --chown=user . .
18
 
19
+ # Run FastAPI app
20
+ CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7860"]
db_pool.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import psycopg2
3
+ from psycopg2 import pool
4
+
5
+ DB_URL = os.getenv("DATABASE_URL")
6
+ _pool = None
7
+
8
+ def init_db_pool():
9
+ global _pool
10
+ if DB_URL:
11
+ _pool = psycopg2.pool.SimpleConnectionPool(1, 5, dsn=DB_URL)
12
+ print("βœ… DB pool initialized")
13
+
14
+ def close_db_pool():
15
+ global _pool
16
+ if _pool:
17
+ _pool.closeall()
18
+ print("βœ… DB pool closed")
19
+
20
+ def get_conn():
21
+ if not _pool:
22
+ raise RuntimeError("DB pool not initialized")
23
+ return _pool.getconn()
logger.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ import db_pool
3
+
4
+ def sync_log_to_db(texts, results):
5
+ values = [(t, r["blur"], r["score"]) for t, r in zip(texts, results)]
6
+ try:
7
+ conn = db_pool.get_conn()
8
+ with conn.cursor() as cur:
9
+ cur.executemany("INSERT INTO cases(text, blur, score) VALUES(%s, %s, %s)", values)
10
+ conn.commit()
11
+ print(f"βœ… Logged {len(values)} rows")
12
+ return True
13
+ except Exception as e:
14
+ print("❌ Logging failed:", e)
15
+ traceback.print_exc()
16
+ return False
model_loader.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
+
4
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
5
+ tokenizer = None
6
+ model = None
7
+
8
+ def load_model():
9
+ global tokenizer, model
10
+
11
+ model_id = "medoxz543/hate-speech"
12
+ print(f"πŸ”„ Loading model from Hugging Face Hub: {model_id}")
13
+ tokenizer = AutoTokenizer.from_pretrained(model_id)
14
+ model = AutoModelForSequenceClassification.from_pretrained(model_id).to(DEVICE)
15
+ model.eval()
16
+ print("βœ… Model ready")
requirements.txt CHANGED
@@ -2,4 +2,5 @@ fastapi
2
  uvicorn
3
  transformers
4
  torch
5
- emoji
 
 
2
  uvicorn
3
  transformers
4
  torch
5
+ emoji
6
+ psycopg2-binary
routes.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException
2
+ from pydantic import BaseModel
3
+ from datetime import datetime
4
+ import torch
5
+ import model_loader
6
+ import os
7
+
8
+ if os.getenv("DATABASE_URL"):
9
+ from logger import sync_log_to_db
10
+
11
+ router = APIRouter()
12
+ DEVICE = model_loader.DEVICE
13
+
14
+ def predict(texts: list[str]) -> list[float]:
15
+ enc = model_loader.tokenizer(texts, padding=True, truncation=True, max_length=70, return_tensors="pt")
16
+ enc = {k: v.to(DEVICE) for k, v in enc.items()}
17
+ with torch.no_grad():
18
+ probs = model_loader.model(**enc).logits.softmax(-1)[:, 1]
19
+ return probs.cpu().tolist()
20
+
21
+ class TextRequest(BaseModel):
22
+ texts: list[str]
23
+
24
+ @router.get("/")
25
+ def root():
26
+ return {"status": "🟒 HF Space is running"}
27
+
28
+ @router.post("/check-text")
29
+ def check_text(payload: TextRequest):
30
+ if not payload.texts:
31
+ raise HTTPException(400, "No texts provided")
32
+ scores = predict(payload.texts)
33
+ results = [{"blur": s >= 0.5, "score": round(s, 4)} for s in scores]
34
+ return {"timestamp": datetime.utcnow().isoformat(timespec="seconds"), "results": results}
35
+
36
+ @router.post("/log-results")
37
+ def log_results(payload: TextRequest):
38
+ if not os.getenv("DATABASE_URL"):
39
+ raise HTTPException(503, "Database is not configured")
40
+ scores = predict(payload.texts)
41
+ results = [{"blur": s >= 0.5, "score": round(s, 4)} for s in scores]
42
+ success = sync_log_to_db(payload.texts, results)
43
+ return {"logged": success}
server.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from fastapi import FastAPI
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from routes import router
5
+ from model_loader import load_model
6
+ import db_pool
7
+
8
+ app = FastAPI()
9
+
10
+ app.add_middleware(
11
+ CORSMiddleware,
12
+ allow_origins=["*"],
13
+ allow_credentials=True,
14
+ allow_methods=["*"],
15
+ allow_headers=["*"],
16
+ )
17
+
18
+ app.include_router(router)
19
+
20
+ @app.on_event("startup")
21
+ def startup():
22
+ print("πŸš€ Starting Hugging Face Space API")
23
+ load_model()
24
+ if os.getenv("DATABASE_URL"):
25
+ db_pool.init_db_pool()
26
+
27
+ @app.on_event("shutdown")
28
+ def shutdown():
29
+ db_pool.close_db_pool()