Spaces:
Running
on
Zero
Running
on
Zero
File size: 7,218 Bytes
d6e219c f1948f2 2f6ac5d e032990 b54664e 6153eb8 0ff864f 1dbc865 e185e86 2f6ac5d 1dbc865 2f6ac5d 1dbc865 e032990 a9d4250 e185e86 1dbc865 e185e86 1dbc865 e185e86 2f6ac5d 1dbc865 2f6ac5d aeed86a e185e86 1dbc865 e185e86 d33c30b 1dbc865 d33c30b 1dbc865 d33c30b d315105 9ab7ab9 d315105 9ab7ab9 d315105 1e3558a 9ab7ab9 4472a1d d315105 b883fe8 1dbc865 d315105 1dbc865 cb6b46c d315105 1dbc865 d315105 cb6b46c d315105 ec5f81e 1dbc865 e032990 d315105 a28ef35 ab8c96f 1dbc865 d315105 1dbc865 d315105 1dbc865 ab8c96f 4292d1b 1dbc865 9ab7ab9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
import gradio as gr
import torch
import numpy as np
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
from transformers import RobertaForSequenceClassification, RobertaTokenizer
from motif_tagging import detect_motifs
import re
# --- Sentiment Model: T5-based Emotion Classifier ---
sentiment_tokenizer = AutoTokenizer.from_pretrained("mrm8488/t5-base-finetuned-emotion")
sentiment_model = AutoModelForSeq2SeqLM.from_pretrained("mrm8488/t5-base-finetuned-emotion")
EMOTION_TO_SENTIMENT = {
"joy": "supportive",
"love": "supportive",
"surprise": "supportive",
"neutral": "supportive",
"sadness": "undermining",
"anger": "undermining",
"fear": "undermining",
"disgust": "undermining",
"shame": "undermining",
"guilt": "undermining"
}
# --- Abuse Detection Model ---
model_name = "SamanthaStorm/autotrain-jlpi4-mllvp"
model = RobertaForSequenceClassification.from_pretrained(model_name, trust_remote_code=True)
tokenizer = RobertaTokenizer.from_pretrained(model_name, trust_remote_code=True)
LABELS = [
"blame shifting", "contradictory statements", "control", "dismissiveness",
"gaslighting", "guilt tripping", "insults", "obscure language",
"projection", "recovery phase", "threat"
]
THRESHOLDS = {
"blame shifting": 0.3,
"contradictory statements": 0.32,
"control": 0.48,
"dismissiveness": 0.45,
"gaslighting": 0.30,
"guilt tripping": 0.20,
"insults": 0.34,
"obscure language": 0.25,
"projection": 0.35,
"recovery phase": 0.25,
"threat": 0.25
}
PATTERN_WEIGHTS = {
"gaslighting": 1.3,
"control": 1.2,
"dismissiveness": 0.8,
"blame shifting": 0.8,
"contradictory statements": 0.75
}
EXPLANATIONS = {
"blame shifting": "Blame-shifting is when one person redirects responsibility onto someone else to avoid accountability.",
"contradictory statements": "Contradictory statements confuse the listener by flipping positions or denying previous claims.",
"control": "Control restricts another person’s autonomy through coercion, manipulation, or threats.",
"dismissiveness": "Dismissiveness is belittling or disregarding another person’s feelings, needs, or opinions.",
"gaslighting": "Gaslighting involves making someone question their own reality, memory, or perceptions.",
"guilt tripping": "Guilt-tripping uses guilt to manipulate someone’s actions or decisions.",
"insults": "Insults are derogatory or demeaning remarks meant to shame, belittle, or hurt someone.",
"obscure language": "Obscure language manipulates through complexity, vagueness, or superiority to confuse the other person.",
"projection": "Projection accuses someone else of the very behaviors or intentions the speaker is exhibiting.",
"recovery phase": "Recovery phase statements attempt to soothe or reset tension without acknowledging harm or change.",
"threat": "Threats use fear of harm (physical, emotional, or relational) to control or intimidate someone."
}
RISK_SNIPPETS = {
"low": (
"🟢 Risk Level: Low",
"The language patterns here do not strongly indicate abuse.",
"Continue to check in with yourself and notice how you feel in response to repeated patterns."
),
"moderate": (
"⚠️ Risk Level: Moderate to High",
"This language includes control, guilt, or reversal tactics.",
"These patterns often lead to emotional confusion and reduced self-trust. Document these messages or talk with someone safe."
),
"high": (
"🛑 Risk Level: High",
"Language includes threats or coercive control, which are strong indicators of escalation.",
"Consider creating a safety plan or contacting a support line. Trust your sense of unease."
)
}
def generate_risk_snippet(abuse_score, top_label):
if abuse_score >= 85:
risk_level = "high"
elif abuse_score >= 60:
risk_level = "moderate"
else:
risk_level = "low"
title, summary, advice = RISK_SNIPPETS[risk_level]
return f"\n\n{title}\n{summary} (Pattern: **{top_label}**)\n💡 {advice}"
# --- Escalation Quiz Questions & Weights ---
ESCALATION_QUESTIONS = [
("Partner has access to firearms or weapons", 4),
("Partner threatened to kill you", 3),
("Partner threatened you with a weapon", 3),
("Partner has ever choked you, even if you considered it consensual at the time", 4),
("Partner injured or threatened your pet(s)", 3),
("Partner has broken your things, punched or kicked walls, or thrown things ", 2),
("Partner forced or coerced you into unwanted sexual acts", 3),
("Partner threatened to take away your children", 2),
("Violence has increased in frequency or severity", 3),
("Partner monitors your calls/GPS/social media", 2)
]
def analyze_single_message(text, thresholds, motif_flags):
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
with torch.no_grad():
outputs = model(**inputs)
scores = torch.sigmoid(outputs.logits.squeeze(0)).numpy()
threshold_labels = [label for label, score in zip(LABELS, scores) if score > thresholds[label]]
top_patterns = sorted([(label, score) for label, score in zip(LABELS, scores)], key=lambda x: x[1], reverse=True)[:2]
return (np.mean([score for _, score in top_patterns]) * 100, threshold_labels, top_patterns, 0.0, {"label": "supportive"})
# --- Composite Analysis with Escalation Quiz ---
def analyze_composite(msg1, msg2, msg3, *answers_and_none):
responses = answers_and_none[:len(ESCALATION_QUESTIONS)]
none_selected = answers_and_none[-1]
if none_selected:
escalation_score = 0
else:
escalation_score = sum(w for (_, w), a in zip(ESCALATION_QUESTIONS, responses) if a)
if escalation_score >= 16:
escalation_level = "High"
elif escalation_score >= 8:
escalation_level = "Moderate"
else:
escalation_level = "Low"
thresholds = THRESHOLDS.copy()
messages = [msg1, msg2, msg3]
active = [m for m in messages if m.strip()]
if not active:
return "Please enter at least one message."
results = [analyze_single_message(m, thresholds, []) for m in active]
abuse_scores = [r[0] for r in results]
top_pattern = max({label for r in results for label in r[2]}, key=lambda l: abuse_scores[0])
composite_abuse = round(sum(abuse_scores)/len(abuse_scores),2)
out = f"Abuse Intensity: {composite_abuse}%\n"
out += f"Escalation Potential: {escalation_level} ({escalation_score}/{sum(w for _,w in ESCALATION_QUESTIONS)})"
out += generate_risk_snippet(composite_abuse, top_pattern)
return out
textbox_inputs = [
gr.Textbox(label="Message 1"),
gr.Textbox(label="Message 2"),
gr.Textbox(label="Message 3")
]
quiz_boxes = [gr.Checkbox(label=q) for q, _ in ESCALATION_QUESTIONS]
none_box = gr.Checkbox(label="None of the above")
iface = gr.Interface(
fn=analyze_composite,
inputs=textbox_inputs + quiz_boxes + [none_box],
outputs=gr.Textbox(label="Results"),
title="Abuse Pattern Detector + Escalation Quiz",
allow_flagging="manual"
)
if __name__ == "__main__":
iface.launch() |