SamanthaStorm commited on
Commit
4afc141
·
verified ·
1 Parent(s): 9d64e69

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -87
app.py CHANGED
@@ -14,15 +14,21 @@ emotion_pipeline = hf_pipeline(
14
  )
15
 
16
  def get_emotion_profile(text):
 
 
 
17
  results = emotion_pipeline(text)
 
18
  if isinstance(results, list) and isinstance(results[0], list):
19
  results = results[0]
20
  return {r["label"].lower(): round(r["score"], 3) for r in results}
21
 
 
 
22
 
23
- # ——— 2) Abuse-patterns Model ——————————————————————————————————————————————
24
  model_name = "SamanthaStorm/tether-multilabel-v3"
25
- model = AutoModelForSequenceClassification.from_pretrained(model_name)
26
  tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
27
 
28
  LABELS = [
@@ -32,36 +38,23 @@ LABELS = [
32
  ]
33
 
34
  THRESHOLDS = {
35
- "blame shifting": 0.28, "contradictory statements": 0.27, "control": 0.08, "dismissiveness": 0.32,
36
- "gaslighting": 0.27, "guilt tripping": 0.31, "insults": 0.10, "obscure language": 0.55,
37
- "projection": 0.09, "recovery phase": 0.33, "threat": 0.15
 
 
 
 
 
 
 
 
38
  }
39
 
40
- # ——— 3) Emotional-tone tagging (detailed categories) —————————————————————————————
41
- def get_emotional_tone_tag(emotion_profile, patterns):
42
- # unpack emotion scores
43
- sadness = emotion_profile.get("sadness", 0)
44
- joy = emotion_profile.get("joy", 0)
45
- neutral = emotion_profile.get("neutral", 0)
46
- disgust = emotion_profile.get("disgust", 0)
47
- anger = emotion_profile.get("anger", 0)
48
- fear = emotion_profile.get("fear", 0)
49
-
50
  def get_emotional_tone_tag(emotion_profile, patterns, text_lower):
51
  """
52
- 18 possible tone tags:
53
- 1–7 = original nuanced tones
54
- 8 = indignant reproach
55
- 9 = confrontational
56
- 10 = passive aggression
57
- 11 = sarcastic mockery
58
- 12 = menacing threat
59
- 13 = pleading concern
60
- 14 = fear-mongering
61
- 15 = disbelieving accusation
62
- 16 = empathetic solidarity
63
- 17 = assertive boundary
64
- 18 = stonewalling
65
  """
66
  sadness = emotion_profile.get("sadness", 0)
67
  joy = emotion_profile.get("joy", 0)
@@ -73,57 +66,57 @@ def get_emotional_tone_tag(emotion_profile, patterns, text_lower):
73
 
74
  # 1. Performative Regret
75
  if (
76
- sadness > 0.4
77
- and any(p in patterns for p in ["blame shifting", "guilt tripping", "recovery phase"])
78
  ):
79
  return "performative regret"
80
 
81
  # 2. Coercive Warmth
82
  if (
83
- (joy > 0.3 or sadness > 0.4)
84
- and any(p in patterns for p in ["control", "gaslighting"])
85
  ):
86
  return "coercive warmth"
87
 
88
  # 3. Cold Invalidation
89
  if (
90
- (neutral + disgust) > 0.5
91
- and any(p in patterns for p in ["dismissiveness", "projection", "obscure language"])
92
  ):
93
  return "cold invalidation"
94
 
95
  # 4. Genuine Vulnerability
96
  if (
97
- (sadness + fear) > 0.5
98
- and all(p == "recovery phase" for p in patterns)
99
  ):
100
  return "genuine vulnerability"
101
 
102
  # 5. Emotional Threat
103
  if (
104
- (anger + disgust) > 0.5
105
- and any(p in patterns for p in ["control", "threat", "insults", "dismissiveness"])
106
  ):
107
  return "emotional threat"
108
 
109
  # 6. Weaponized Sadness
110
  if (
111
- sadness > 0.6
112
- and any(p in patterns for p in ["guilt tripping", "projection"])
113
  ):
114
  return "weaponized sadness"
115
 
116
  # 7. Toxic Resignation
117
  if (
118
- neutral > 0.5
119
- and any(p in patterns for p in ["dismissiveness", "obscure language"])
120
  ):
121
  return "toxic resignation"
122
 
123
  # 8. Indignant Reproach
124
  if (
125
- anger > 0.5
126
- and any(p in patterns for p in ["guilt tripping", "contradictory statements"])
127
  ):
128
  return "indignant reproach"
129
 
@@ -133,8 +126,8 @@ def get_emotional_tone_tag(emotion_profile, patterns, text_lower):
133
 
134
  # 10. Passive Aggression
135
  if (
136
- neutral > 0.6
137
- and any(p in patterns for p in ["dismissiveness", "projection"])
138
  ):
139
  return "passive aggression"
140
 
@@ -148,9 +141,9 @@ def get_emotional_tone_tag(emotion_profile, patterns, text_lower):
148
 
149
  # 13. Pleading Concern
150
  if (
151
- sadness > 0.3
152
- and any(k in text_lower for k in ["sorry", "apolog", "forgive"])
153
- and not patterns
154
  ):
155
  return "pleading concern"
156
 
@@ -176,45 +169,31 @@ def get_emotional_tone_tag(emotion_profile, patterns, text_lower):
176
 
177
  return None
178
 
179
-
180
-
181
- # ——— 4) Single-message analysis —————————————————————————————————————————————
182
  def analyze_message(text):
183
- # 1) emotion profiling
 
184
  emotion_profile = get_emotion_profile(text)
185
-
186
- # 2) abuse-pattern classification
187
  toks = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
188
  with torch.no_grad():
189
  logits = model(**toks).logits.squeeze(0)
190
  scores = torch.sigmoid(logits).cpu().numpy()
191
-
192
- # 3) identify active patterns
193
- active_patterns = [
194
- label for label, prob in zip(LABELS, scores)
195
- if prob >= THRESHOLDS[label]
196
- ]
197
-
198
- # 4) add recovery-phase on apology keywords
199
- low = text.lower()
200
- if any(k in low for k in ["sorry", "apolog", "forgive"]):
201
- if "recovery phase" not in active_patterns:
202
- active_patterns.append("recovery phase")
203
-
204
- # 5) tone tagging with detailed rules
205
- tone_tag = get_emotional_tone_tag(emotion_profile, active_patterns)
206
-
207
  return {
208
  "emotion_profile": emotion_profile,
209
  "active_patterns": active_patterns,
210
  "tone_tag": tone_tag
211
  }
212
 
213
-
214
- # ——— 5) Composite wrapper (handles uploads + text boxes) ——————————————————————————
215
  def analyze_composite(uploaded_file, *texts):
216
  outputs = []
217
-
218
  # file upload
219
  if uploaded_file is not None:
220
  raw = uploaded_file.read()
@@ -227,7 +206,6 @@ def analyze_composite(uploaded_file, *texts):
227
  content = raw.decode("utf-8")
228
  except UnicodeDecodeError:
229
  content = raw.decode("latin-1")
230
-
231
  r = analyze_message(content)
232
  outputs.append(
233
  "── Uploaded File ──\n"
@@ -235,7 +213,6 @@ def analyze_composite(uploaded_file, *texts):
235
  f"Active Patterns : {r['active_patterns']}\n"
236
  f"Emotional Tone : {r['tone_tag']}\n"
237
  )
238
-
239
  # text inputs
240
  for idx, txt in enumerate(texts, start=1):
241
  if not txt:
@@ -247,25 +224,19 @@ def analyze_composite(uploaded_file, *texts):
247
  f"Active Patterns : {r['active_patterns']}\n"
248
  f"Emotional Tone : {r['tone_tag']}\n"
249
  )
250
-
251
  if not outputs:
252
  return "Please enter at least one message."
253
-
254
  return "\n".join(outputs)
255
 
256
-
257
- # ——— 6) Gradio interface ————————————————————————————————————————————————
258
  message_inputs = [gr.Textbox(label=f"Message {i+1}") for i in range(3)]
259
-
260
  iface = gr.Interface(
261
  fn=analyze_composite,
262
- inputs=[
263
- gr.File(file_types=[".txt", ".png", ".jpg", ".jpeg"], label="Upload text or image")
264
- ] + message_inputs,
265
  outputs=gr.Textbox(label="Analysis"),
266
- title="Tether Analyzer (detailed tone tags)",
267
- description="Emotion profiling, pattern tags, and nuanced tone categories—no abuse score or DARVO."
268
  )
269
 
270
  if __name__ == "__main__":
271
- iface.launch()
 
14
  )
15
 
16
  def get_emotion_profile(text):
17
+ """
18
+ Returns a dict of emotion scores for the input text.
19
+ """
20
  results = emotion_pipeline(text)
21
+ # Some pipelines return a list of lists
22
  if isinstance(results, list) and isinstance(results[0], list):
23
  results = results[0]
24
  return {r["label"].lower(): round(r["score"], 3) for r in results}
25
 
26
+ # apology keywords for pleading concern
27
+ APOLOGY_KEYWORDS = ["sorry", "apolog", "forgive"]
28
 
29
+ # ——— 2) Abuse-Patterns Model ——————————————————————————————————————————————
30
  model_name = "SamanthaStorm/tether-multilabel-v3"
31
+ model = AutoModelForSequenceClassification.from_pretrained(model_name)
32
  tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
33
 
34
  LABELS = [
 
38
  ]
39
 
40
  THRESHOLDS = {
41
+ "blame shifting": 0.28,
42
+ "contradictory statements": 0.27,
43
+ "control": 0.08,
44
+ "dismissiveness": 0.32,
45
+ "gaslighting": 0.27,
46
+ "guilt tripping": 0.31,
47
+ "insults": 0.10,
48
+ "obscure language": 0.55,
49
+ "projection": 0.09,
50
+ "recovery phase": 0.33,
51
+ "threat": 0.15
52
  }
53
 
54
+ # ——— 3) Emotional-Tone Tagging —————————————————————————————————————————————
 
 
 
 
 
 
 
 
 
55
  def get_emotional_tone_tag(emotion_profile, patterns, text_lower):
56
  """
57
+ Assigns one of 18 nuanced tone categories based on emotion scores, patterns, and text.
 
 
 
 
 
 
 
 
 
 
 
 
58
  """
59
  sadness = emotion_profile.get("sadness", 0)
60
  joy = emotion_profile.get("joy", 0)
 
66
 
67
  # 1. Performative Regret
68
  if (
69
+ sadness > 0.4 and
70
+ any(p in patterns for p in ["blame shifting", "guilt tripping", "recovery phase"])
71
  ):
72
  return "performative regret"
73
 
74
  # 2. Coercive Warmth
75
  if (
76
+ (joy > 0.3 or sadness > 0.4) and
77
+ any(p in patterns for p in ["control", "gaslighting"])
78
  ):
79
  return "coercive warmth"
80
 
81
  # 3. Cold Invalidation
82
  if (
83
+ (neutral + disgust) > 0.5 and
84
+ any(p in patterns for p in ["dismissiveness", "projection", "obscure language"])
85
  ):
86
  return "cold invalidation"
87
 
88
  # 4. Genuine Vulnerability
89
  if (
90
+ (sadness + fear) > 0.5 and
91
+ all(p == "recovery phase" for p in patterns)
92
  ):
93
  return "genuine vulnerability"
94
 
95
  # 5. Emotional Threat
96
  if (
97
+ (anger + disgust) > 0.5 and
98
+ any(p in patterns for p in ["control", "threat", "insults", "dismissiveness"])
99
  ):
100
  return "emotional threat"
101
 
102
  # 6. Weaponized Sadness
103
  if (
104
+ sadness > 0.6 and
105
+ any(p in patterns for p in ["guilt tripping", "projection"])
106
  ):
107
  return "weaponized sadness"
108
 
109
  # 7. Toxic Resignation
110
  if (
111
+ neutral > 0.5 and
112
+ any(p in patterns for p in ["dismissiveness", "obscure language"])
113
  ):
114
  return "toxic resignation"
115
 
116
  # 8. Indignant Reproach
117
  if (
118
+ anger > 0.5 and
119
+ any(p in patterns for p in ["guilt tripping", "contradictory statements"])
120
  ):
121
  return "indignant reproach"
122
 
 
126
 
127
  # 10. Passive Aggression
128
  if (
129
+ neutral > 0.6 and
130
+ any(p in patterns for p in ["dismissiveness", "projection"])
131
  ):
132
  return "passive aggression"
133
 
 
141
 
142
  # 13. Pleading Concern
143
  if (
144
+ sadness > 0.3 and
145
+ any(k in text_lower for k in APOLOGY_KEYWORDS) and
146
+ not patterns
147
  ):
148
  return "pleading concern"
149
 
 
169
 
170
  return None
171
 
172
+ # ——— 4) Single-message Analysis ———————————————————————————————————————————
 
 
173
  def analyze_message(text):
174
+ text_lower = text.lower()
175
+ # 1) Emotion
176
  emotion_profile = get_emotion_profile(text)
177
+ # 2) Patterns
 
178
  toks = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
179
  with torch.no_grad():
180
  logits = model(**toks).logits.squeeze(0)
181
  scores = torch.sigmoid(logits).cpu().numpy()
182
+ active_patterns = [lab for lab, sc in zip(LABELS, scores) if sc >= THRESHOLDS[lab]]
183
+ # append recovery-phase if apology
184
+ if any(k in text_lower for k in APOLOGY_KEYWORDS) and "recovery phase" not in active_patterns:
185
+ active_patterns.append("recovery phase")
186
+ # 3) Tone
187
+ tone_tag = get_emotional_tone_tag(emotion_profile, active_patterns, text_lower)
 
 
 
 
 
 
 
 
 
 
188
  return {
189
  "emotion_profile": emotion_profile,
190
  "active_patterns": active_patterns,
191
  "tone_tag": tone_tag
192
  }
193
 
194
+ # ——— 5) Composite Wrapper ————————————————————————————————————————————————
 
195
  def analyze_composite(uploaded_file, *texts):
196
  outputs = []
 
197
  # file upload
198
  if uploaded_file is not None:
199
  raw = uploaded_file.read()
 
206
  content = raw.decode("utf-8")
207
  except UnicodeDecodeError:
208
  content = raw.decode("latin-1")
 
209
  r = analyze_message(content)
210
  outputs.append(
211
  "── Uploaded File ──\n"
 
213
  f"Active Patterns : {r['active_patterns']}\n"
214
  f"Emotional Tone : {r['tone_tag']}\n"
215
  )
 
216
  # text inputs
217
  for idx, txt in enumerate(texts, start=1):
218
  if not txt:
 
224
  f"Active Patterns : {r['active_patterns']}\n"
225
  f"Emotional Tone : {r['tone_tag']}\n"
226
  )
 
227
  if not outputs:
228
  return "Please enter at least one message."
 
229
  return "\n".join(outputs)
230
 
231
+ # ——— 6) Gradio Interface ————————————————————————————————————————————————
 
232
  message_inputs = [gr.Textbox(label=f"Message {i+1}") for i in range(3)]
 
233
  iface = gr.Interface(
234
  fn=analyze_composite,
235
+ inputs=[gr.File(file_types=[".txt", ".png", ".jpg", ".jpeg"], label="Upload text or image")] + message_inputs,
 
 
236
  outputs=gr.Textbox(label="Analysis"),
237
+ title="Tether Analyzer (extended tone tags)",
238
+ description="Emotion profiling, pattern tags, and a wide set of nuanced tone categories—no abuse score or DARVO."
239
  )
240
 
241
  if __name__ == "__main__":
242
+ iface.launch()