import joblib import os from fastapi import FastAPI, HTTPException from pydantic import BaseModel # ------------------------------------------------------------------ # App setup # ------------------------------------------------------------------ app = FastAPI( title="GitHub Spam Detector API", description="Predicts whether a GitHub comment is spam (1) or not spam (0). " "Trained on 100k+ real GitHub comments using TF-IDF + LinearSVC.", version="1.0.0", ) # ------------------------------------------------------------------ # Model loading (once at startup, never per-request) # ------------------------------------------------------------------ MODEL_PATH = os.getenv("MODEL_PATH", "spam_detector_model_cv.pkl") @app.on_event("startup") def load_model(): global model if not os.path.exists(MODEL_PATH): raise RuntimeError(f"Model file not found at: {MODEL_PATH}") model = joblib.load(MODEL_PATH) print(f"Model loaded successfully from {MODEL_PATH}") # ------------------------------------------------------------------ # Request / Response schemas # ------------------------------------------------------------------ class PredictRequest(BaseModel): text: str class Config: json_schema_extra = { "example": { "text": "Please fix the bug at line 56, it causes a null pointer exception." } } class PredictResponse(BaseModel): text: str prediction: int # 0 = Not Spam, 1 = Spam label: str # Human-readable label # ------------------------------------------------------------------ # Endpoints # ------------------------------------------------------------------ @app.get("/", tags=["Health"]) def root(): """Health check endpoint.""" return {"status": "ok", "message": "Spam Detector API is running."} @app.post("/predict", response_model=PredictResponse, tags=["Prediction"]) def predict(request: PredictRequest): """ Predict whether a piece of text is spam or not. - **text**: The comment / text to classify. Returns: - **prediction**: `0` (Not Spam) or `1` (Spam) - **label**: Human-readable string `"spam"` or `"not_spam"` """ if not request.text or not request.text.strip(): raise HTTPException(status_code=422, detail="Input text cannot be empty.") raw_pred = model.predict([request.text])[0] prediction = int(raw_pred) label = "spam" if prediction == 1 else "not_spam" return PredictResponse( text=request.text, prediction=prediction, label=label, )