SamanthaStorm commited on
Commit
8169a27
·
verified ·
1 Parent(s): 65d0b2c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -65
app.py CHANGED
@@ -346,34 +346,21 @@ def get_risk_stage(patterns, sentiment):
346
  elif sentiment == "supportive" and any(p in patterns for p in ["projection", "dismissiveness"]):
347
  return 4
348
  return 1
349
- # Use pattern scores from the top-scoring message for risk snippet
350
- index_top_msg = abuse_scores.index(max(abuse_scores))
351
- pattern_scores_for_snippet = {label: score for label, score in results[index_top_msg][0][2]}
352
- def generate_risk_snippet(abuse_score, top_label, escalation_score, stage, pattern_scores):
353
- aggression_score = pattern_scores.get("aggression", 0)
354
 
355
- # Updated risk level logic
356
  if abuse_score >= 85 or escalation_score >= 16:
357
  risk_level = "high"
358
- elif abuse_score >= 60 or escalation_score >= 8 or aggression_score >= 0.75:
359
  risk_level = "moderate"
360
  elif stage == 2 and abuse_score >= 40:
361
- risk_level = "moderate"
362
  else:
363
  risk_level = "low"
364
-
365
- # Narrative
366
- base = f"\n\n🛑 Risk Level: {risk_level.capitalize()}\n"
367
- base += f"This message shows strong indicators of **{top_label}**. "
368
-
369
- if risk_level == "high":
370
- base += "The language may reflect patterns of emotional control, even when expressed in soft or caring terms.\n"
371
- elif risk_level == "moderate":
372
- base += "There are signs of emotional pressure or verbal aggression that may escalate if repeated.\n"
373
  else:
374
- base += "The message does not strongly indicate abuse, but it's important to monitor for patterns.\n"
375
-
376
- return risk_level, base
377
 
378
  WHY_FLAGGED = {
379
  "control": "This message may reflect efforts to restrict someone’s autonomy, even if it's framed as concern or care.",
@@ -562,50 +549,22 @@ def analyze_composite(msg1, date1, msg2, date2, msg3, date3, *answers_and_none):
562
  sentiments = [r[0][3]['label'] for r in results]
563
  stages = [r[0][4] for r in results]
564
  darvo_scores = [r[0][5] for r in results]
565
- tone_tags = [r[0][6] for r in results]
566
  dates_used = [r[1] or "Undated" for r in results] # Store dates for future mapping
567
-
568
- # Calculate escalation bump
569
  escalation_bump = 0
570
  for result, _ in results:
571
  abuse_score, threshold_labels, top_patterns, sentiment, stage, darvo_score, tone_tag = result
572
-
573
  if darvo_score > 0.65:
574
  escalation_bump += 3
575
-
576
- if tone_tag in ["forced accountability flip", "emotional threat", "aggressive dismissal", "mocking detachment"]:
577
  escalation_bump += 2
578
-
579
  if abuse_score > 80:
580
  escalation_bump += 2
581
-
582
  if stage == 2:
583
  escalation_bump += 3
584
 
585
- if "threat" in threshold_labels or "aggression" in threshold_labels:
586
- escalation_bump += 4
587
-
588
- # Helper: score trend of pattern severity
589
- def message_severity_index(threshold_labels):
590
- weights = {
591
- "recovery": 0,
592
- "dismissiveness": 1,
593
- "deflection": 1,
594
- "guilt tripping": 2,
595
- "control": 3,
596
- "gaslighting": 3,
597
- "aggression": 4,
598
- "threat": 5,
599
- }
600
- return max([weights.get(label, 0) for label in threshold_labels], default=0)
601
-
602
- severity_levels = [message_severity_index(r[0][1]) for r in results]
603
- if len(severity_levels) >= 2 and severity_levels == sorted(severity_levels):
604
- escalation_bump += 2
605
-
606
- escalation_bump = min(escalation_bump, 10)
607
-
608
- # Final escalation score
609
  hybrid_score = escalation_score + escalation_bump if escalation_score is not None else 0
610
  risk_level = (
611
  "High" if hybrid_score >= 16 else
@@ -613,6 +572,10 @@ def analyze_composite(msg1, date1, msg2, date2, msg3, date3, *answers_and_none):
613
  "Low"
614
  )
615
 
 
 
 
 
616
  # Post-check override (e.g. stage 2 or high abuse score forces Moderate risk)
617
  if any(score > 70 for score in abuse_scores) or any(stage == 2 for stage in stages):
618
  if risk_level == "Low":
@@ -623,18 +586,27 @@ def analyze_composite(msg1, date1, msg2, date2, msg3, date3, *answers_and_none):
623
 
624
  # --- Composite Abuse Score with Weighted Patterns ---
625
  composite_abuse_scores = []
 
626
  for result, _ in results:
627
  abuse_score, threshold_labels, top_patterns, _, _, _, _ = result
628
  weighted_score = 0
629
  total_weight = 0
 
630
  for label, score in top_patterns:
631
  weight = PATTERN_WEIGHTS.get(label, 1.0)
632
  weighted_score += score * weight
633
  total_weight += weight
634
- final_score = (weighted_score / total_weight) * 100 if total_weight > 0 else 0
 
 
 
 
 
635
  composite_abuse_scores.append(final_score)
636
 
637
  composite_abuse = int(round(sum(composite_abuse_scores) / len(composite_abuse_scores)))
 
 
638
  most_common_stage = max(set(stages), key=stages.count)
639
  stage_text = RISK_STAGE_LABELS[most_common_stage]
640
 
@@ -647,41 +619,34 @@ def analyze_composite(msg1, date1, msg2, date2, msg3, date3, *answers_and_none):
647
  out = f"Abuse Intensity: {composite_abuse}%\n"
648
  out += "📊 This reflects the strength and severity of detected abuse patterns in the message(s).\n\n"
649
 
 
650
  if escalation_score is None:
651
  escalation_text = "📉 Escalation Potential: Unknown (Checklist not completed)\n"
652
  escalation_text += "⚠️ *This section was not completed. Escalation potential is unknown.*\n"
 
653
  else:
654
  escalation_text = f"🧨 **Escalation Potential: {risk_level} ({escalation_score}/{sum(w for _, w in ESCALATION_QUESTIONS)})**\n"
655
  escalation_text += "This score comes directly from the safety checklist and functions as a standalone escalation risk score.\n"
656
  escalation_text += "It indicates how many serious risk factors are present based on your answers to the safety checklist.\n"
657
-
658
- # Derive top label
659
  top_label = None
660
  if results:
661
  sorted_patterns = sorted(
662
  [(label, score) for r in results for label, score in r[0][2]],
663
  key=lambda x: x[1],
664
  reverse=True
665
- )
666
  if sorted_patterns:
667
  top_label = f"{sorted_patterns[0][0]} – {int(round(sorted_patterns[0][1] * 100))}%"
668
  if top_label is None:
669
  top_label = "Unknown – 0%"
670
-
671
- out += generate_risk_snippet(
672
- composite_abuse,
673
- top_label,
674
- hybrid_score if escalation_score is not None else 0,
675
- most_common_stage,
676
- pattern_scores_for_snippet
677
- )
678
  out += f"\n\n{stage_text}"
679
  out += darvo_blurb
680
  out += "\n\n🎭 **Emotional Tones Detected:**\n"
681
  for i, tone in enumerate(tone_tags):
682
  label = tone if tone else "none"
683
  out += f"• Message {i+1}: *{label}*\n"
684
-
685
  print(f"DEBUG: avg_darvo = {avg_darvo}")
686
  pattern_labels = [r[0][2][0][0] for r in results] # top label for each message
687
  timeline_image = generate_abuse_score_chart(dates_used, abuse_scores, pattern_labels)
 
346
  elif sentiment == "supportive" and any(p in patterns for p in ["projection", "dismissiveness"]):
347
  return 4
348
  return 1
 
 
 
 
 
349
 
350
+ def generate_risk_snippet(abuse_score, top_label, escalation_score, stage):
351
  if abuse_score >= 85 or escalation_score >= 16:
352
  risk_level = "high"
353
+ elif abuse_score >= 60 or escalation_score >= 8:
354
  risk_level = "moderate"
355
  elif stage == 2 and abuse_score >= 40:
356
+ risk_level = "moderate" # 🔧 New rule for escalation stage
357
  else:
358
  risk_level = "low"
359
+ if isinstance(top_label, str) and " – " in top_label:
360
+ pattern_label, pattern_score = top_label.split(" – ")
 
 
 
 
 
 
 
361
  else:
362
+ pattern_label = str(top_label) if top_label is not None else "Unknown"
363
+ pattern_score = ""
 
364
 
365
  WHY_FLAGGED = {
366
  "control": "This message may reflect efforts to restrict someone’s autonomy, even if it's framed as concern or care.",
 
549
  sentiments = [r[0][3]['label'] for r in results]
550
  stages = [r[0][4] for r in results]
551
  darvo_scores = [r[0][5] for r in results]
552
+ tone_tags= [r[0][6] for r in results]
553
  dates_used = [r[1] or "Undated" for r in results] # Store dates for future mapping
554
+ # Calculate escalation bump *after* model results exist
 
555
  escalation_bump = 0
556
  for result, _ in results:
557
  abuse_score, threshold_labels, top_patterns, sentiment, stage, darvo_score, tone_tag = result
 
558
  if darvo_score > 0.65:
559
  escalation_bump += 3
560
+ if tone_tag in ["forced accountability flip", "emotional threat"]:
 
561
  escalation_bump += 2
 
562
  if abuse_score > 80:
563
  escalation_bump += 2
 
564
  if stage == 2:
565
  escalation_bump += 3
566
 
567
+ # Now we can safely calculate hybrid_score
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  hybrid_score = escalation_score + escalation_bump if escalation_score is not None else 0
569
  risk_level = (
570
  "High" if hybrid_score >= 16 else
 
572
  "Low"
573
  )
574
 
575
+ # Now compute scores and allow override
576
+ abuse_scores = [r[0][0] for r in results]
577
+ stages = [r[0][4] for r in results]
578
+
579
  # Post-check override (e.g. stage 2 or high abuse score forces Moderate risk)
580
  if any(score > 70 for score in abuse_scores) or any(stage == 2 for stage in stages):
581
  if risk_level == "Low":
 
586
 
587
  # --- Composite Abuse Score with Weighted Patterns ---
588
  composite_abuse_scores = []
589
+
590
  for result, _ in results:
591
  abuse_score, threshold_labels, top_patterns, _, _, _, _ = result
592
  weighted_score = 0
593
  total_weight = 0
594
+
595
  for label, score in top_patterns:
596
  weight = PATTERN_WEIGHTS.get(label, 1.0)
597
  weighted_score += score * weight
598
  total_weight += weight
599
+
600
+ if total_weight > 0:
601
+ final_score = (weighted_score / total_weight) * 100
602
+ else:
603
+ final_score = 0
604
+
605
  composite_abuse_scores.append(final_score)
606
 
607
  composite_abuse = int(round(sum(composite_abuse_scores) / len(composite_abuse_scores)))
608
+
609
+
610
  most_common_stage = max(set(stages), key=stages.count)
611
  stage_text = RISK_STAGE_LABELS[most_common_stage]
612
 
 
619
  out = f"Abuse Intensity: {composite_abuse}%\n"
620
  out += "📊 This reflects the strength and severity of detected abuse patterns in the message(s).\n\n"
621
 
622
+ # Save this line for later use at the
623
  if escalation_score is None:
624
  escalation_text = "📉 Escalation Potential: Unknown (Checklist not completed)\n"
625
  escalation_text += "⚠️ *This section was not completed. Escalation potential is unknown.*\n"
626
+ hybrid_score = 0 # ✅ fallback so it's defined for generate_risk_snippet
627
  else:
628
  escalation_text = f"🧨 **Escalation Potential: {risk_level} ({escalation_score}/{sum(w for _, w in ESCALATION_QUESTIONS)})**\n"
629
  escalation_text += "This score comes directly from the safety checklist and functions as a standalone escalation risk score.\n"
630
  escalation_text += "It indicates how many serious risk factors are present based on your answers to the safety checklist.\n"
631
+ # Derive top_label from the strongest top_patterns across all messages
 
632
  top_label = None
633
  if results:
634
  sorted_patterns = sorted(
635
  [(label, score) for r in results for label, score in r[0][2]],
636
  key=lambda x: x[1],
637
  reverse=True
638
+ )
639
  if sorted_patterns:
640
  top_label = f"{sorted_patterns[0][0]} – {int(round(sorted_patterns[0][1] * 100))}%"
641
  if top_label is None:
642
  top_label = "Unknown – 0%"
643
+ out += generate_risk_snippet(composite_abuse, top_label, hybrid_score if escalation_score is not None else 0, most_common_stage)
 
 
 
 
 
 
 
644
  out += f"\n\n{stage_text}"
645
  out += darvo_blurb
646
  out += "\n\n🎭 **Emotional Tones Detected:**\n"
647
  for i, tone in enumerate(tone_tags):
648
  label = tone if tone else "none"
649
  out += f"• Message {i+1}: *{label}*\n"
 
650
  print(f"DEBUG: avg_darvo = {avg_darvo}")
651
  pattern_labels = [r[0][2][0][0] for r in results] # top label for each message
652
  timeline_image = generate_abuse_score_chart(dates_used, abuse_scores, pattern_labels)