quantumbit commited on
Commit
a70328e
·
verified ·
1 Parent(s): cee18ce

🚀 Deploy via GitHub Actions

Browse files
Files changed (6) hide show
  1. .gitignore +1 -0
  2. Dockerfile +27 -0
  3. README.md +117 -5
  4. main.py +81 -0
  5. requirements.txt +6 -0
  6. spam_detector_model_cv.pkl +3 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ dataset.csv
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a slim official Python image
2
+ FROM python:3.12-slim
3
+
4
+ # Hugging Face Spaces runs on port 7860
5
+ EXPOSE 7860
6
+
7
+ # Create a non-root user for security
8
+ RUN useradd -m -u 1000 appuser
9
+
10
+ WORKDIR /app
11
+
12
+ # Copy dependency list first (better layer caching)
13
+ COPY requirements.txt .
14
+
15
+ # Install dependencies
16
+ RUN pip install --no-cache-dir -r requirements.txt
17
+
18
+ # Copy the rest of the application files
19
+ COPY . .
20
+
21
+ # Set correct ownership
22
+ RUN chown -R appuser:appuser /app
23
+
24
+ USER appuser
25
+
26
+ # Start the FastAPI app via Uvicorn on port 7860
27
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,122 @@
1
  ---
2
- title: Gitspam Detect Api
3
- emoji: 🌍
4
- colorFrom: purple
5
- colorTo: gray
6
  sdk: docker
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: GitHub Spam Detector API
3
+ emoji: 🛡️
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: docker
7
  pinned: false
8
+ license: mit
9
+ app_port: 7860
10
  ---
11
 
12
+ # 🛡️ GitHub Spam Detector API
13
+
14
+ A production-ready REST API to classify GitHub comments as **Spam** or **Not Spam** in real time.
15
+
16
+ Built with **FastAPI** and deployed via **Docker** on Hugging Face Spaces.
17
+
18
+ ---
19
+
20
+ ## 📌 Model Details
21
+
22
+ | Property | Value |
23
+ |---|---|
24
+ | **Training Data** | 100,000+ real GitHub issue & PR comments |
25
+ | **Feature Extraction** | TF-IDF (unigrams + bigrams, stop-words removed) |
26
+ | **Classifier** | LinearSVC (tuned via GridSearchCV) |
27
+ | **Test Accuracy** | 99% |
28
+ | **Test F1-Score** | 0.99 (both classes) |
29
+ | **AUC-ROC** | 1.00 |
30
+
31
+ ---
32
+
33
+ ## 🚀 API Endpoints
34
+
35
+ ### `GET /`
36
+ Health check — confirms the server is running.
37
+
38
+ **Response:**
39
+ ```json
40
+ {
41
+ "status": "ok",
42
+ "message": "Spam Detector API is running."
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ### `POST /predict`
49
+ Classify a piece of text as spam or not spam.
50
+
51
+ **Request Body:**
52
+ ```json
53
+ {
54
+ "text": "Please fix the bug at line 56, it causes a null pointer exception."
55
+ }
56
+ ```
57
+
58
+ **Response:**
59
+ ```json
60
+ {
61
+ "text": "Please fix the bug at line 56, it causes a null pointer exception.",
62
+ "prediction": 0,
63
+ "label": "not_spam"
64
+ }
65
+ ```
66
+
67
+ | Field | Type | Description |
68
+ |---|---|---|
69
+ | `prediction` | `int` | `0` = Not Spam, `1` = Spam |
70
+ | `label` | `string` | `"spam"` or `"not_spam"` |
71
+
72
+ ---
73
+
74
+ ## 🧪 Try it Out
75
+
76
+ Once the Space is running, visit the interactive Swagger docs at:
77
+
78
+ ```
79
+ https://<your-username>-github-spam-detector-api.hf.space/docs
80
+ ```
81
+
82
+ Or use `curl`:
83
+ ```bash
84
+ curl -X POST "https://<your-username>-github-spam-detector-api.hf.space/predict" \
85
+ -H "Content-Type: application/json" \
86
+ -d '{"text": "subscribe me if u love eminem"}'
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 🛠️ Run Locally
92
+
93
+ ```bash
94
+ # Install dependencies
95
+ pip install -r requirements.txt
96
+
97
+ # Start the server
98
+ uvicorn main:app --host 0.0.0.0 --port 7860 --reload
99
+ ```
100
+
101
+ Then visit: [http://localhost:7860/docs](http://localhost:7860/docs)
102
+
103
+ ---
104
+
105
+ ## 📦 Tech Stack
106
+ - **Python 3.12**
107
+ - **FastAPI** — REST API framework
108
+ - **scikit-learn** — TF-IDF + LinearSVC model
109
+ - **Uvicorn** — ASGI server
110
+ - **Docker** — containerized deployment
111
+
112
+ ---
113
+
114
+ ## ⚠️ Known Limitations
115
+
116
+ - Trained exclusively on GitHub comment data. May underperform on spam from other domains (e.g., YouTube, email).
117
+ - Context-blind to "Markdown camouflage" — wrapping spam text inside ` ```diff ` code blocks may fool the model as the TF-IDF vectorizer weighs the code-block tokens heavily toward non-spam.
118
+ - For multilingual or highly obfuscated spam, consider upgrading to a transformer-based model (e.g., DistilBERT).
119
+
120
+ ---
121
+
122
+ *Part of the H2-GitGriffin Hackathon Project.*
main.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import joblib
2
+ import os
3
+ from fastapi import FastAPI, HTTPException
4
+ from pydantic import BaseModel
5
+
6
+ # ------------------------------------------------------------------
7
+ # App setup
8
+ # ------------------------------------------------------------------
9
+ app = FastAPI(
10
+ title="GitHub Spam Detector API",
11
+ description="Predicts whether a GitHub comment is spam (1) or not spam (0). "
12
+ "Trained on 100k+ real GitHub comments using TF-IDF + LinearSVC.",
13
+ version="1.0.0",
14
+ )
15
+
16
+ # ------------------------------------------------------------------
17
+ # Model loading (once at startup, never per-request)
18
+ # ------------------------------------------------------------------
19
+ MODEL_PATH = os.getenv("MODEL_PATH", "spam_detector_model_cv.pkl")
20
+
21
+ @app.on_event("startup")
22
+ def load_model():
23
+ global model
24
+ if not os.path.exists(MODEL_PATH):
25
+ raise RuntimeError(f"Model file not found at: {MODEL_PATH}")
26
+ model = joblib.load(MODEL_PATH)
27
+ print(f"Model loaded successfully from {MODEL_PATH}")
28
+
29
+
30
+ # ------------------------------------------------------------------
31
+ # Request / Response schemas
32
+ # ------------------------------------------------------------------
33
+ class PredictRequest(BaseModel):
34
+ text: str
35
+
36
+ class Config:
37
+ json_schema_extra = {
38
+ "example": {
39
+ "text": "Please fix the bug at line 56, it causes a null pointer exception."
40
+ }
41
+ }
42
+
43
+
44
+ class PredictResponse(BaseModel):
45
+ text: str
46
+ prediction: int # 0 = Not Spam, 1 = Spam
47
+ label: str # Human-readable label
48
+
49
+
50
+ # ------------------------------------------------------------------
51
+ # Endpoints
52
+ # ------------------------------------------------------------------
53
+ @app.get("/", tags=["Health"])
54
+ def root():
55
+ """Health check endpoint."""
56
+ return {"status": "ok", "message": "Spam Detector API is running."}
57
+
58
+
59
+ @app.post("/predict", response_model=PredictResponse, tags=["Prediction"])
60
+ def predict(request: PredictRequest):
61
+ """
62
+ Predict whether a piece of text is spam or not.
63
+
64
+ - **text**: The comment / text to classify.
65
+
66
+ Returns:
67
+ - **prediction**: `0` (Not Spam) or `1` (Spam)
68
+ - **label**: Human-readable string `"spam"` or `"not_spam"`
69
+ """
70
+ if not request.text or not request.text.strip():
71
+ raise HTTPException(status_code=422, detail="Input text cannot be empty.")
72
+
73
+ raw_pred = model.predict([request.text])[0]
74
+ prediction = int(raw_pred)
75
+ label = "spam" if prediction == 1 else "not_spam"
76
+
77
+ return PredictResponse(
78
+ text=request.text,
79
+ prediction=prediction,
80
+ label=label,
81
+ )
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi==0.111.0
2
+ uvicorn[standard]==0.29.0
3
+ scikit-learn==1.4.2
4
+ joblib==1.4.2
5
+ pydantic==2.7.1
6
+ numpy==1.26.4
spam_detector_model_cv.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ad1b0399122c1c3c6b8514a1cc43a60b732a8176aaa9b334f25c1c54009760e
3
+ size 83350887