SamanthaStorm commited on
Commit
623a77f
·
verified ·
1 Parent(s): 9f4751d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +20 -47
app.py CHANGED
@@ -98,7 +98,6 @@ def generate_risk_snippet(abuse_score, top_label):
98
  title, summary, advice = RISK_SNIPPETS[risk_level]
99
  return f"\n\n{title}\n{summary} (Pattern: **{top_label}**)\n💡 {advice}"
100
 
101
- # --- DARVO Detection ---
102
  DARVO_PATTERNS = {
103
  "blame shifting", "projection", "dismissiveness", "guilt tripping", "contradictory statements"
104
  }
@@ -109,20 +108,15 @@ DARVO_MOTIFS = [
109
  ]
110
 
111
  def detect_contradiction(message):
112
- contradiction_flag = False
113
  contradiction_phrases = [
114
  (r"\b(i love you).{0,15}(i hate you|you ruin everything)", re.IGNORECASE),
115
  (r"\b(i’m sorry).{0,15}(but you|if you hadn’t)", re.IGNORECASE),
116
  (r"\b(i’m trying).{0,15}(you never|why do you)", re.IGNORECASE),
117
  (r"\b(do what you want).{0,15}(you’ll regret it|i always give everything)", re.IGNORECASE),
118
  (r"\b(i don’t care).{0,15}(you never think of me)", re.IGNORECASE),
119
- (r"\b(i guess i’m just).{0,15}(the bad guy|worthless|never enough)", re.IGNORECASE),
120
  ]
121
- for pattern, flags in contradiction_phrases:
122
- if re.search(pattern, message, flags):
123
- contradiction_flag = True
124
- break
125
- return contradiction_flag
126
 
127
  def calculate_darvo_score(patterns, sentiment_before, sentiment_after, motifs_found, contradiction_flag=False):
128
  pattern_hits = len([p.lower() for p in patterns if p.lower() in DARVO_PATTERNS])
@@ -139,7 +133,6 @@ def calculate_darvo_score(patterns, sentiment_before, sentiment_after, motifs_fo
139
  )
140
  return round(min(darvo_score, 1.0), 3)
141
 
142
- # --- Escalation Quiz Questions & Weights ---
143
  ESCALATION_QUESTIONS = [
144
  ("Partner has access to firearms or weapons", 4),
145
  ("Partner threatened to kill you", 3),
@@ -155,20 +148,12 @@ ESCALATION_QUESTIONS = [
155
 
156
  def analyze_single_message(text, thresholds, motif_flags):
157
  motif_hits, matched_phrases = detect_motifs(text)
158
- sentiment = {"label": "undermining"} # fallback in case sentiment fails
159
- try:
160
- input_ids = sentiment_tokenizer(f"emotion: {text}", return_tensors="pt").input_ids
161
- with torch.no_grad():
162
- outputs = sentiment_model.generate(input_ids)
163
- emotion = sentiment_tokenizer.decode(outputs[0], skip_special_tokens=True).strip().lower()
164
- sentiment = {
165
- "label": EMOTION_TO_SENTIMENT.get(emotion, "undermining"),
166
- "emotion": emotion
167
- }
168
- except:
169
- sentiment["emotion"] = "unknown"
170
-
171
- sentiment_score = 0.5 if sentiment["label"] == "undermining" else 0.0
172
  contradiction_flag = detect_contradiction(text)
173
  motifs = [phrase for _, phrase in matched_phrases]
174
 
@@ -177,7 +162,7 @@ def analyze_single_message(text, thresholds, motif_flags):
177
  outputs = model(**inputs)
178
  scores = torch.sigmoid(outputs.logits.squeeze(0)).numpy()
179
 
180
- labels = [label for label, score in zip(LABELS, scores) if score > thresholds[label]]
181
  top_patterns = sorted([(label, score) for label, score in zip(LABELS, scores)], key=lambda x: x[1], reverse=True)[:2]
182
  pattern_labels = [label for label, _ in top_patterns]
183
 
@@ -185,47 +170,36 @@ def analyze_single_message(text, thresholds, motif_flags):
185
 
186
  return (
187
  np.mean([score for _, score in top_patterns]) * 100,
188
- labels,
189
  top_patterns,
190
  darvo_score,
191
- sentiment
192
  )
193
 
194
- # --- Composite Analysis with Escalation Quiz ---
195
  def analyze_composite(msg1, msg2, msg3, *answers_and_none):
196
  responses = answers_and_none[:len(ESCALATION_QUESTIONS)]
197
  none_selected = answers_and_none[-1]
198
- if none_selected:
199
- escalation_score = 0
200
- else:
201
- escalation_score = sum(w for (_, w), a in zip(ESCALATION_QUESTIONS, responses) if a)
202
- if escalation_score >= 16:
203
- escalation_level = "High"
204
- elif escalation_score >= 8:
205
- escalation_level = "Moderate"
206
- else:
207
- escalation_level = "Low"
208
 
209
- thresholds = THRESHOLDS.copy()
210
  messages = [msg1, msg2, msg3]
211
  active = [m for m in messages if m.strip()]
212
  if not active:
213
  return "Please enter at least one message."
214
 
215
- results = [analyze_single_message(m, thresholds, []) for m in active]
216
  abuse_scores = [r[0] for r in results]
 
217
  top_pattern = max({label for r in results for label in r[2]}, key=lambda l: abuse_scores[0])
218
- composite_abuse = round(sum(abuse_scores)/len(abuse_scores),2)
 
219
 
220
  out = f"Abuse Intensity: {composite_abuse}%\n"
221
  out += f"Escalation Potential: {escalation_level} ({escalation_score}/{sum(w for _,w in ESCALATION_QUESTIONS)})"
222
  out += generate_risk_snippet(composite_abuse, top_pattern)
223
-
224
- avg_darvo = round(sum([r[3] for r in results]) / len(results), 3)
225
  if avg_darvo > 0.25:
226
- darvo_descriptor = "moderate" if avg_darvo < 0.65 else "high"
227
- out += f"\n\nDARVO Score: {avg_darvo} → This indicates a **{darvo_descriptor} likelihood** of narrative reversal (DARVO), where the speaker may be denying, attacking, or reversing blame."
228
-
229
  return out
230
 
231
  textbox_inputs = [
@@ -233,7 +207,6 @@ textbox_inputs = [
233
  gr.Textbox(label="Message 2"),
234
  gr.Textbox(label="Message 3")
235
  ]
236
-
237
  quiz_boxes = [gr.Checkbox(label=q) for q, _ in ESCALATION_QUESTIONS]
238
  none_box = gr.Checkbox(label="None of the above")
239
 
@@ -246,4 +219,4 @@ iface = gr.Interface(
246
  )
247
 
248
  if __name__ == "__main__":
249
- iface.launch()
 
98
  title, summary, advice = RISK_SNIPPETS[risk_level]
99
  return f"\n\n{title}\n{summary} (Pattern: **{top_label}**)\n💡 {advice}"
100
 
 
101
  DARVO_PATTERNS = {
102
  "blame shifting", "projection", "dismissiveness", "guilt tripping", "contradictory statements"
103
  }
 
108
  ]
109
 
110
  def detect_contradiction(message):
 
111
  contradiction_phrases = [
112
  (r"\b(i love you).{0,15}(i hate you|you ruin everything)", re.IGNORECASE),
113
  (r"\b(i’m sorry).{0,15}(but you|if you hadn’t)", re.IGNORECASE),
114
  (r"\b(i’m trying).{0,15}(you never|why do you)", re.IGNORECASE),
115
  (r"\b(do what you want).{0,15}(you’ll regret it|i always give everything)", re.IGNORECASE),
116
  (r"\b(i don’t care).{0,15}(you never think of me)", re.IGNORECASE),
117
+ (r"\b(i guess i’m just).{0,15}(the bad guy|worthless|never enough)", re.IGNORECASE)
118
  ]
119
+ return any(re.search(pattern, message, flags) for pattern, flags in contradiction_phrases)
 
 
 
 
120
 
121
  def calculate_darvo_score(patterns, sentiment_before, sentiment_after, motifs_found, contradiction_flag=False):
122
  pattern_hits = len([p.lower() for p in patterns if p.lower() in DARVO_PATTERNS])
 
133
  )
134
  return round(min(darvo_score, 1.0), 3)
135
 
 
136
  ESCALATION_QUESTIONS = [
137
  ("Partner has access to firearms or weapons", 4),
138
  ("Partner threatened to kill you", 3),
 
148
 
149
  def analyze_single_message(text, thresholds, motif_flags):
150
  motif_hits, matched_phrases = detect_motifs(text)
151
+ input_ids = sentiment_tokenizer(f"emotion: {text}", return_tensors="pt").input_ids
152
+ with torch.no_grad():
153
+ outputs = sentiment_model.generate(input_ids)
154
+ emotion = sentiment_tokenizer.decode(outputs[0], skip_special_tokens=True).strip().lower()
155
+ sentiment = EMOTION_TO_SENTIMENT.get(emotion, "undermining")
156
+ sentiment_score = 0.5 if sentiment == "undermining" else 0.0
 
 
 
 
 
 
 
 
157
  contradiction_flag = detect_contradiction(text)
158
  motifs = [phrase for _, phrase in matched_phrases]
159
 
 
162
  outputs = model(**inputs)
163
  scores = torch.sigmoid(outputs.logits.squeeze(0)).numpy()
164
 
165
+ threshold_labels = [label for label, score in zip(LABELS, scores) if score > thresholds[label]]
166
  top_patterns = sorted([(label, score) for label, score in zip(LABELS, scores)], key=lambda x: x[1], reverse=True)[:2]
167
  pattern_labels = [label for label, _ in top_patterns]
168
 
 
170
 
171
  return (
172
  np.mean([score for _, score in top_patterns]) * 100,
173
+ threshold_labels,
174
  top_patterns,
175
  darvo_score,
176
+ {"label": sentiment, "emotion": emotion}
177
  )
178
 
 
179
  def analyze_composite(msg1, msg2, msg3, *answers_and_none):
180
  responses = answers_and_none[:len(ESCALATION_QUESTIONS)]
181
  none_selected = answers_and_none[-1]
182
+ escalation_score = 0 if none_selected else sum(w for (_, w), a in zip(ESCALATION_QUESTIONS, responses) if a)
183
+ escalation_level = "High" if escalation_score >= 16 else "Moderate" if escalation_score >= 8 else "Low"
 
 
 
 
 
 
 
 
184
 
 
185
  messages = [msg1, msg2, msg3]
186
  active = [m for m in messages if m.strip()]
187
  if not active:
188
  return "Please enter at least one message."
189
 
190
+ results = [analyze_single_message(m, THRESHOLDS.copy(), []) for m in active]
191
  abuse_scores = [r[0] for r in results]
192
+ darvo_scores = [r[3] for r in results]
193
  top_pattern = max({label for r in results for label in r[2]}, key=lambda l: abuse_scores[0])
194
+ composite_abuse = round(sum(abuse_scores)/len(abuse_scores), 2)
195
+ avg_darvo = round(sum(darvo_scores)/len(darvo_scores), 3)
196
 
197
  out = f"Abuse Intensity: {composite_abuse}%\n"
198
  out += f"Escalation Potential: {escalation_level} ({escalation_score}/{sum(w for _,w in ESCALATION_QUESTIONS)})"
199
  out += generate_risk_snippet(composite_abuse, top_pattern)
 
 
200
  if avg_darvo > 0.25:
201
+ level = "moderate" if avg_darvo < 0.65 else "high"
202
+ out += f"\n\nDARVO Score: {avg_darvo} → This indicates a **{level} likelihood** of narrative reversal (DARVO), where the speaker may be denying, attacking, or reversing blame."
 
203
  return out
204
 
205
  textbox_inputs = [
 
207
  gr.Textbox(label="Message 2"),
208
  gr.Textbox(label="Message 3")
209
  ]
 
210
  quiz_boxes = [gr.Checkbox(label=q) for q, _ in ESCALATION_QUESTIONS]
211
  none_box = gr.Checkbox(label="None of the above")
212
 
 
219
  )
220
 
221
  if __name__ == "__main__":
222
+ iface.launch()