Spaces:
Running on Zero
Running on Zero
| """ScamShield NLP — Hugging Face Space entry point. | |
| A pure-inference Gradio app exposing the trained calibrated Linear SVM. | |
| Two ways to use it: | |
| 1. Web UI — type/paste a message and click "Analyze". | |
| 2. HTTP API — the same function is exposed through the Gradio API. | |
| """ | |
| import gradio as gr | |
| import spaces | |
| from predictor import ScamPredictor | |
| # --------------------------------------------------------- | |
| # Model | |
| # --------------------------------------------------------- | |
| predictor = ScamPredictor() | |
| # --------------------------------------------------------- | |
| # Configuration | |
| # --------------------------------------------------------- | |
| MAX_MESSAGE_LENGTH = 20000 | |
| TITLE = "ScamShield NLP" | |
| DESCRIPTION = ( | |
| "Classifies a text message as **SCAM** or **LEGITIMATE** using a " | |
| "**calibrated Linear SVM** trained on the ScamShield corpus " | |
| "(TF-IDF features, n-grams 1–2). Returns a calibrated scam probability " | |
| "(confidence) and a risk level." | |
| ) | |
| # --------------------------------------------------------- | |
| # Example messages | |
| # --------------------------------------------------------- | |
| EXAMPLES = [ | |
| ["Congratulations! You have won a prize. Click this link now."], | |
| [ | |
| "Dear customer, your account will be locked. " | |
| "Verify now: http://bit.ly/fake" | |
| ], | |
| [ | |
| "URGENT: Your parcel could not be delivered. " | |
| "Pay $2 to reschedule: https://short.link/x" | |
| ], | |
| ["Hi, the meeting is moved to 4pm tomorrow. See you then."], | |
| [ | |
| "Your OTP for verification is 482913. " | |
| "Do not share it with anyone." | |
| ], | |
| [ | |
| "Reminder: your subscription renews on the 28th. " | |
| "Manage settings here." | |
| ], | |
| ] | |
| # --------------------------------------------------------- | |
| # Analyze function | |
| # --------------------------------------------------------- | |
| def analyze(message: str) -> dict: | |
| """Validate input, then run inference with detailed error logging.""" | |
| import traceback | |
| try: | |
| # --------------------------------------------- | |
| # Validate input | |
| # --------------------------------------------- | |
| if message is None or not str(message).strip(): | |
| raise gr.Error("Message cannot be empty.") | |
| message = str(message) | |
| if len(message) > MAX_MESSAGE_LENGTH: | |
| raise gr.Error( | |
| f"Message too long (max {MAX_MESSAGE_LENGTH} characters)." | |
| ) | |
| # --------------------------------------------- | |
| # Run model prediction | |
| # --------------------------------------------- | |
| print("=" * 60) | |
| print("ANALYZE REQUEST") | |
| print(f"Message: {message}") | |
| print("=" * 60) | |
| result = predictor.predict(message) | |
| # --------------------------------------------- | |
| # Success logging | |
| # --------------------------------------------- | |
| print("=" * 60) | |
| print(f"SUCCESS: {result}") | |
| print("=" * 60) | |
| return result | |
| except gr.Error: | |
| # Keep Gradio validation errors readable. | |
| raise | |
| except Exception as e: | |
| # --------------------------------------------- | |
| # Detailed diagnostic logging | |
| # --------------------------------------------- | |
| error_msg = ( | |
| f"{type(e).__name__}: {e}\n" | |
| f"{traceback.format_exc()}" | |
| ) | |
| print("=" * 60) | |
| print("ERROR DURING ANALYSIS") | |
| print(error_msg) | |
| print("=" * 60) | |
| # Show a shorter message to the UI while keeping | |
| # the complete traceback in Hugging Face logs. | |
| raise gr.Error( | |
| f"Analysis failed: {type(e).__name__}: {e}" | |
| ) | |
| # --------------------------------------------------------- | |
| # Gradio UI | |
| # --------------------------------------------------------- | |
| with gr.Blocks( | |
| title=TITLE, | |
| theme=gr.themes.Soft() | |
| ) as demo: | |
| # --------------------------------------------- | |
| # Header | |
| # --------------------------------------------- | |
| gr.Markdown(f"# {TITLE}") | |
| gr.Markdown(DESCRIPTION) | |
| # --------------------------------------------- | |
| # Main layout | |
| # --------------------------------------------- | |
| with gr.Row(): | |
| # ----------------------------------------- | |
| # Input | |
| # ----------------------------------------- | |
| with gr.Column(scale=3): | |
| message_input = gr.Textbox( | |
| label="Message", | |
| lines=5, | |
| placeholder=( | |
| "Paste the SMS, email or chat message " | |
| "to classify…" | |
| ), | |
| ) | |
| analyze_button = gr.Button( | |
| "Analyze", | |
| variant="primary" | |
| ) | |
| # ----------------------------------------- | |
| # Output | |
| # ----------------------------------------- | |
| with gr.Column(scale=2): | |
| result_output = gr.JSON( | |
| label="Result" | |
| ) | |
| # --------------------------------------------- | |
| # Analyze button | |
| # --------------------------------------------- | |
| analyze_button.click( | |
| analyze, | |
| inputs=message_input, | |
| outputs=result_output, | |
| api_name="predict", | |
| ) | |
| # --------------------------------------------- | |
| # Enter / Submit | |
| # --------------------------------------------- | |
| message_input.submit( | |
| analyze, | |
| inputs=message_input, | |
| outputs=result_output, | |
| ) | |
| # --------------------------------------------- | |
| # Examples + Model information | |
| # --------------------------------------------- | |
| gr.Markdown( | |
| """ | |
| ### Example messages | |
| Click an example to load it, then press **Analyze**. | |
| ### Model | |
| `CalibratedClassifierCV(LinearSVC)` over | |
| `TfidfVectorizer` (10,000 features, n-gram 1–2) | |
| **Held-out test set:** | |
| - Accuracy: **96.91%** | |
| - Precision: **96.64%** | |
| - Recall: **94.99%** | |
| - F1: **95.81%** | |
| """ | |
| ) | |
| # --------------------------------------------- | |
| # Example messages | |
| # --------------------------------------------- | |
| gr.Examples( | |
| examples=EXAMPLES, | |
| inputs=message_input, | |
| outputs=result_output, | |
| ) | |
| # --------------------------------------------------------- | |
| # Launch | |
| # --------------------------------------------------------- | |
| if __name__ == "__main__": | |
| demo.launch() |