import gradio as gr import joblib import numpy as np import re # ---------------------------- # Load the trained model # ---------------------------- model = joblib.load("spam_model.joblib") # ---------------------------- # Text normalization (optional but helps) # ---------------------------- _url = re.compile(r'https?://\S+|www\.\S+') _email = re.compile(r'\b[\w\.-]+@[\w\.-]+\.\w+\b') _num = re.compile(r'\b\d[\d,.\-:/]*\b') def normalize_text(t: str) -> str: t = t.strip().lower() t = _url.sub(' __url__ ', t) t = _email.sub(' __email__ ', t) t = _num.sub(' __number__ ', t) return t # ---------------------------- # Prediction function with confidence # ---------------------------- def predict_with_confidence(text: str): text_norm = normalize_text(text) _input = [text_norm if text_norm else text] pred = int(model.predict(_input)[0]) if hasattr(model, "predict_proba"): proba = float(model.predict_proba(_input)[0][1]) # spam prob else: score = float(model.decision_function(_input)[0]) proba = 1 / (1 + np.exp(-score)) # sigmoid label = "SPAM 🚨" if pred == 1 else "HAM ✅" return label, proba # ---------------------------- # Custom CSS # ---------------------------- CUSTOM_CSS = """ :root { --brand: #0ea5e9; --brand-2: #0284c7; --success: #22c55e; --danger: #ef4444; --card-bg: #0b1220; --ink: #e5e7eb; } .gradio-container {max-width: 2100px !important;} #app-card { background: linear-gradient(135deg, #0b1220 0%, #0f172a 100%); color: var(--ink); border-radius: 18px; padding: 28px; box-shadow: 0 10px 30px rgba(0,0,0,0.35); border: 1px solid rgba(255,255,255,0.08); } #title {font-size: 28px;font-weight: 800;letter-spacing: 0.2px;} .subtitle {opacity: 0.85;margin-top: 2px;font-size: 14px;} .badge { display: inline-block;padding: 4px 10px;border-radius: 999px; background: linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 100%); color: white;font-weight: 600;font-size: 12px; } #predict-btn > button { background: linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 100%) !important; border: none !important;color: white !important;font-weight: 700 !important; border-radius: 10px !important; } #clear-btn > button { background: transparent !important;border: 1px solid rgba(255,255,255,0.15) !important; color: var(--ink) !important;font-weight: 600 !important;border-radius: 10px !important; } .result-card { border-radius: 14px;padding: 14px 16px;border: 1px solid rgba(255,255,255,0.08); display: flex;align-items: center;gap: 10px; } .result-label {font-size: 18px;font-weight: 800;} .confidence-wrap {display: flex;align-items: center;gap: 10px;} .confidence-bar { height: 10px;border-radius: 999px;flex: 1; background: linear-gradient(90deg, var(--danger), var(--success)); position: relative;overflow: hidden; } .confidence-fill { position: absolute;top: 0;left: 0;bottom: 0; background: rgba(255,255,255,0.9); mix-blend-mode: overlay; } .footer {opacity: 0.7;font-size: 12px;margin-top: 8px;} """ # ---------------------------- # UI Prediction wrapper # ---------------------------- def ui_predict(msg): label, proba = predict_with_confidence(msg) pct = int(round(proba * 100)) color = "var(--danger)" if "SPAM" in label else "var(--success)" styled_label = f"{label}" html = f"""