import gradio as gr import torch from transformers import AutoTokenizer, AutoModelForSequenceClassification MODEL_NAME = "Fatimasane26/distilbert-toxic-jigsaw" MAX_LENGTH = 128 THRESHOLD = 0.5 CATEGORIES = [ ("Toxic", "☠️", "#f43f5e"), ("Severe Toxic", "πŸ’€", "#e11d48"), ("Obscene", "🀬", "#fb923c"), ("Threat", "βš”οΈ", "#a78bfa"), ("Insult", "😀", "#facc15"), ("Identity Hate", "🎯", "#f472b6"), ] print("⏳ Loading model...") tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME) model.eval() device = "cuda" if torch.cuda.is_available() else "cpu" model.to(device) print(f"βœ… Model ready on {device}") def predict(text): if not text or not text.strip(): return "", "", build_bars(None) inputs = tokenizer( text, return_tensors="pt", truncation=True, max_length=MAX_LENGTH, padding=True, return_token_type_ids=False ).to(device) with torch.no_grad(): probs = torch.sigmoid(model(**inputs).logits).cpu().numpy()[0] detected = [name for (name, _, __), p in zip(CATEGORIES, probs) if p > THRESHOLD] max_score = float(max(probs)) if max_score < 0.2: verdict = "βœ… Clean" confidence = f"{int(max_score*100)}% β€” No toxicity detected" elif max_score < THRESHOLD: verdict = "⚠️ Low Risk" confidence = f"{int(max_score*100)}% β€” Potentially sensitive" else: cats = ", ".join(detected) if detected else "Unknown" verdict = "🚨 Toxic" confidence = f"{int(max_score*100)}% β€” {cats}" return verdict, confidence, build_bars(probs) def build_bars(probs): if probs is None: return """
πŸ“Š Probabilities will appear here
""" rows = "" for (name, emoji, color), score in zip(CATEGORIES, probs): pct = int(score * 100) width = max(pct, 1) bold = "font-weight:700;" if score > THRESHOLD else "opacity:0.6;" badge = f"!" if score > THRESHOLD else "" rows += f"""
{emoji} {name}{badge} {pct}%
""" return f"
{rows}
" CSS = """ * { box-sizing: border-box; } body, .gradio-container { background: #0a0f1e !important; font-family: system-ui, sans-serif; } .gradio-container { max-width: 960px !important; margin: 0 auto !important; padding: 0 16px !important; } footer { display: none !important; } /* Card style */ .card { background: #0f172a !important; border: 1px solid #1e293b !important; border-radius: 14px !important; padding: 0 !important; overflow: hidden !important; } /* Label headers like the reference */ .card-label { background: linear-gradient(135deg, #6d28d9, #0ea5e9); color: white; font-weight: 700; font-size: 13px; padding: 8px 16px; border-radius: 999px; display: inline-block; margin-bottom: 12px; letter-spacing: 0.3px; } /* Inputs */ textarea, input[type=text] { background: #1e293b !important; border: 1px solid #334155 !important; border-radius: 10px !important; color: #e2e8f0 !important; font-size: 15px !important; } textarea:focus { border-color: #6d28d9 !important; box-shadow: 0 0 0 2px #6d28d920 !important; } /* Buttons */ button.primary { background: linear-gradient(135deg, #6d28d9, #0ea5e9) !important; border: none !important; border-radius: 10px !important; color: white !important; font-weight: 700 !important; font-size: 14px !important; padding: 10px 0 !important; transition: opacity 0.2s !important; } button.primary:hover { opacity: 0.85 !important; } button.secondary { background: #1e293b !important; border: 1px solid #334155 !important; border-radius: 10px !important; color: #94a3b8 !important; font-weight: 600 !important; } /* Output text boxes */ .output-text input, .output-text textarea { background: #1e293b !important; border: 1px solid #334155 !important; color: #e2e8f0 !important; font-weight: 600 !important; border-radius: 10px !important; } /* Examples */ .examples-holder { background: transparent !important; } table.examples { background: transparent !important; } table.examples td { color: #64748b !important; font-size: 12px !important; } table.examples tr:hover td { color: #e2e8f0 !important; background: #1e293b !important; } """ EXAMPLES = [ "You are such an idiot, I hate you so much!", "I will find you and destroy everything you love.", "Thank you for your help, really appreciate it!", "People like you should not be allowed here.", "Great discussion, learned a lot today!", "I'll make sure you regret this, I promise.", ] with gr.Blocks(css=CSS, title="πŸ›‘οΈ Toxic Comment Classifier") as demo: # ── HEADER ────────────────────────────────────────────────────── gr.HTML("""

πŸ›‘οΈ Toxic Comment Classifier

Analyze any text comment and detect toxicity across 6 categories in real time.

πŸ€— DistilBERT πŸ“¦ 223K Comments 🏷️ 6 Categories ⚑ Real-time
""") # ── MAIN LAYOUT ───────────────────────────────────────────────── with gr.Row(equal_height=False): # LEFT β€” Input with gr.Column(scale=5): gr.HTML("
πŸ’¬ Comment to Analyze
") text_input = gr.Textbox( placeholder="Type or paste a comment here…", lines=6, show_label=False, container=False ) with gr.Row(): btn = gr.Button("πŸ” Analyze Now", variant="primary", scale=3) gr.ClearButton([text_input], value="βœ• Clear", scale=1) gr.HTML("
β€” Try an example β€”
") gr.Examples( examples=[[e] for e in EXAMPLES], inputs=text_input, label="" ) # RIGHT β€” Output with gr.Column(scale=5): gr.HTML("
🎯 Predicted Verdict
") verdict_out = gr.Textbox( show_label=False, interactive=False, placeholder="β€”", container=False, elem_classes=["output-text"] ) gr.HTML("
πŸ“Š Confidence
") conf_out = gr.Textbox( show_label=False, interactive=False, placeholder="β€”", container=False, elem_classes=["output-text"] ) gr.HTML("
πŸ“ˆ Category Probabilities
") bars_out = gr.HTML("""
πŸ“Š Probabilities will appear here
""") # ── FOOTER ────────────────────────────────────────────────────── gr.HTML("""
NLP Final Project  Β·  Built with HuggingFace Transformers & Gradio
""") btn.click(fn=predict, inputs=text_input, outputs=[verdict_out, conf_out, bars_out]) text_input.submit(fn=predict, inputs=text_input, outputs=[verdict_out, conf_out, bars_out]) if __name__ == "__main__": demo.launch()