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()