SamanthaStorm commited on
Commit
70ce6b1
·
verified ·
1 Parent(s): 239a968

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -98
app.py CHANGED
@@ -1,16 +1,9 @@
1
  import gradio as gr
2
- import re
3
- from transformers import pipeline as hf_pipeline
 
4
 
5
- # Load SST model
6
- sst_classifier = hf_pipeline(
7
- "text-classification",
8
- model="distilbert-base-uncased-finetuned-sst-2-english",
9
- top_k=1,
10
- truncation=True
11
- )
12
-
13
- # Load emotion classifier
14
  emotion_pipeline = hf_pipeline(
15
  "text-classification",
16
  model="j-hartmann/emotion-english-distilroberta-base",
@@ -18,98 +11,95 @@ emotion_pipeline = hf_pipeline(
18
  truncation=True
19
  )
20
 
21
- # Lexicon rules
22
- negations = {"not", "never", "no", "none", "nobody", "nothing", "neither", "nowhere", "hardly", "scarcely", "barely"}
23
- amplifiers = {"very", "really", "extremely", "so", "totally", "completely", "absolutely", "utterly", "super"}
24
- softeners = {"slightly", "somewhat", "a bit", "a little", "mildly", "fairly", "kind of"}
25
-
26
- def preprocess_sentiment_text(text):
27
- words = text.lower().split()
28
- modified = []
29
- negate = False
30
-
31
- for word in words:
32
- stripped = re.sub(r'\W+', '', word)
33
-
34
- if stripped in negations:
35
- negate = True
36
- modified.append("<NEG>")
37
- continue
38
- if stripped in amplifiers:
39
- modified.append(f"<AMP>{word}")
40
- continue
41
- if stripped in softeners:
42
- modified.append(f"<SOFT>{word}")
43
- continue
44
- if negate:
45
- modified.append(f"<NEG>{word}")
46
- negate = False
47
- else:
48
- modified.append(word)
49
-
50
- return " ".join(modified)
51
-
52
  def get_emotion_profile(text):
53
- emotions = emotion_pipeline(text)
54
- if isinstance(emotions, list) and isinstance(emotions[0], list):
55
- emotions = emotions[0]
56
- return {e['label'].lower(): round(e['score'], 3) for e in emotions}
57
-
58
- def get_emotional_tone_tag(emotions, sentiment, patterns, abuse_score=0):
59
- sadness = emotions.get("sadness", 0)
60
- joy = emotions.get("joy", 0)
61
- neutral = emotions.get("neutral", 0)
62
- disgust = emotions.get("disgust", 0)
63
- anger = emotions.get("anger", 0)
64
- fear = emotions.get("fear", 0)
65
-
66
- if sadness > 0.4 and any(p in patterns for p in ["blame shifting", "guilt tripping", "recovery phase"]) and (sentiment == "undermining" or abuse_score > 40):
67
- return "performative regret"
68
- if (joy > 0.3 or sadness > 0.4) and any(p in patterns for p in ["control", "gaslighting"]) and sentiment == "undermining":
69
- return "coercive warmth"
70
- if (neutral + disgust) > 0.5 and any(p in patterns for p in ["dismissiveness", "projection", "obscure language"]) and sentiment == "undermining":
71
- return "cold invalidation"
72
- if (sadness + fear) > 0.5 and sentiment == "supportive" and all(p in ["recovery phase"] for p in patterns):
73
- return "genuine vulnerability"
74
- if (anger + disgust) > 0.5 and any(p in patterns for p in ["control", "threat", "insults", "dismissiveness"]) and sentiment == "undermining":
75
- return "emotional threat"
76
- if sadness > 0.6 and any(p in patterns for p in ["guilt tripping", "projection"]) and sentiment == "undermining":
77
- return "weaponized sadness"
78
- if neutral > 0.5 and any(p in patterns for p in ["dismissiveness", "obscure language"]) and sentiment == "undermining":
79
- return "toxic resignation"
80
-
81
- return None
82
-
83
  def analyze_message(text):
84
- preprocessed = preprocess_sentiment_text(text)
85
-
86
- # Run sentiment classification
87
- sst_result = sst_classifier(preprocessed)
88
- sentiment_output = sst_result[0] if isinstance(sst_result, list) else sst_result
89
- sentiment_label = "supportive" if sentiment_output["label"] == "POSITIVE" else "undermining"
90
- sentiment_score = round(sentiment_output["score"] * 100, 2)
91
-
92
- # Run emotion model
93
- emotions = get_emotion_profile(text)
94
- emotion_summary = "\n".join([f"{k.title()}: {v:.2f}" for k, v in emotions.items()])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
- # No abuse patterns yet
97
- patterns = []
98
- tone_tag = get_emotional_tone_tag(emotions, sentiment_label, patterns)
99
- tone_output = tone_tag if tone_tag else "None detected"
100
 
101
- return (
102
- f"🧠 Sentiment: {sentiment_label.title()} ({sentiment_score}%)\n\n"
103
- f"🎭 Emotional Profile:\n{emotion_summary}\n\n"
104
- f"🔍 Tone Tag: {tone_output}"
105
- )
106
 
107
  iface = gr.Interface(
108
- fn=analyze_message,
109
- inputs=gr.Textbox(lines=4, placeholder="Paste a message here..."),
110
- outputs="text",
111
- title="Tether SST + Emotional Tone Tagger",
112
- description="Applies lexicon-enhanced preprocessing, classifies sentiment, profiles emotion, and infers tone tags using behavioral logic."
113
  )
114
 
115
- iface.launch()
 
 
1
  import gradio as gr
2
+ import torch
3
+ from transformers import pipeline as hf_pipeline, AutoModelForSequenceClassification, AutoTokenizer
4
+ from motif_tagging import detect_motifs
5
 
6
+ # ——— 1) Emotion Pipeline ————————————————————————————————————————————————
 
 
 
 
 
 
 
 
7
  emotion_pipeline = hf_pipeline(
8
  "text-classification",
9
  model="j-hartmann/emotion-english-distilroberta-base",
 
11
  truncation=True
12
  )
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  def get_emotion_profile(text):
15
+ """
16
+ Returns a dict of { emotion_label: score } for the input text.
17
+ """
18
+ results = emotion_pipeline(text)
19
+ # some pipelines return [[…]]
20
+ if isinstance(results, list) and isinstance(results[0], list):
21
+ results = results[0]
22
+ return {r["label"].lower(): round(r["score"], 3) for r in results}
23
+
24
+
25
+ # ——— 2) Abuse-patterns Model ——————————————————————————————————————————————
26
+ model_name = "SamanthaStorm/tether-multilabel-v3"
27
+ model = AutoModelForSequenceClassification.from_pretrained(model_name)
28
+ tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
29
+
30
+ LABELS = [
31
+ "blame shifting", "contradictory statements", "control", "dismissiveness",
32
+ "gaslighting", "guilt tripping", "insults", "obscure language",
33
+ "projection", "recovery phase", "threat"
34
+ ]
35
+
36
+ THRESHOLDS = {
37
+ "blame shifting": 0.28, "contradictory statements": 0.27, "control": 0.08, "dismissiveness": 0.32,
38
+ "gaslighting": 0.27, "guilt tripping": 0.31, "insults": 0.10, "obscure language": 0.55,
39
+ "projection": 0.09, "recovery phase": 0.33, "threat": 0.15
40
+ }
41
+
42
+
43
+ # ——— 3) Single-message analysis ——————————————————————————————————————————————
 
44
  def analyze_message(text):
45
+ """
46
+ Runs motif detection, emotion profiling, and the abuse-pattern classifier.
47
+ Returns a dict with:
48
+ - matched_phrases: list of raw regex hits
49
+ - emotion_profile: { emotion: score }
50
+ - active_patterns: [ labels above their threshold ]
51
+ """
52
+ motif_hits, matched_phrases = detect_motifs(text)
53
+ emotion_profile = get_emotion_profile(text)
54
+
55
+ # get raw model scores
56
+ toks = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
57
+ with torch.no_grad():
58
+ logits = model(**toks).logits.squeeze(0)
59
+ scores = torch.sigmoid(logits).cpu().numpy()
60
+
61
+ # pick up all labels whose score >= threshold
62
+ active = [lab for lab, sc in zip(LABELS, scores) if sc >= THRESHOLDS[lab]]
63
+
64
+ return {
65
+ "matched_phrases": matched_phrases,
66
+ "emotion_profile": emotion_profile,
67
+ "active_patterns": active
68
+ }
69
+
70
+
71
+ # ——— 4) Composite wrapper (for multiple inputs) ———————————————————————————————————
72
+ def analyze_composite(*texts):
73
+ """
74
+ Accept multiple text inputs, run analyze_message on each, and
75
+ return a single formatted string.
76
+ """
77
+ outputs = []
78
+ for idx, txt in enumerate(texts, start=1):
79
+ if not txt:
80
+ continue
81
+ r = analyze_message(txt)
82
+ block = (
83
+ f"── Message {idx} ──\n"
84
+ f"Matched Phrases: {r['matched_phrases']}\n"
85
+ f"Emotion Profile: {r['emotion_profile']}\n"
86
+ f"Active Patterns: {r['active_patterns']}\n"
87
+ )
88
+ outputs.append(block)
89
+ return "\n".join(outputs) if outputs else "Please enter at least one message."
90
 
 
 
 
 
91
 
92
+ # ——— 5) Gradio interface ————————————————————————————————————————————————
93
+ # adjust how many message inputs you want here:
94
+ message_inputs = [gr.Textbox(label=f"Message {i+1}") for i in range(3)]
 
 
95
 
96
  iface = gr.Interface(
97
+ fn=analyze_composite,
98
+ inputs=message_inputs,
99
+ outputs=gr.Textbox(label="Analysis"),
100
+ title="Tether Analyzer (no abuse-score / no DARVO)",
101
+ description="Detects motifs, emotions, and active abuse-patterns only."
102
  )
103
 
104
+ if __name__ == "__main__":
105
+ iface.launch()