Perth0603 commited on
Commit
9265bd4
·
verified ·
1 Parent(s): 0b9dff4
Files changed (3) hide show
  1. README.md +28 -12
  2. app.py +50 -0
  3. requirements.txt +5 -0
README.md CHANGED
@@ -1,12 +1,28 @@
1
- ---
2
- title: Phishwatch Proxy
3
- emoji: 📊
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 5.47.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Space - Phishing Text Classifier (FastAPI)
2
+
3
+ This Space exposes a minimal `/predict` endpoint for your MobileBERT phishing model so the Flutter app can call it reliably.
4
+
5
+ ## Files
6
+ - app.py - FastAPI app that loads the model and returns `{ label, score }`.
7
+ - requirements.txt - Python dependencies.
8
+
9
+ ## How to deploy
10
+ 1. Create a new Space on Hugging Face (type: FastAPI).
11
+ 2. Upload the contents of this `hf_space/` folder to the Space root.
12
+ 3. In Space Settings Variables, add:
13
+ - MODEL_ID = Perth0603/phishing-email-mobilebert
14
+ 4. Wait for the Space to build and become green. Test:
15
+ - GET `/` should return `{ status: ok, model: ... }`
16
+ - POST `/predict` with `{ "inputs": "Win an iPhone! Click here" }`
17
+
18
+ ## Flutter app config
19
+ Set the Space URL in your env file so the app targets the Space instead of the Hosted Inference API:
20
+
21
+ ```
22
+ {"HF_SPACE_URL":"https://<your-space>.hf.space"}
23
+ ```
24
+
25
+ Run the app:
26
+ ```
27
+ flutter run --dart-define-from-file=hf.env.json
28
+ ```
app.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from pydantic import BaseModel
3
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
4
+ import torch
5
+ import os
6
+
7
+
8
+ MODEL_ID = os.environ.get("MODEL_ID", "Perth0603/phishing-email-mobilebert")
9
+
10
+ app = FastAPI(title="Phishing Text Classifier", version="1.0.0")
11
+
12
+
13
+ class PredictPayload(BaseModel):
14
+ inputs: str
15
+
16
+
17
+ # Lazy singletons for model/tokenizer
18
+ _tokenizer = None
19
+ _model = None
20
+
21
+
22
+ def _load_model():
23
+ global _tokenizer, _model
24
+ if _tokenizer is None or _model is None:
25
+ _tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
26
+ _model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
27
+ # Warm-up
28
+ with torch.no_grad():
29
+ _ = _model(**_tokenizer(["warm up"], return_tensors="pt")).logits
30
+
31
+
32
+ @app.get("/")
33
+ def root():
34
+ return {"status": "ok", "model": MODEL_ID}
35
+
36
+
37
+ @app.post("/predict")
38
+ def predict(payload: PredictPayload):
39
+ _load_model()
40
+ with torch.no_grad():
41
+ logits = _model(**_tokenizer([payload.inputs], return_tensors="pt")).logits
42
+ probs = torch.softmax(logits, dim=-1)[0]
43
+ score, idx = torch.max(probs, dim=0)
44
+
45
+ # Map common ids to labels (kept generic; your config also has these)
46
+ id2label = {0: "LEGIT", 1: "PHISH"}
47
+ label = id2label.get(int(idx), str(int(idx)))
48
+ return {"label": label, "score": float(score)}
49
+
50
+
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ fastapi==0.115.0
2
+ uvicorn==0.30.6
3
+ transformers==4.46.3
4
+ torch>=2.0.0
5
+