SamanthaStorm commited on
Commit
46468f4
Β·
verified Β·
1 Parent(s): 0f140ee

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +428 -368
app.py CHANGED
@@ -4,6 +4,7 @@ from transformers import AutoTokenizer, AutoModelForSequenceClassification
4
  import numpy as np
5
  import logging
6
  from datetime import datetime
 
7
 
8
  # Set up logging
9
  logging.basicConfig(
@@ -16,49 +17,146 @@ logging.basicConfig(
16
  )
17
  logger = logging.getLogger(__name__)
18
 
19
- class FallacyFinder:
20
  def __init__(self):
21
- # Fallacy labels mapping
22
  self.fallacy_labels = {
23
- 'ad_hominem': 'Ad Hominem (Personal Attack)',
24
- 'strawman': 'Strawman (Misrepresenting Argument)',
25
- 'whataboutism': 'Whataboutism (Deflecting with Counter-Accusations)',
26
- 'gaslighting': 'Gaslighting (Making Someone Question Reality)',
27
- 'false_dichotomy': 'False Dichotomy (Only Two Options When More Exist)',
28
- 'appeal_to_emotion': 'Appeal to Emotion (Using Emotions Instead of Logic)',
29
- 'darvo': 'DARVO (Deny, Attack, Reverse Victim/Offender)',
30
- 'moving_goalposts': 'Moving Goalposts (Changing Criteria When Challenged)',
31
- 'cherry_picking': 'Cherry Picking (Selecting Only Supporting Evidence)',
32
- 'appeal_to_authority': 'Appeal to Authority (Inappropriate Use of Authority)',
33
- 'slippery_slope': 'Slippery Slope (One Thing Leads to Extreme Consequences)',
34
- 'motte_and_bailey': 'Motte and Bailey (Defending Weak Position with Stronger One)',
35
- 'gish_gallop': 'Gish Gallop (Overwhelming with Many Weak Arguments)',
36
- 'kafkatrapping': 'Kafkatrapping (Denial Proves Guilt)',
37
- 'sealioning': 'Sealioning (Persistent Bad-Faith Requests for Evidence)',
38
- 'no_fallacy': 'No Fallacy (Clean, Logical Statement)'
39
  }
40
 
41
- # Fallacy descriptions
42
  self.fallacy_descriptions = {
43
- 'ad_hominem': "Attacking the person making the argument rather than addressing the argument itself.",
44
- 'strawman': "Misrepresenting someone's argument to make it easier to attack.",
45
- 'whataboutism': "Deflecting criticism by pointing out similar issues elsewhere instead of addressing the original concern.",
46
- 'gaslighting': "Making someone question their own memory, perception, or sanity.",
47
- 'false_dichotomy': "Presenting only two options when more alternatives exist.",
48
- 'appeal_to_emotion': "Using emotional manipulation instead of logical reasoning to persuade.",
49
- 'darvo': "A manipulation tactic: Deny the abuse, Attack the victim, Reverse Victim and Offender roles.",
50
- 'moving_goalposts': "Changing the criteria for evidence or success when the original criteria are met.",
51
- 'cherry_picking': "Selecting only data or examples that support your position while ignoring contradictory evidence.",
52
- 'appeal_to_authority': "Citing an authority figure inappropriately to support an argument.",
53
- 'slippery_slope': "Arguing that one small step will lead to a chain of negative consequences.",
54
- 'motte_and_bailey': "Switching between a defensible position (motte) and a controversial one (bailey).",
55
- 'gish_gallop': "Overwhelming an opponent with many rapid-fire arguments, regardless of their quality.",
56
- 'kafkatrapping': "A logical trap where denial of guilt is taken as evidence of guilt.",
57
- 'sealioning': "Persistently requesting evidence or debate in a way that appears reasonable but is actually harassment.",
58
- 'no_fallacy': "The statement appears to be logically sound and free of common fallacies."
59
  }
60
 
61
- # Load your trained model from Hugging Face
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  self.model = None
63
  self.tokenizer = None
64
  self.use_model = False
@@ -68,161 +166,50 @@ class FallacyFinder:
68
  self.tokenizer = AutoTokenizer.from_pretrained("SamanthaStorm/fallacyfinder")
69
  self.model = AutoModelForSequenceClassification.from_pretrained(
70
  "SamanthaStorm/fallacyfinder",
71
- num_labels=16 # Specify 16 fallacy classes
72
  )
73
  self.use_model = True
74
  logger.info("βœ… Model loaded successfully!")
75
  except Exception as e:
76
  logger.error(f"❌ Error loading model: {e}")
77
- logger.error("Model loading failed - cannot continue without trained model")
78
- raise e # Stop execution if model fails to load
79
-
80
- def predict_with_rules(self, text):
81
- """Rule-based fallacy detection for when model isn't available"""
82
- text_lower = text.lower().strip()
83
- detected_patterns = []
84
-
85
- # Ad Hominem patterns
86
- ad_hominem_patterns = [
87
- 'you\'re', 'you are', 'stupid', 'idiot', 'moron', 'dumb', 'ignorant',
88
- 'loser', 'pathetic', 'crazy', 'insane', 'nuts'
89
- ]
90
- matched_ad_hominem = [p for p in ad_hominem_patterns if p in text_lower]
91
- if matched_ad_hominem and ('you' in text_lower):
92
- detected_patterns.append(f"ad_hominem: {matched_ad_hominem}")
93
- logger.info(f"RULE-BASED: Ad Hominem detected - patterns: {matched_ad_hominem}")
94
- return 'ad_hominem', 0.85, detected_patterns
95
-
96
- # Strawman patterns
97
- strawman_patterns = [
98
- 'so you\'re saying', 'so you think', 'so you believe', 'so you want',
99
- 'according to you', 'you\'re advocating', 'you want to destroy'
100
- ]
101
- matched_strawman = [p for p in strawman_patterns if p in text_lower]
102
- if matched_strawman:
103
- detected_patterns.append(f"strawman: {matched_strawman}")
104
- logger.info(f"RULE-BASED: Strawman detected - patterns: {matched_strawman}")
105
- return 'strawman', 0.82, detected_patterns
106
-
107
- # Whataboutism patterns
108
- whataboutism_patterns = [
109
- 'what about', 'but what about', 'how about when', 'what about when you',
110
- 'but you', 'but when you'
111
- ]
112
- matched_whataboutism = [p for p in whataboutism_patterns if p in text_lower]
113
- if matched_whataboutism:
114
- detected_patterns.append(f"whataboutism: {matched_whataboutism}")
115
- logger.info(f"RULE-BASED: Whataboutism detected - patterns: {matched_whataboutism}")
116
- return 'whataboutism', 0.88, detected_patterns
117
-
118
- # Gaslighting patterns
119
- gaslighting_patterns = [
120
- 'that never happened', 'you\'re imagining', 'you\'re being too sensitive',
121
- 'you\'re overreacting', 'that\'s not what i said', 'you\'re remembering wrong',
122
- 'you\'re being paranoid', 'you\'re being dramatic'
123
- ]
124
- matched_gaslighting = [p for p in gaslighting_patterns if p in text_lower]
125
- if matched_gaslighting:
126
- detected_patterns.append(f"gaslighting: {matched_gaslighting}")
127
- logger.info(f"RULE-BASED: Gaslighting detected - patterns: {matched_gaslighting}")
128
- return 'gaslighting', 0.80, detected_patterns
129
-
130
- # False Dichotomy patterns
131
- false_dichotomy_patterns = [
132
- 'either you\'re', 'either we', 'you\'re either', 'it\'s either',
133
- 'with us or against us', 'love it or leave it'
134
- ]
135
- matched_false_dichotomy = [p for p in false_dichotomy_patterns if p in text_lower]
136
- if matched_false_dichotomy:
137
- detected_patterns.append(f"false_dichotomy: {matched_false_dichotomy}")
138
- logger.info(f"RULE-BASED: False Dichotomy detected - patterns: {matched_false_dichotomy}")
139
- return 'false_dichotomy', 0.78, detected_patterns
140
-
141
- # Appeal to Emotion patterns
142
- emotion_patterns = [
143
- 'think of the children', 'innocent people will', 'if you have any heart',
144
- 'how can you sleep', 'break the hearts', 'suffer needlessly'
145
- ]
146
- matched_emotion = [p for p in emotion_patterns if p in text_lower]
147
- if matched_emotion:
148
- detected_patterns.append(f"appeal_to_emotion: {matched_emotion}")
149
- logger.info(f"RULE-BASED: Appeal to Emotion detected - patterns: {matched_emotion}")
150
- return 'appeal_to_emotion', 0.83, detected_patterns
151
-
152
- # DARVO patterns
153
- darvo_patterns = [
154
- 'i never did that', 'you\'re the one who', 'i\'m the victim here',
155
- 'you\'re attacking me', 'i\'m innocent', 'you started this'
156
- ]
157
- matched_darvo = [p for p in darvo_patterns if p in text_lower]
158
- if matched_darvo and len(matched_darvo) >= 2:
159
- detected_patterns.append(f"darvo: {matched_darvo}")
160
- logger.info(f"RULE-BASED: DARVO detected - patterns: {matched_darvo}")
161
- return 'darvo', 0.75, detected_patterns
162
-
163
- # Moving Goalposts patterns
164
- goalpost_patterns = [
165
- 'that doesn\'t count because', 'you need better sources', 'that\'s not enough evidence',
166
- 'those statistics don\'t count', 'that\'s different because'
167
- ]
168
- matched_goalpost = [p for p in goalpost_patterns if p in text_lower]
169
- if matched_goalpost:
170
- detected_patterns.append(f"moving_goalposts: {matched_goalpost}")
171
- logger.info(f"RULE-BASED: Moving Goalposts detected - patterns: {matched_goalpost}")
172
- return 'moving_goalposts', 0.77, detected_patterns
173
-
174
- # Appeal to Authority patterns
175
- authority_patterns = [
176
- 'my doctor said', 'the expert said', 'the ceo thinks', 'einstein believed',
177
- 'my professor told me', 'the government says'
178
- ]
179
- matched_authority = [p for p in authority_patterns if p in text_lower]
180
- if matched_authority:
181
- detected_patterns.append(f"appeal_to_authority: {matched_authority}")
182
- logger.info(f"RULE-BASED: Appeal to Authority detected - patterns: {matched_authority}")
183
- return 'appeal_to_authority', 0.72, detected_patterns
184
-
185
- # Slippery Slope patterns
186
- slope_patterns = [
187
- 'if we allow this', 'this will lead to', 'give them an inch',
188
- 'this is just the beginning', 'where will it end', 'slippery slope'
189
- ]
190
- matched_slope = [p for p in slope_patterns if p in text_lower]
191
- if matched_slope:
192
- detected_patterns.append(f"slippery_slope: {matched_slope}")
193
- logger.info(f"RULE-BASED: Slippery Slope detected - patterns: {matched_slope}")
194
- return 'slippery_slope', 0.74, detected_patterns
195
-
196
- # Check for healthy communication (No Fallacy)
197
- healthy_patterns = [
198
- 'i understand your perspective', 'i disagree because', 'based on the evidence',
199
- 'i appreciate your input', 'let\'s examine', 'i think we need more information',
200
- 'i see the merit', 'thank you for sharing'
201
- ]
202
- matched_healthy = [p for p in healthy_patterns if p in text_lower]
203
- if matched_healthy:
204
- detected_patterns.append(f"no_fallacy: {matched_healthy}")
205
- logger.info(f"RULE-BASED: Healthy communication detected - patterns: {matched_healthy}")
206
- return 'no_fallacy', 0.90, detected_patterns
207
-
208
- # Default to no fallacy if nothing detected
209
- logger.info("RULE-BASED: No specific patterns detected, defaulting to no_fallacy")
210
- return 'no_fallacy', 0.60, ["no_specific_patterns"]
211
-
212
  def predict_fallacy(self, text):
213
- """Main prediction function using trained model only"""
214
  if not text.strip():
215
- return None, 0, "Please enter a message to analyze.", []
216
 
217
  logger.info(f"ANALYZING: '{text[:100]}{'...' if len(text) > 100 else ''}'")
218
 
219
  if not self.use_model or self.model is None:
220
- logger.error("Trained model not available - cannot analyze")
221
- return None, 0, "Model not loaded", []
222
 
223
- # Use trained model
224
  try:
225
- logger.info("Using trained model for prediction")
226
  inputs = self.tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
227
 
228
  with torch.no_grad():
@@ -231,236 +218,309 @@ class FallacyFinder:
231
  predicted_class_id = predictions.argmax().item()
232
  confidence = predictions.max().item()
233
 
234
- # Log all prediction scores
235
  label_keys = list(self.fallacy_labels.keys())
236
- prediction_scores = {}
237
- for i, label in enumerate(label_keys):
238
- score = predictions[0][i].item()
239
- prediction_scores[label] = f"{score:.3f}"
240
 
241
- logger.info(f"MODEL PREDICTIONS: {prediction_scores}")
 
 
 
242
 
243
- # Map to fallacy label
244
  predicted_label = label_keys[predicted_class_id]
245
- logger.info(f"MODEL RESULT: {predicted_label} (confidence: {confidence:.3f})")
246
-
247
- patterns_detected = [f"model_prediction: top_3: {sorted(prediction_scores.items(), key=lambda x: float(x[1]), reverse=True)[:3]}"]
248
 
249
- fallacy_name = self.fallacy_labels[predicted_label]
250
- description = self.fallacy_descriptions[predicted_label]
251
-
252
- logger.info(f"FINAL RESULT: {predicted_label} ({fallacy_name}) - confidence: {confidence}")
253
 
254
- return predicted_label, confidence, fallacy_name, description, patterns_detected
255
 
256
  except Exception as e:
257
  logger.error(f"Model prediction failed: {e}")
258
- return None, 0, "Prediction failed", []
259
-
260
- def analyze_message(self, message):
261
- """Analyze a message and return formatted results"""
262
- if not message.strip():
263
- return "Please enter a message to analyze.", "", ""
264
-
265
- predicted_label, confidence, fallacy_name, description, patterns = self.predict_fallacy(message)
266
 
267
- # Format confidence as percentage
268
- confidence_percent = f"{confidence * 100:.1f}%"
269
 
270
- # Determine confidence level and colors
271
- if confidence >= 0.8:
272
- confidence_level = "πŸ”΅ High Confidence"
273
- confidence_color = "high"
274
- elif confidence >= 0.6:
275
- confidence_level = "🟣 Medium Confidence"
276
- confidence_color = "medium"
277
- else:
278
- confidence_level = "🟒 Low Confidence"
279
- confidence_color = "low"
280
 
281
- # Format the main result with better structure
282
  if predicted_label == 'no_fallacy':
283
- result = f"βœ… **No Fallacy Detected**\n\n**Confidence:** {confidence_level} ({confidence_percent})"
 
 
284
  else:
285
- result = f"⚠️ **{fallacy_name}**\n\n**Confidence:** {confidence_level} ({confidence_percent})"
286
-
287
- # Add top predictions in a clean list format
288
- if patterns and patterns[0].startswith('model_prediction'):
289
- # Extract top 3 predictions from debug info
290
- try:
291
- import ast
292
- debug_str = patterns[0].replace('model_prediction: top_3: ', '')
293
- top_predictions = ast.literal_eval(debug_str)
294
-
295
- result += f"\n\n**Top Predictions:**"
296
- for i, (fallacy, score) in enumerate(top_predictions, 1):
297
- fallacy_display = self.fallacy_labels.get(fallacy, fallacy.replace('_', ' ').title())
298
- percentage = f"{float(score) * 100:.1f}%"
299
- result += f"\n{i}. {fallacy_display}: {percentage}"
300
-
301
- except:
302
- # Fallback if parsing fails
303
- result += f"\n\n**Debug Info:** Model prediction analysis available in logs"
304
 
305
- # Format the explanation
306
- explanation = f"**What this means:**\n{description}"
307
-
308
- # Add examples or tips based on fallacy type
309
- if predicted_label == 'ad_hominem':
310
- explanation += "\n\n**Better approach:** Focus on the argument itself rather than the person making it."
311
- elif predicted_label == 'strawman':
312
- explanation += "\n\n**Better approach:** Address the actual argument being made, not a distorted version of it."
313
- elif predicted_label == 'whataboutism':
314
- explanation += "\n\n**Better approach:** Address the original concern directly before discussing other issues."
315
- elif predicted_label == 'gaslighting':
316
- explanation += "\n\n**Better approach:** Acknowledge the other person's experience and work toward understanding."
317
- elif predicted_label == 'no_fallacy':
318
- explanation = "**Great communication!** This message appears to use logical reasoning and respectful language."
319
-
320
- # Log the final user-facing result
321
- logger.info(f"USER RESULT: {predicted_label} - {confidence_percent} confidence")
322
- logger.info("="*50)
323
-
324
- return result, explanation, confidence_color
325
 
326
- # Initialize the fallacy finder
327
- logger.info("Initializing Fallacy Finder...")
328
- finder = FallacyFinder()
329
- logger.info("Fallacy Finder initialized successfully")
330
 
331
- def analyze_fallacy(message):
332
- result, explanation, _ = finder.analyze_message(message) # Ignore 3rd return value
333
- return result, explanation
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
- # Create the Gradio interface
336
- with gr.Blocks(
337
- theme=gr.themes.Soft(primary_hue="blue", secondary_hue="indigo"),
338
- title="Fallacy Finder",
339
- css="""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  .gradio-container {
341
- max-width: 900px !important;
 
342
  }
343
- .high-confidence {
344
- border-left: 4px solid #3b82f6;
 
 
 
345
  }
346
- .medium-confidence {
347
- border-left: 4px solid #8b5cf6;
 
 
 
348
  }
349
- .low-confidence {
350
- border-left: 4px solid #10b981;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  }
352
  """
353
- ) as demo:
354
-
355
- gr.Markdown(
356
- """
357
- # πŸ” Fallacy Finder
358
-
359
- Analyze messages for logical fallacies and argumentative patterns. Enter any statement, argument, or message to identify potential fallacies and get suggestions for better communication.
360
-
361
- **Detects 15+ types of fallacies** including ad hominem, strawman, whataboutism, gaslighting, and more.
362
- """
363
- )
364
 
365
- with gr.Row():
366
- with gr.Column(scale=2):
367
- message_input = gr.Textbox(
368
- label="Enter a message to analyze",
369
- placeholder="e.g., 'You're just saying that because you're too young to understand politics'",
370
- lines=4,
371
- info="Paste any statement, argument, or message you want to check for logical fallacies"
372
- )
 
 
 
373
 
374
- analyze_btn = gr.Button("πŸ” Analyze for Fallacies", variant="primary", size="lg")
375
 
376
- with gr.Column(scale=1):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  gr.Markdown(
378
  """
379
- ### Quick Examples:
 
 
 
 
 
 
380
 
381
- **Ad Hominem:**
382
- *"You're too stupid to understand"*
383
 
384
- **Strawman:**
385
- *"So you want to destroy the economy?"*
 
 
 
386
 
387
- **Whataboutism:**
388
- *"What about when you did the same thing?"*
389
 
390
- **No Fallacy:**
391
- *"I disagree because the evidence shows..."*
 
 
 
 
392
  """
393
  )
394
-
395
- with gr.Row():
396
- with gr.Column():
397
- result_output = gr.Textbox(
398
- label="Analysis Result",
399
- lines=6,
400
- interactive=False
401
- )
402
-
403
- explanation_output = gr.Textbox(
404
- label="Explanation & Suggestions",
405
- lines=6,
406
- interactive=False
407
- )
408
-
409
- # Examples section
410
- gr.Markdown("## πŸ“‹ Try These Examples")
411
-
412
- example_messages = [
413
- "You're clearly too young and inexperienced to have a valid opinion on this topic",
414
- "So you're saying we should just let criminals run free and destroy society?",
415
- "What about when you lied to me last year? Why should I trust you now?",
416
- "That never happened, you're just being overly emotional and dramatic about nothing",
417
- "Either you support the troops or you hate America - there's no middle ground",
418
- "I understand your concerns, but based on the research I've seen, I think we should consider other options"
419
- ]
420
-
421
- examples = gr.Examples(
422
- examples=[[msg] for msg in example_messages],
423
- inputs=[message_input],
424
- outputs=[result_output, explanation_output],
425
- fn=analyze_fallacy,
426
- cache_examples=False
427
- )
428
-
429
- # Information section
430
- gr.Markdown(
431
- """
432
- ### 🎯 How It Works
433
 
434
- This tool uses an AI model trained to detect logical fallacies and argumentative patterns that can harm productive communication. It analyzes:
435
-
436
- - **Personal attacks** instead of addressing arguments
437
- - **Misrepresentations** of opposing viewpoints
438
- - **Emotional manipulation** instead of logical reasoning
439
- - **Deflection tactics** that avoid the main issue
440
- - **False choices** that ignore other options
441
-
442
- ### πŸ’‘ Tips for Better Arguments
443
-
444
- βœ… **Focus on the argument, not the person**
445
- βœ… **Address the actual position being argued**
446
- βœ… **Use evidence and logical reasoning**
447
- βœ… **Acknowledge valid points from the other side**
448
- βœ… **Stay on topic and address concerns directly**
449
- """
450
- )
451
-
452
- # Connect the function
453
- analyze_btn.click(
454
- fn=analyze_fallacy,
455
- inputs=[message_input],
456
- outputs=[result_output, explanation_output]
457
- )
 
 
 
458
 
459
  # Launch the app
460
  if __name__ == "__main__":
461
- logger.info("Starting Gradio interface...")
 
462
  demo.launch(
463
  share=True,
464
  server_name="0.0.0.0",
465
- server_port=7860
 
466
  )
 
4
  import numpy as np
5
  import logging
6
  from datetime import datetime
7
+ import re
8
 
9
  # Set up logging
10
  logging.basicConfig(
 
17
  )
18
  logger = logging.getLogger(__name__)
19
 
20
+ class EnhancedFallacyFinder:
21
  def __init__(self):
22
+ # Enhanced fallacy labels with better descriptions
23
  self.fallacy_labels = {
24
+ 'ad_hominem': 'Ad Hominem',
25
+ 'strawman': 'Strawman',
26
+ 'whataboutism': 'Whataboutism',
27
+ 'gaslighting': 'Gaslighting',
28
+ 'false_dichotomy': 'False Dichotomy',
29
+ 'appeal_to_emotion': 'Appeal to Emotion',
30
+ 'darvo': 'DARVO',
31
+ 'moving_goalposts': 'Moving Goalposts',
32
+ 'cherry_picking': 'Cherry Picking',
33
+ 'appeal_to_authority': 'Appeal to Authority',
34
+ 'slippery_slope': 'Slippery Slope',
35
+ 'motte_and_bailey': 'Motte and Bailey',
36
+ 'gish_gallop': 'Gish Gallop',
37
+ 'kafkatrapping': 'Kafkatrapping',
38
+ 'sealioning': 'Sealioning',
39
+ 'no_fallacy': 'Clean Communication'
40
  }
41
 
42
+ # Simplified, actionable descriptions
43
  self.fallacy_descriptions = {
44
+ 'ad_hominem': "Attacking the person instead of their argument",
45
+ 'strawman': "Misrepresenting someone's position to attack it easier",
46
+ 'whataboutism': "Deflecting by pointing to other issues",
47
+ 'gaslighting': "Making someone question their own reality",
48
+ 'false_dichotomy': "Presenting only two options when more exist",
49
+ 'appeal_to_emotion': "Using emotions to manipulate instead of logic",
50
+ 'darvo': "Deny, Attack, and Reverse victim/offender roles",
51
+ 'moving_goalposts': "Changing requirements when original ones are met",
52
+ 'cherry_picking': "Selecting only supporting evidence",
53
+ 'appeal_to_authority': "Misusing authority to support weak arguments",
54
+ 'slippery_slope': "Claiming one thing leads to extreme outcomes",
55
+ 'motte_and_bailey': "Switching between strong and weak positions",
56
+ 'gish_gallop': "Overwhelming with many rapid-fire weak arguments",
57
+ 'kafkatrapping': "Where denial of guilt proves guilt",
58
+ 'sealioning': "Persistent bad-faith requests for evidence",
59
+ 'no_fallacy': "Logical, respectful communication"
60
  }
61
 
62
+ # Rewrite suggestions - the most valuable feature
63
+ self.rewrite_suggestions = {
64
+ 'ad_hominem': {
65
+ 'problem': "Focuses on attacking the person",
66
+ 'better': "Focus on the argument: 'I disagree with your point because...'"
67
+ },
68
+ 'strawman': {
69
+ 'problem': "Misrepresents the other person's view",
70
+ 'better': "Address their actual position: 'I understand you're saying X, but I think Y because...'"
71
+ },
72
+ 'whataboutism': {
73
+ 'problem': "Deflects instead of addressing the issue",
74
+ 'better': "Address the concern first: 'You're right about X. Here's how we can fix it...'"
75
+ },
76
+ 'gaslighting': {
77
+ 'problem': "Makes the other person question reality",
78
+ 'better': "Acknowledge their experience: 'I remember it differently, let's figure out what happened...'"
79
+ },
80
+ 'false_dichotomy': {
81
+ 'problem': "Forces an either/or choice",
82
+ 'better': "Present more options: 'There are several ways we could approach this...'"
83
+ },
84
+ 'appeal_to_emotion': {
85
+ 'problem': "Uses emotions to manipulate",
86
+ 'better': "Use facts and logic: 'The evidence shows that...'"
87
+ },
88
+ 'darvo': {
89
+ 'problem': "Reverses victim and offender",
90
+ 'better': "Take responsibility: 'I understand your concern. Let me address it...'"
91
+ },
92
+ 'moving_goalposts': {
93
+ 'problem': "Changes requirements unfairly",
94
+ 'better': "Be consistent: 'Here's what I need to be convinced...'"
95
+ },
96
+ 'cherry_picking': {
97
+ 'problem': "Ignores contradictory evidence",
98
+ 'better': "Consider all evidence: 'While some data shows X, other studies show Y...'"
99
+ },
100
+ 'appeal_to_authority': {
101
+ 'problem': "Relies on inappropriate authority",
102
+ 'better': "Use relevant expertise: 'According to experts in this specific field...'"
103
+ },
104
+ 'slippery_slope': {
105
+ 'problem': "Assumes extreme consequences",
106
+ 'better': "Focus on immediate effects: 'This specific change would result in...'"
107
+ },
108
+ 'motte_and_bailey': {
109
+ 'problem': "Switches between positions",
110
+ 'better': "Be consistent: 'My position is X, and here's why...'"
111
+ },
112
+ 'gish_gallop': {
113
+ 'problem': "Overwhelms with too many points",
114
+ 'better': "Focus on key issues: 'The main concern is X because...'"
115
+ },
116
+ 'kafkatrapping': {
117
+ 'problem': "Makes denial proof of guilt",
118
+ 'better': "Allow for honest denial: 'Let's examine the evidence together...'"
119
+ },
120
+ 'sealioning': {
121
+ 'problem': "Persistently demands evidence in bad faith",
122
+ 'better': "Ask genuinely: 'I'd appreciate learning more about your perspective...'"
123
+ },
124
+ 'no_fallacy': {
125
+ 'problem': "None detected",
126
+ 'better': "Great communication! Clear, logical, and respectful."
127
+ }
128
+ }
129
+
130
+ # Categorized examples for better exploration
131
+ self.example_categories = {
132
+ "Personal Attacks": [
133
+ "You're too stupid to understand this basic concept",
134
+ "What would someone with your background know about this?",
135
+ "You're clearly too emotional to think rationally about this"
136
+ ],
137
+ "Deflection & Avoidance": [
138
+ "What about when you made the same mistake last year?",
139
+ "But what about all the problems with your solution?",
140
+ "That never happened, you're imagining things"
141
+ ],
142
+ "False Choices": [
143
+ "Either you support this or you hate progress",
144
+ "You're either with us or against us on this issue",
145
+ "We either act now or everything will be ruined"
146
+ ],
147
+ "Manipulation": [
148
+ "Think of the innocent children who will suffer",
149
+ "If you really cared about people, you'd support this",
150
+ "How can you sleep at night knowing this?"
151
+ ],
152
+ "Healthy Communication": [
153
+ "I understand your concerns, but here's why I disagree",
154
+ "Based on the evidence I've seen, I think we should consider this",
155
+ "I appreciate your perspective and want to discuss this further"
156
+ ]
157
+ }
158
+
159
+ # Load model
160
  self.model = None
161
  self.tokenizer = None
162
  self.use_model = False
 
166
  self.tokenizer = AutoTokenizer.from_pretrained("SamanthaStorm/fallacyfinder")
167
  self.model = AutoModelForSequenceClassification.from_pretrained(
168
  "SamanthaStorm/fallacyfinder",
169
+ num_labels=16
170
  )
171
  self.use_model = True
172
  logger.info("βœ… Model loaded successfully!")
173
  except Exception as e:
174
  logger.error(f"❌ Error loading model: {e}")
175
+ raise e
176
+
177
+ def get_confidence_display(self, confidence):
178
+ """Simplified traffic light confidence system"""
179
+ if confidence >= 0.85:
180
+ return "πŸ”΄ Strong Detection", "high", f"{confidence * 100:.0f}%"
181
+ elif confidence >= 0.70:
182
+ return "🟑 Likely Fallacy", "medium", f"{confidence * 100:.0f}%"
183
+ elif confidence >= 0.55:
184
+ return "🟠 Possible Issue", "low", f"{confidence * 100:.0f}%"
185
+ else:
186
+ return "🟒 Looks Clean", "clean", f"{confidence * 100:.0f}%"
187
+
188
+ def get_text_guidance(self, text):
189
+ """Provide real-time guidance as user types"""
190
+ if len(text.strip()) == 0:
191
+ return "πŸ’‘ Enter a message to analyze for logical fallacies"
192
+ elif len(text.strip()) < 10:
193
+ return "πŸ’‘ Try a longer example for better analysis"
194
+ elif len(text) > 500:
195
+ return "⚠️ Very long text - consider analyzing in smaller parts"
196
+ elif len(text) > 200:
197
+ return "πŸ“ Good length for comprehensive analysis"
198
+ else:
199
+ return "βœ… Perfect length for analysis"
200
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  def predict_fallacy(self, text):
202
+ """Main prediction function"""
203
  if not text.strip():
204
+ return None, 0, [], {}
205
 
206
  logger.info(f"ANALYZING: '{text[:100]}{'...' if len(text) > 100 else ''}'")
207
 
208
  if not self.use_model or self.model is None:
209
+ logger.error("Model not available")
210
+ return None, 0, [], {}
211
 
 
212
  try:
 
213
  inputs = self.tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
214
 
215
  with torch.no_grad():
 
218
  predicted_class_id = predictions.argmax().item()
219
  confidence = predictions.max().item()
220
 
221
+ # Get top 3 predictions for transparency
222
  label_keys = list(self.fallacy_labels.keys())
223
+ top_predictions = []
224
+ values, indices = torch.topk(predictions[0], 3)
 
 
225
 
226
+ for i in range(3):
227
+ label = label_keys[indices[i].item()]
228
+ score = values[i].item()
229
+ top_predictions.append((label, score))
230
 
 
231
  predicted_label = label_keys[predicted_class_id]
 
 
 
232
 
233
+ logger.info(f"MODEL RESULT: {predicted_label} (confidence: {confidence:.3f})")
 
 
 
234
 
235
+ return predicted_label, confidence, top_predictions, {}
236
 
237
  except Exception as e:
238
  logger.error(f"Model prediction failed: {e}")
239
+ return None, 0, [], {}
240
+
241
+ def format_analysis_result(self, predicted_label, confidence, top_predictions):
242
+ """Format the main analysis result with better visual design"""
243
+ if predicted_label is None:
244
+ return "❌ Analysis failed. Please try again.", "", ""
 
 
245
 
246
+ # Get confidence display
247
+ conf_display, conf_level, conf_percent = self.get_confidence_display(confidence)
248
 
249
+ # Get fallacy info
250
+ fallacy_name = self.fallacy_labels[predicted_label]
251
+ description = self.fallacy_descriptions[predicted_label]
252
+ suggestion = self.rewrite_suggestions[predicted_label]
 
 
 
 
 
 
253
 
254
+ # Format main result
255
  if predicted_label == 'no_fallacy':
256
+ icon = "βœ…"
257
+ main_result = f"{icon} **{fallacy_name}**"
258
+ result_color = "success"
259
  else:
260
+ icon = "⚠️"
261
+ main_result = f"{icon} **{fallacy_name} Detected**"
262
+ result_color = "warning"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
+ # Build result string
265
+ result = f"""
266
+ {main_result}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
 
268
+ **Confidence:** {conf_display} ({conf_percent})
 
 
 
269
 
270
+ **What this means:** {description}
271
+ """
272
+
273
+ # Add top predictions for transparency
274
+ if len(top_predictions) >= 2:
275
+ result += f"\n**Other possibilities:**"
276
+ for i, (label, score) in enumerate(top_predictions[1:3], 2):
277
+ fallacy_display = self.fallacy_labels[label]
278
+ percentage = f"{score * 100:.0f}%"
279
+ result += f"\n{i}. {fallacy_display}: {percentage}"
280
+
281
+ # Format suggestions
282
+ if predicted_label != 'no_fallacy':
283
+ suggestion_text = f"""
284
+ **πŸ’‘ How to improve:**
285
 
286
+ **The problem:** {suggestion['problem']}
287
+
288
+ **Better approach:** {suggestion['better']}
289
+ """
290
+ else:
291
+ suggestion_text = """
292
+ **πŸŽ‰ Excellent communication!**
293
+
294
+ This message uses logical reasoning and respectful language. Keep it up!
295
+
296
+ **What makes this good:**
297
+ β€’ Addresses the topic directly
298
+ β€’ Uses respectful language
299
+ β€’ Focuses on facts and reasoning
300
+ β€’ Acknowledges other perspectives
301
+ """
302
+
303
+ return result, suggestion_text, conf_level
304
+
305
+ def create_enhanced_interface():
306
+ """Create the enhanced Gradio interface"""
307
+
308
+ # Initialize the finder
309
+ logger.info("Initializing Enhanced Fallacy Finder...")
310
+ finder = EnhancedFallacyFinder()
311
+ logger.info("Enhanced Fallacy Finder initialized successfully")
312
+
313
+ # Analysis function
314
+ def analyze_message(message):
315
+ """Main analysis function called by interface"""
316
+ if not message.strip():
317
+ return "Please enter a message to analyze.", "", "clean"
318
+
319
+ predicted_label, confidence, top_predictions, _ = finder.predict_fallacy(message)
320
+ result, suggestion, conf_level = finder.format_analysis_result(predicted_label, confidence, top_predictions)
321
+
322
+ logger.info(f"USER RESULT: {predicted_label} - {confidence*100:.0f}% confidence")
323
+ return result, suggestion, conf_level
324
+
325
+ # Get guidance function
326
+ def get_guidance(text):
327
+ return finder.get_text_guidance(text)
328
+
329
+ # Custom CSS for better visual design
330
+ custom_css = """
331
  .gradio-container {
332
+ max-width: 1000px !important;
333
+ margin: auto;
334
  }
335
+ .high {
336
+ background: linear-gradient(90deg, #fee2e2, #fef2f2);
337
+ border-left: 4px solid #dc2626;
338
+ padding: 1rem;
339
+ border-radius: 8px;
340
  }
341
+ .medium {
342
+ background: linear-gradient(90deg, #fef3c7, #fffbeb);
343
+ border-left: 4px solid #d97706;
344
+ padding: 1rem;
345
+ border-radius: 8px;
346
  }
347
+ .low {
348
+ background: linear-gradient(90deg, #ddd6fe, #f3f4f6);
349
+ border-left: 4px solid #7c3aed;
350
+ padding: 1rem;
351
+ border-radius: 8px;
352
+ }
353
+ .clean {
354
+ background: linear-gradient(90deg, #dcfce7, #f0fdf4);
355
+ border-left: 4px solid #16a34a;
356
+ padding: 1rem;
357
+ border-radius: 8px;
358
+ }
359
+ .examples-grid {
360
+ display: grid;
361
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
362
+ gap: 1rem;
363
+ margin: 1rem 0;
364
+ }
365
+ .category-header {
366
+ font-weight: bold;
367
+ color: #374151;
368
+ margin-bottom: 0.5rem;
369
  }
370
  """
 
 
 
 
 
 
 
 
 
 
 
371
 
372
+ # Create the interface
373
+ with gr.Blocks(
374
+ theme=gr.themes.Soft(primary_hue="blue", secondary_hue="indigo"),
375
+ title="Enhanced Fallacy Finder",
376
+ css=custom_css
377
+ ) as demo:
378
+
379
+ # Header
380
+ gr.Markdown(
381
+ """
382
+ # πŸ” Fallacy Finder Pro
383
 
384
+ **Advanced AI-powered logical fallacy detection** - Analyze any message for argumentative fallacies and get specific suggestions for better communication.
385
 
386
+ ✨ **Enhanced with**: Real-time guidance β€’ Rewrite suggestions β€’ Confidence levels β€’ Categorized examples
387
+ """
388
+ )
389
+
390
+ # Main interface
391
+ with gr.Row():
392
+ with gr.Column(scale=3):
393
+ # Input section
394
+ message_input = gr.Textbox(
395
+ label="πŸ’¬ Enter your message",
396
+ placeholder="e.g., 'You're just saying that because you're too young to understand politics'",
397
+ lines=4,
398
+ info="Paste any statement, argument, or message to check for logical fallacies"
399
+ )
400
+
401
+ # Real-time guidance
402
+ guidance_output = gr.Textbox(
403
+ label="πŸ’‘ Guidance",
404
+ interactive=False,
405
+ max_lines=1
406
+ )
407
+
408
+ # Action buttons
409
+ with gr.Row():
410
+ analyze_btn = gr.Button("πŸ” Analyze Message", variant="primary", size="lg")
411
+ clear_btn = gr.Button("πŸ”„ Clear", variant="secondary")
412
+
413
+ with gr.Column(scale=2):
414
+ # Quick stats or tips
415
+ gr.Markdown(
416
+ """
417
+ ### 🎯 What We Detect
418
+
419
+ **Personal Attacks** β€’ **Strawman Arguments** β€’ **Whataboutism** β€’ **Gaslighting** β€’ **False Choices** β€’ **Emotional Manipulation** β€’ **And 10+ more...**
420
+
421
+ ### 🚦 Confidence Levels
422
+ πŸ”΄ **Strong Detection** (85%+)
423
+ 🟑 **Likely Fallacy** (70%+)
424
+ 🟠 **Possible Issue** (55%+)
425
+ 🟒 **Looks Clean** (<55%)
426
+ """
427
+ )
428
+
429
+ # Results section
430
+ with gr.Row():
431
+ with gr.Column():
432
+ result_output = gr.Textbox(
433
+ label="πŸ“Š Analysis Result",
434
+ lines=6,
435
+ interactive=False
436
+ )
437
+
438
+ suggestion_output = gr.Textbox(
439
+ label="πŸ’‘ Suggestions & Improvements",
440
+ lines=6,
441
+ interactive=False
442
+ )
443
+
444
+ # Enhanced examples section
445
+ gr.Markdown("## πŸ“š Try These Examples")
446
+
447
+ # Create example buttons for each category
448
+ for category, examples in finder.example_categories.items():
449
+ with gr.Accordion(f"{category}", open=False):
450
+ for example in examples:
451
+ example_btn = gr.Button(f"πŸ“ {example[:60]}{'...' if len(example) > 60 else ''}",
452
+ variant="secondary", size="sm")
453
+ example_btn.click(
454
+ lambda x=example: x,
455
+ outputs=message_input
456
+ )
457
+
458
+ # Information section
459
+ with gr.Accordion("πŸŽ“ Learn More", open=False):
460
  gr.Markdown(
461
  """
462
+ ### How This Works
463
+
464
+ Our AI model analyzes text patterns to identify logical fallacies that can harm productive communication. It's trained on thousands of examples to recognize:
465
+
466
+ - **Argumentative fallacies** that weaken reasoning
467
+ - **Manipulation tactics** that avoid real discussion
468
+ - **Respectful communication** patterns to encourage
469
 
470
+ ### Tips for Better Arguments
 
471
 
472
+ βœ… **Address the argument, not the person**
473
+ βœ… **Represent opposing views accurately**
474
+ βœ… **Use evidence and logical reasoning**
475
+ βœ… **Stay focused on the main issue**
476
+ βœ… **Acknowledge valid concerns**
477
 
478
+ ### About Confidence Scores
 
479
 
480
+ - **High confidence** = Clear fallacy pattern detected
481
+ - **Medium confidence** = Likely problematic, worth reviewing
482
+ - **Low confidence** = Possible issue, but context matters
483
+ - **Clean** = No concerning patterns found
484
+
485
+ *Remember: Context always matters in human communication!*
486
  """
487
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
 
489
+ # Connect functions
490
+ message_input.change(
491
+ fn=get_guidance,
492
+ inputs=[message_input],
493
+ outputs=[guidance_output]
494
+ )
495
+
496
+ analyze_btn.click(
497
+ fn=analyze_message,
498
+ inputs=[message_input],
499
+ outputs=[result_output, suggestion_output]
500
+ )
501
+
502
+ clear_btn.click(
503
+ fn=lambda: ("", "", "", ""),
504
+ outputs=[message_input, result_output, suggestion_output, guidance_output]
505
+ )
506
+
507
+ # Footer
508
+ gr.Markdown(
509
+ """
510
+ ---
511
+ **Fallacy Finder Pro** β€’ Built with ❀️ for better communication β€’ [Learn about logical fallacies](https://en.wikipedia.org/wiki/List_of_fallacies)
512
+ """
513
+ )
514
+
515
+ return demo
516
 
517
  # Launch the app
518
  if __name__ == "__main__":
519
+ logger.info("Starting Enhanced Gradio interface...")
520
+ demo = create_enhanced_interface()
521
  demo.launch(
522
  share=True,
523
  server_name="0.0.0.0",
524
+ server_port=7860,
525
+ show_error=True
526
  )