SamanthaStorm commited on
Commit
9879504
Β·
verified Β·
1 Parent(s): 6c8beb4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +182 -95
app.py CHANGED
@@ -21,6 +21,40 @@ logger = logging.getLogger(__name__)
21
  # Device configuration
22
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
23
  logger.info(f"Using device: {device}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  # Model initialization
26
  model_name = "SamanthaStorm/tether-multilabel-v4"
@@ -492,50 +526,57 @@ def generate_abuse_score_chart(dates, scores, patterns):
492
 
493
  def analyze_composite(msg1, msg2, msg3, *answers_and_none):
494
  """Analyze multiple messages and checklist responses"""
495
- logger.debug("\n====== STARTING NEW ANALYSIS ======")
 
496
  try:
497
  # Process checklist responses
498
- logger.debug("\n--- Checklist Processing ---")
 
499
  none_selected_checked = answers_and_none[-1]
500
  responses_checked = any(answers_and_none[:-1])
501
  none_selected = not responses_checked and none_selected_checked
502
 
503
- logger.debug(f"None selected checked: {none_selected_checked}")
504
- logger.debug(f"Responses checked: {responses_checked}")
505
- logger.debug(f"None selected: {none_selected}")
 
506
 
507
  if none_selected:
508
  escalation_score = 0
509
  escalation_note = "Checklist completed: no danger items reported."
510
  escalation_completed = True
511
- logger.debug("No items selected in checklist")
512
  elif responses_checked:
513
  escalation_score = sum(w for (_, w), a in zip(ESCALATION_QUESTIONS, answers_and_none[:-1]) if a)
514
  escalation_note = "Checklist completed."
515
  escalation_completed = True
516
- logger.debug(f"Checklist completed with score: {escalation_score}")
 
517
  # Log checked items
518
- logger.debug("Checked items:")
519
  for (q, w), a in zip(ESCALATION_QUESTIONS, answers_and_none[:-1]):
520
  if a:
521
- logger.debug(f"β€’ {q} (weight: {w})")
522
  else:
523
  escalation_score = None
524
  escalation_note = "Checklist not completed."
525
  escalation_completed = False
526
- logger.debug("Checklist not completed")
527
 
528
  # Process messages
529
- logger.debug("\n--- Message Processing ---")
 
530
  messages = [msg1, msg2, msg3]
531
  active = [(m, f"Message {i+1}") for i, m in enumerate(messages) if m.strip()]
532
- logger.debug(f"Number of active messages: {len(active)}")
 
533
  if not active:
534
- logger.debug("No messages provided")
535
  return "Please enter at least one message.", None
536
 
537
  # Detect threats
538
- logger.debug("\n--- Threat Detection ---")
 
539
  def normalize(text):
540
  import unicodedata
541
  text = text.lower().strip()
@@ -553,31 +594,40 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
553
  threat_risk = "Yes" if flat_threats else "No"
554
 
555
  if flat_threats:
556
- logger.debug("Detected threats:")
557
  for threat in flat_threats:
558
- logger.debug(f"β€’ {threat}")
559
  else:
560
- logger.debug("No explicit threats detected")
561
-
562
  # Analyze each message
563
- logger.debug("\n--- Individual Message Analysis ---")
 
564
  results = []
565
  for m, d in active:
566
- logger.debug(f"\nAnalyzing {d}:")
567
- logger.debug("-" * 40)
568
  result = analyze_single_message(m, THRESHOLDS.copy())
569
  results.append((result, d))
570
 
571
- # Log results for each message
572
  abuse_score, patterns, matched_scores, sentiment, stage, darvo_score, tone = result
573
- logger.debug(f"Results for {d}:")
574
- logger.debug(f"β€’ Abuse Score: {abuse_score}")
575
- logger.debug(f"β€’ Patterns: {patterns}")
576
- logger.debug(f"β€’ Matched Scores: {matched_scores}")
577
- logger.debug(f"β€’ Sentiment: {sentiment['label']}")
578
- logger.debug(f"β€’ Stage: {stage}")
579
- logger.debug(f"β€’ DARVO Score: {darvo_score}")
580
- logger.debug(f"β€’ Tone: {tone}")
 
 
 
 
 
 
 
 
 
581
 
582
  # Extract scores and metadata
583
  abuse_scores = [r[0][0] for r in results]
@@ -586,87 +636,115 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
586
  tone_tags = [r[0][6] for r in results]
587
  dates_used = [r[1] for r in results]
588
 
589
- logger.debug("\n--- Pattern Analysis ---")
 
 
590
  predicted_labels = [label for r in results for label in r[0][1]]
591
- logger.debug(f"All detected patterns: {predicted_labels}")
592
- # Pattern severity analysis
593
- logger.debug("\n--- Pattern Severity Analysis ---")
594
- high = {'control'}
595
- moderate = {'gaslighting', 'dismissiveness', 'obscure language', 'insults',
596
- 'contradictory statements', 'guilt tripping'}
597
- low = {'blame shifting', 'projection', 'recovery'}
598
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  counts = {'high': 0, 'moderate': 0, 'low': 0}
600
  for label in predicted_labels:
601
  if label in high:
602
  counts['high'] += 1
603
- logger.debug(f"High severity pattern found: {label}")
604
  elif label in moderate:
605
  counts['moderate'] += 1
606
- logger.debug(f"Moderate severity pattern found: {label}")
607
  elif label in low:
608
  counts['low'] += 1
609
- logger.debug(f"Low severity pattern found: {label}")
610
 
611
- logger.debug(f"Pattern counts - High: {counts['high']}, Moderate: {counts['moderate']}, Low: {counts['low']}")
 
 
 
612
 
613
- # Pattern escalation logic
614
- logger.debug("\n--- Escalation Risk Assessment ---")
 
615
  if counts['high'] >= 2 and counts['moderate'] >= 2:
616
  pattern_escalation_risk = "Critical"
617
- logger.debug("Critical risk: Multiple high and moderate patterns")
618
  elif (counts['high'] >= 2 and counts['moderate'] >= 1) or \
619
  (counts['moderate'] >= 3) or \
620
  (counts['high'] >= 1 and counts['moderate'] >= 2):
621
  pattern_escalation_risk = "High"
622
- logger.debug("High risk: Significant pattern combination")
623
  elif (counts['moderate'] == 2) or \
624
  (counts['high'] == 1 and counts['moderate'] == 1) or \
625
  (counts['moderate'] == 1 and counts['low'] >= 2) or \
626
  (counts['high'] == 1 and sum(counts.values()) == 1):
627
  pattern_escalation_risk = "Moderate"
628
- logger.debug("Moderate risk: Concerning pattern combination")
629
  else:
630
  pattern_escalation_risk = "Low"
631
- logger.debug("Low risk: Limited pattern severity")
632
 
633
- # Calculate checklist escalation risk
634
- logger.debug("\n--- Checklist Risk Assessment ---")
 
635
  checklist_escalation_risk = "Unknown" if escalation_score is None else (
636
  "Critical" if escalation_score >= 20 else
637
  "Moderate" if escalation_score >= 10 else
638
  "Low"
639
  )
640
- logger.debug(f"Checklist escalation risk: {checklist_escalation_risk}")
641
-
642
- # Calculate escalation bump
643
- logger.debug("\n--- Escalation Bump Calculation ---")
 
 
644
  escalation_bump = 0
645
  for result, msg_id in results:
646
  abuse_score, _, _, sentiment, stage, darvo_score, tone_tag = result
647
- logger.debug(f"\nChecking escalation factors for {msg_id}:")
 
 
648
  if darvo_score > 0.65:
649
  escalation_bump += 3
650
- logger.debug("β€’ +3 for high DARVO score")
651
  if tone_tag in ["forced accountability flip", "emotional threat"]:
652
  escalation_bump += 2
653
- logger.debug("β€’ +2 for concerning tone")
654
  if abuse_score > 80:
655
  escalation_bump += 2
656
- logger.debug("β€’ +2 for high abuse score")
657
  if stage == 2:
658
  escalation_bump += 3
659
- logger.debug("β€’ +3 for escalation stage")
 
 
 
 
 
 
660
 
661
- logger.debug(f"Total escalation bump: +{escalation_bump}")
662
 
663
- # Calculate combined risk
664
- logger.debug("\n--- Combined Risk Calculation ---")
 
665
  def rank(label):
666
  return {"Low": 0, "Moderate": 1, "High": 2, "Critical": 3, "Unknown": 0}.get(label, 0)
667
 
668
  combined_score = rank(pattern_escalation_risk) + rank(checklist_escalation_risk) + escalation_bump
669
- logger.debug(f"Combined risk score: {combined_score}")
 
 
 
 
670
 
671
  escalation_risk = (
672
  "Critical" if combined_score >= 6 else
@@ -674,17 +752,18 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
674
  "Moderate" if combined_score >= 2 else
675
  "Low"
676
  )
677
- logger.debug(f"Final escalation risk: {escalation_risk}")
678
 
679
- # Build escalation text
680
- logger.debug("\n--- Building Output Text ---")
 
681
  if escalation_score is None:
682
  escalation_text = (
683
  "🚫 **Escalation Potential: Unknown** (Checklist not completed)\n"
684
  "⚠️ This section was not completed. Escalation potential is estimated using message data only.\n"
685
  )
686
  hybrid_score = 0
687
- logger.debug("Generated unknown escalation text")
688
  elif escalation_score == 0:
689
  escalation_text = (
690
  "βœ… **Escalation Checklist Completed:** No danger items reported.\n"
@@ -694,7 +773,7 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
694
  f"β€’ Escalation Bump: +{escalation_bump} (from DARVO, tone, intensity, etc.)"
695
  )
696
  hybrid_score = escalation_bump
697
- logger.debug("Generated no-risk escalation text")
698
  else:
699
  hybrid_score = escalation_score + escalation_bump
700
  escalation_text = (
@@ -704,32 +783,36 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
704
  f"β€’ Checklist Risk: {checklist_escalation_risk}\n"
705
  f"β€’ Escalation Bump: +{escalation_bump} (from DARVO, tone, intensity, etc.)"
706
  )
707
- logger.debug(f"Generated escalation text with hybrid score: {hybrid_score}")
708
 
709
- # Calculate composite abuse score
710
- logger.debug("\n--- Final Scores ---")
 
711
  composite_abuse = int(round(sum(abuse_scores) / len(abuse_scores)))
712
- logger.debug(f"Composite abuse score: {composite_abuse}")
713
-
714
- # Get most common stage
715
  most_common_stage = max(set(stages), key=stages.count)
716
- stage_text = RISK_STAGE_LABELS[most_common_stage]
717
- logger.debug(f"Most common stage: {most_common_stage}")
 
 
718
 
719
- # Build final output
720
- logger.debug("\n--- Generating Final Output ---")
 
721
  out = f"Abuse Intensity: {composite_abuse}%\n"
722
  out += "πŸ“Š This reflects the strength and severity of detected abuse patterns in the message(s).\n\n"
723
 
724
- # Add risk assessment
725
  risk_level = (
726
  "Critical" if composite_abuse >= 85 or hybrid_score >= 20 else
727
  "High" if composite_abuse >= 70 or hybrid_score >= 15 else
728
  "Moderate" if composite_abuse >= 50 or hybrid_score >= 10 else
729
  "Low"
730
  )
731
- logger.debug(f"Final risk level: {risk_level}")
732
-
 
733
  risk_descriptions = {
734
  "Critical": (
735
  "🚨 **Risk Level: Critical**\n"
@@ -754,24 +837,23 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
754
  }
755
 
756
  out += risk_descriptions[risk_level]
757
- out += f"\n\n{stage_text}"
758
 
759
- # Add DARVO analysis
760
- avg_darvo = round(sum(darvo_scores) / len(darvo_scores), 3)
761
- logger.debug(f"Average DARVO score: {avg_darvo}")
762
  if avg_darvo > 0.25:
763
  level = "moderate" if avg_darvo < 0.65 else "high"
764
  out += f"\n\n🎭 **DARVO Score: {avg_darvo}** β†’ This indicates a **{level} likelihood** of narrative reversal (DARVO), where the speaker may be denying, attacking, or reversing blame."
 
765
 
766
- # Add emotional tones
767
- logger.debug("\n--- Adding Emotional Tones ---")
768
  out += "\n\n🎭 **Emotional Tones Detected:**\n"
769
  for i, tone in enumerate(tone_tags):
770
  out += f"β€’ Message {i+1}: *{tone or 'none'}*\n"
771
  logger.debug(f"Message {i+1} tone: {tone}")
772
 
773
- # Add threats section
774
- logger.debug("\n--- Adding Threat Analysis ---")
775
  if flat_threats:
776
  out += "\n\n🚨 **Immediate Danger Threats Detected:**\n"
777
  for t in set(flat_threats):
@@ -783,8 +865,8 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
783
  out += "This does *not* rule out risk, but no direct threat phrases were matched."
784
  logger.debug("No threats to add")
785
 
786
- # Generate timeline
787
- logger.debug("\n--- Generating Timeline ---")
788
  pattern_labels = [
789
  pats[0][0] if (pats := r[0][2]) else "none"
790
  for r in results
@@ -792,18 +874,23 @@ def analyze_composite(msg1, msg2, msg3, *answers_and_none):
792
  timeline_image = generate_abuse_score_chart(dates_used, abuse_scores, pattern_labels)
793
  logger.debug("Timeline generated successfully")
794
 
795
- # Add escalation text
796
  out += "\n\n" + escalation_text
797
 
798
- logger.debug("\n====== ANALYSIS COMPLETE ======\n")
 
799
  return out, timeline_image
800
 
801
  except Exception as e:
802
- logger.error(f"Error in analyze_composite: {e}")
803
- logger.error(f"Traceback: {traceback.format_exc()}")
 
 
 
804
  return "An error occurred during analysis.", None
805
 
806
 
 
807
  # Gradio Interface Setup
808
  def create_interface():
809
  try:
 
21
  # Device configuration
22
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
23
  logger.info(f"Using device: {device}")
24
+ # Set up custom logging
25
+ class CustomFormatter(logging.Formatter):
26
+ """Custom formatter with colors and better formatting"""
27
+ grey = "\x1b[38;21m"
28
+ blue = "\x1b[38;5;39m"
29
+ yellow = "\x1b[38;5;226m"
30
+ red = "\x1b[38;5;196m"
31
+ bold_red = "\x1b[31;1m"
32
+ reset = "\x1b[0m"
33
+
34
+ format_str = "%(message)s"
35
+
36
+ FORMATS = {
37
+ logging.DEBUG: blue + format_str + reset,
38
+ logging.INFO: grey + format_str + reset,
39
+ logging.WARNING: yellow + format_str + reset,
40
+ logging.ERROR: red + format_str + reset,
41
+ logging.CRITICAL: bold_red + format_str + reset
42
+ }
43
+
44
+ def format(self, record):
45
+ log_fmt = self.FORMATS.get(record.levelno)
46
+ formatter = logging.Formatter(log_fmt)
47
+ return formatter.format(record)
48
+
49
+ # Setup logger
50
+ logger = logging.getLogger(__name__)
51
+ logger.setLevel(logging.DEBUG)
52
+
53
+ # Create console handler with custom formatter
54
+ ch = logging.StreamHandler()
55
+ ch.setLevel(logging.DEBUG)
56
+ ch.setFormatter(CustomFormatter())
57
+ logger.handlers = [ch]
58
 
59
  # Model initialization
60
  model_name = "SamanthaStorm/tether-multilabel-v4"
 
526
 
527
  def analyze_composite(msg1, msg2, msg3, *answers_and_none):
528
  """Analyze multiple messages and checklist responses"""
529
+ logger.debug("\nπŸ”„ STARTING NEW ANALYSIS")
530
+ logger.debug("=" * 50)
531
  try:
532
  # Process checklist responses
533
+ logger.debug("\nπŸ“‹ CHECKLIST PROCESSING")
534
+ logger.debug("=" * 50)
535
  none_selected_checked = answers_and_none[-1]
536
  responses_checked = any(answers_and_none[:-1])
537
  none_selected = not responses_checked and none_selected_checked
538
 
539
+ logger.debug("Checklist Status:")
540
+ logger.debug(f" β€’ None Selected Box: {'βœ“' if none_selected_checked else 'βœ—'}")
541
+ logger.debug(f" β€’ Has Responses: {'βœ“' if responses_checked else 'βœ—'}")
542
+ logger.debug(f" β€’ Final Status: {'None Selected' if none_selected else 'Has Selections'}")
543
 
544
  if none_selected:
545
  escalation_score = 0
546
  escalation_note = "Checklist completed: no danger items reported."
547
  escalation_completed = True
548
+ logger.debug("\nβœ“ Checklist: No items selected")
549
  elif responses_checked:
550
  escalation_score = sum(w for (_, w), a in zip(ESCALATION_QUESTIONS, answers_and_none[:-1]) if a)
551
  escalation_note = "Checklist completed."
552
  escalation_completed = True
553
+ logger.debug(f"\nπŸ“Š Checklist Score: {escalation_score}")
554
+
555
  # Log checked items
556
+ logger.debug("\n⚠️ Selected Risk Factors:")
557
  for (q, w), a in zip(ESCALATION_QUESTIONS, answers_and_none[:-1]):
558
  if a:
559
+ logger.debug(f" β€’ [{w} points] {q}")
560
  else:
561
  escalation_score = None
562
  escalation_note = "Checklist not completed."
563
  escalation_completed = False
564
+ logger.debug("\n❗ Checklist: Not completed")
565
 
566
  # Process messages
567
+ logger.debug("\nπŸ“ MESSAGE PROCESSING")
568
+ logger.debug("=" * 50)
569
  messages = [msg1, msg2, msg3]
570
  active = [(m, f"Message {i+1}") for i, m in enumerate(messages) if m.strip()]
571
+ logger.debug(f"Active Messages: {len(active)} of 3")
572
+
573
  if not active:
574
+ logger.debug("❌ Error: No messages provided")
575
  return "Please enter at least one message.", None
576
 
577
  # Detect threats
578
+ logger.debug("\n🚨 THREAT DETECTION")
579
+ logger.debug("=" * 50)
580
  def normalize(text):
581
  import unicodedata
582
  text = text.lower().strip()
 
594
  threat_risk = "Yes" if flat_threats else "No"
595
 
596
  if flat_threats:
597
+ logger.debug("⚠️ DETECTED THREATS:")
598
  for threat in flat_threats:
599
+ logger.debug(f" β€’ {threat}")
600
  else:
601
+ logger.debug("βœ“ No explicit threats detected")
 
602
  # Analyze each message
603
+ logger.debug("\nπŸ” INDIVIDUAL MESSAGE ANALYSIS")
604
+ logger.debug("=" * 50)
605
  results = []
606
  for m, d in active:
607
+ logger.debug(f"\nπŸ“ ANALYZING {d}")
608
+ logger.debug("=" * 40)
609
  result = analyze_single_message(m, THRESHOLDS.copy())
610
  results.append((result, d))
611
 
612
+ # Unpack results for cleaner logging
613
  abuse_score, patterns, matched_scores, sentiment, stage, darvo_score, tone = result
614
+
615
+ # Log core metrics
616
+ logger.debug("\nπŸ“Š CORE METRICS")
617
+ logger.debug(f" β€’ Abuse Score: {abuse_score:.1f}%")
618
+ logger.debug(f" β€’ DARVO Score: {darvo_score:.3f}")
619
+ logger.debug(f" β€’ Risk Stage: {stage}")
620
+ logger.debug(f" β€’ Sentiment: {sentiment['label']}")
621
+ logger.debug(f" β€’ Tone: {tone}")
622
+
623
+ # Log detected patterns with scores
624
+ if patterns:
625
+ logger.debug("\n🎯 DETECTED PATTERNS")
626
+ for label, score, weight in matched_scores:
627
+ severity = "❗HIGH" if label in high else "⚠️ MODERATE" if label in moderate else "πŸ“ LOW"
628
+ logger.debug(f" β€’ {severity} | {label}: {score:.3f} (weight: {weight})")
629
+ else:
630
+ logger.debug("\nβœ“ No abuse patterns detected")
631
 
632
  # Extract scores and metadata
633
  abuse_scores = [r[0][0] for r in results]
 
636
  tone_tags = [r[0][6] for r in results]
637
  dates_used = [r[1] for r in results]
638
 
639
+ # Pattern Analysis
640
+ logger.debug("\nπŸ“ˆ PATTERN ANALYSIS SUMMARY")
641
+ logger.debug("=" * 50)
642
  predicted_labels = [label for r in results for label in r[0][1]]
 
 
 
 
 
 
 
643
 
644
+ if predicted_labels:
645
+ logger.debug("Detected Patterns Across All Messages:")
646
+ for label in set(predicted_labels):
647
+ count = predicted_labels.count(label)
648
+ if label in high:
649
+ logger.debug(f" ❗ HIGH: {label} (Γ—{count})")
650
+ elif label in moderate:
651
+ logger.debug(f" ⚠️ MODERATE: {label} (Γ—{count})")
652
+ elif label in low:
653
+ logger.debug(f" πŸ“ LOW: {label} (Γ—{count})")
654
+ else:
655
+ logger.debug("βœ“ No patterns detected across messages")
656
+
657
+ # Pattern Severity Analysis
658
+ logger.debug("\nβš–οΈ SEVERITY ANALYSIS")
659
+ logger.debug("=" * 50)
660
  counts = {'high': 0, 'moderate': 0, 'low': 0}
661
  for label in predicted_labels:
662
  if label in high:
663
  counts['high'] += 1
 
664
  elif label in moderate:
665
  counts['moderate'] += 1
 
666
  elif label in low:
667
  counts['low'] += 1
 
668
 
669
+ logger.debug("Pattern Distribution:")
670
+ logger.debug(f" ❗ High Severity: {counts['high']}")
671
+ logger.debug(f" ⚠️ Moderate Severity: {counts['moderate']}")
672
+ logger.debug(f" πŸ“ Low Severity: {counts['low']}")
673
 
674
+ # Risk Assessment
675
+ logger.debug("\n🎯 RISK ASSESSMENT")
676
+ logger.debug("=" * 50)
677
  if counts['high'] >= 2 and counts['moderate'] >= 2:
678
  pattern_escalation_risk = "Critical"
679
+ logger.debug("❗ CRITICAL RISK: Multiple high and moderate patterns")
680
  elif (counts['high'] >= 2 and counts['moderate'] >= 1) or \
681
  (counts['moderate'] >= 3) or \
682
  (counts['high'] >= 1 and counts['moderate'] >= 2):
683
  pattern_escalation_risk = "High"
684
+ logger.debug("⚠️ HIGH RISK: Significant pattern combination")
685
  elif (counts['moderate'] == 2) or \
686
  (counts['high'] == 1 and counts['moderate'] == 1) or \
687
  (counts['moderate'] == 1 and counts['low'] >= 2) or \
688
  (counts['high'] == 1 and sum(counts.values()) == 1):
689
  pattern_escalation_risk = "Moderate"
690
+ logger.debug("⚑ MODERATE RISK: Concerning pattern combination")
691
  else:
692
  pattern_escalation_risk = "Low"
693
+ logger.debug("πŸ“ LOW RISK: Limited pattern severity")
694
 
695
+ # Checklist Risk Assessment
696
+ logger.debug("\nπŸ“‹ CHECKLIST RISK ASSESSMENT")
697
+ logger.debug("=" * 50)
698
  checklist_escalation_risk = "Unknown" if escalation_score is None else (
699
  "Critical" if escalation_score >= 20 else
700
  "Moderate" if escalation_score >= 10 else
701
  "Low"
702
  )
703
+ logger.debug(f"Risk Level: {checklist_escalation_risk}")
704
+ if escalation_score is not None:
705
+ logger.debug(f"Score: {escalation_score}/29")
706
+ # Escalation Analysis
707
+ logger.debug("\nπŸ“ˆ ESCALATION ANALYSIS")
708
+ logger.debug("=" * 50)
709
  escalation_bump = 0
710
  for result, msg_id in results:
711
  abuse_score, _, _, sentiment, stage, darvo_score, tone_tag = result
712
+ logger.debug(f"\nπŸ” Message {msg_id} Escalation Factors:")
713
+
714
+ factors = []
715
  if darvo_score > 0.65:
716
  escalation_bump += 3
717
+ factors.append("β–² +3: High DARVO score ({darvo_score:.3f})")
718
  if tone_tag in ["forced accountability flip", "emotional threat"]:
719
  escalation_bump += 2
720
+ factors.append(f"β–² +2: Concerning tone ({tone_tag})")
721
  if abuse_score > 80:
722
  escalation_bump += 2
723
+ factors.append(f"β–² +2: High abuse score ({abuse_score:.1f}%)")
724
  if stage == 2:
725
  escalation_bump += 3
726
+ factors.append("β–² +3: Escalation stage")
727
+
728
+ if factors:
729
+ for factor in factors:
730
+ logger.debug(f" {factor}")
731
+ else:
732
+ logger.debug(" βœ“ No escalation factors")
733
 
734
+ logger.debug(f"\nπŸ“Š Total Escalation Bump: +{escalation_bump}")
735
 
736
+ # Combined Risk Calculation
737
+ logger.debug("\n🎯 FINAL RISK CALCULATION")
738
+ logger.debug("=" * 50)
739
  def rank(label):
740
  return {"Low": 0, "Moderate": 1, "High": 2, "Critical": 3, "Unknown": 0}.get(label, 0)
741
 
742
  combined_score = rank(pattern_escalation_risk) + rank(checklist_escalation_risk) + escalation_bump
743
+ logger.debug("Risk Components:")
744
+ logger.debug(f" β€’ Pattern Risk: {pattern_escalation_risk} (+{rank(pattern_escalation_risk)})")
745
+ logger.debug(f" β€’ Checklist Risk: {checklist_escalation_risk} (+{rank(checklist_escalation_risk)})")
746
+ logger.debug(f" β€’ Escalation Bump: +{escalation_bump}")
747
+ logger.debug(f" = Combined Score: {combined_score}")
748
 
749
  escalation_risk = (
750
  "Critical" if combined_score >= 6 else
 
752
  "Moderate" if combined_score >= 2 else
753
  "Low"
754
  )
755
+ logger.debug(f"\n⚠️ Final Escalation Risk: {escalation_risk}")
756
 
757
+ # Generate Output Text
758
+ logger.debug("\nπŸ“ GENERATING OUTPUT")
759
+ logger.debug("=" * 50)
760
  if escalation_score is None:
761
  escalation_text = (
762
  "🚫 **Escalation Potential: Unknown** (Checklist not completed)\n"
763
  "⚠️ This section was not completed. Escalation potential is estimated using message data only.\n"
764
  )
765
  hybrid_score = 0
766
+ logger.debug("Generated output for incomplete checklist")
767
  elif escalation_score == 0:
768
  escalation_text = (
769
  "βœ… **Escalation Checklist Completed:** No danger items reported.\n"
 
773
  f"β€’ Escalation Bump: +{escalation_bump} (from DARVO, tone, intensity, etc.)"
774
  )
775
  hybrid_score = escalation_bump
776
+ logger.debug("Generated output for no-risk checklist")
777
  else:
778
  hybrid_score = escalation_score + escalation_bump
779
  escalation_text = (
 
783
  f"β€’ Checklist Risk: {checklist_escalation_risk}\n"
784
  f"β€’ Escalation Bump: +{escalation_bump} (from DARVO, tone, intensity, etc.)"
785
  )
786
+ logger.debug(f"Generated output with hybrid score: {hybrid_score}/29")
787
 
788
+ # Final Metrics
789
+ logger.debug("\nπŸ“Š FINAL METRICS")
790
+ logger.debug("=" * 50)
791
  composite_abuse = int(round(sum(abuse_scores) / len(abuse_scores)))
792
+ logger.debug(f"Composite Abuse Score: {composite_abuse}%")
793
+
 
794
  most_common_stage = max(set(stages), key=stages.count)
795
+ logger.debug(f"Most Common Stage: {most_common_stage}")
796
+
797
+ avg_darvo = round(sum(darvo_scores) / len(darvo_scores), 3)
798
+ logger.debug(f"Average DARVO Score: {avg_darvo}")
799
 
800
+ # Generate Final Output
801
+ logger.debug("\nπŸ“„ GENERATING FINAL REPORT")
802
+ logger.debug("=" * 50)
803
  out = f"Abuse Intensity: {composite_abuse}%\n"
804
  out += "πŸ“Š This reflects the strength and severity of detected abuse patterns in the message(s).\n\n"
805
 
806
+ # Risk Level Assessment
807
  risk_level = (
808
  "Critical" if composite_abuse >= 85 or hybrid_score >= 20 else
809
  "High" if composite_abuse >= 70 or hybrid_score >= 15 else
810
  "Moderate" if composite_abuse >= 50 or hybrid_score >= 10 else
811
  "Low"
812
  )
813
+ logger.debug(f"Final Risk Level: {risk_level}")
814
+
815
+ # Add Risk Description
816
  risk_descriptions = {
817
  "Critical": (
818
  "🚨 **Risk Level: Critical**\n"
 
837
  }
838
 
839
  out += risk_descriptions[risk_level]
840
+ out += f"\n\n{RISK_STAGE_LABELS[most_common_stage]}"
841
 
842
+ # Add DARVO Analysis
 
 
843
  if avg_darvo > 0.25:
844
  level = "moderate" if avg_darvo < 0.65 else "high"
845
  out += f"\n\n🎭 **DARVO Score: {avg_darvo}** β†’ This indicates a **{level} likelihood** of narrative reversal (DARVO), where the speaker may be denying, attacking, or reversing blame."
846
+ logger.debug(f"Added DARVO analysis ({level} level)")
847
 
848
+ # Add Emotional Tones
849
+ logger.debug("\n🎭 Adding Emotional Tones")
850
  out += "\n\n🎭 **Emotional Tones Detected:**\n"
851
  for i, tone in enumerate(tone_tags):
852
  out += f"β€’ Message {i+1}: *{tone or 'none'}*\n"
853
  logger.debug(f"Message {i+1} tone: {tone}")
854
 
855
+ # Add Threats Section
856
+ logger.debug("\n⚠️ Adding Threat Analysis")
857
  if flat_threats:
858
  out += "\n\n🚨 **Immediate Danger Threats Detected:**\n"
859
  for t in set(flat_threats):
 
865
  out += "This does *not* rule out risk, but no direct threat phrases were matched."
866
  logger.debug("No threats to add")
867
 
868
+ # Generate Timeline
869
+ logger.debug("\nπŸ“ˆ Generating Timeline")
870
  pattern_labels = [
871
  pats[0][0] if (pats := r[0][2]) else "none"
872
  for r in results
 
874
  timeline_image = generate_abuse_score_chart(dates_used, abuse_scores, pattern_labels)
875
  logger.debug("Timeline generated successfully")
876
 
877
+ # Add Escalation Text
878
  out += "\n\n" + escalation_text
879
 
880
+ logger.debug("\nβœ… ANALYSIS COMPLETE")
881
+ logger.debug("=" * 50)
882
  return out, timeline_image
883
 
884
  except Exception as e:
885
+ logger.error("\n❌ ERROR IN ANALYSIS")
886
+ logger.error("=" * 50)
887
+ logger.error(f"Error type: {type(e).__name__}")
888
+ logger.error(f"Error message: {str(e)}")
889
+ logger.error(f"Traceback:\n{traceback.format_exc()}")
890
  return "An error occurred during analysis.", None
891
 
892
 
893
+
894
  # Gradio Interface Setup
895
  def create_interface():
896
  try: