import gradio as gr import pandas as pd import numpy as np import plotly.express as px import re # Fallacy Lexicon containing keywords, regex, descriptions, and plain-English corrections FALLACY_PATTERNS = [ { "name": "Ad Hominem (Personal Attack)", "patterns": [ r'\b(idiot|fool|stupid|corrupt|liar|cheat|incompetent|naive|hypocrite|disgrace)\b', r'\b(you are|he is|she is) (unqualified|irrational|delusional)\b' ], "description": "Attacking the character or motives of a person rather than addressing their actual argument.", "correction": "Focus on the logic, evidence, or specific data points of the opponent's claim, not their character." }, { "name": "Slippery Slope (Extreme Projections)", "patterns": [ r'\b(inevitably lead to|slippery slope|disaster will follow|will cause the end of|road to ruin|downhill from here|avalanche of)\b', r'\bif we allow.*then everything.*will collapse\b' ], "description": "Claiming that a relatively small initial step will inevitably lead to a chain of disastrous events without supporting evidence.", "correction": "Provide empirical evidence showing a causal mechanism connecting the initial step to the final extreme outcome." }, { "name": "False Dilemma (Either/Or)", "patterns": [ r'\b(either we|either you|must choose between|no other choice|only two options|with us or against us|black or white)\b' ], "description": "Presenting only two alternative states or choices when in fact multiple middle grounds or other options exist.", "correction": "Acknowledge third-party options, compromise zones, or other collaborative solutions." }, { "name": "Appeal to False Authority", "patterns": [ r'\b(everyone knows|scientists agree that|as.*said once|common knowledge|unnamed sources say|trust me on this)\b' ], "description": "Claiming a statement must be true because an authority figure or 'everyone' asserts it, without citing verified evidence.", "correction": "Cite direct peer-reviewed academic studies, primary documents, or transparent data collections." }, { "name": "Circular Reasoning (Begging the Question)", "patterns": [ r'\b(because it is|obviously true since|by definition|self-evident|proved by the fact that it is)\b', r'\b(.*) is true because (\1) is true\b' ], "description": "An argument where the conclusion is already assumed in the premise, repeating the claim in different words.", "correction": "Introduce independent external facts, logic, or observations to support the premise." }, { "name": "Strawman (Caricaturing Opponent)", "patterns": [ r'\b(they want to destroy|their extreme goal|total ban on|completely wipe out|total elimination of|radical agenda to)\b' ], "description": "Oversimplifying, exaggerating, or misrepresenting an opponent's argument to make it easier to attack.", "correction": "State the opponent's argument in its strongest form (steelman) before attempting to refute it." } ] def deconstruct_rhetoric(text): if not text.strip(): return None, "
Please paste debate transcripts or editorials.
", "" text_lower = text.lower() highlighted_text = text fallacies_found = [] # Track statistics summary_counts = {item["name"]: 0 for item in FALLACY_PATTERNS} # Process each pattern for fallacy in FALLACY_PATTERNS: name = fallacy["name"] desc = fallacy["description"] corr = fallacy["correction"] for pat in fallacy["patterns"]: matches = list(re.finditer(pat, text_lower, re.IGNORECASE)) if matches: summary_counts[name] += len(matches) for m in matches: matched_str = m.group(0) fallacies_found.append({ "Rhetorical Indicator": matched_str, "Fallacy Category": name, "Logic Description": desc, "Constructive Correction": corr }) # Generate highlighted HTML (using span wraps) # To prevent nested replacement bugs, we replace with placeholders and substitute at the end replacements = {} for idx, item in enumerate(fallacies_found): indicator = item["Rhetorical Indicator"] placeholder = f"___PLACEHOLDER_{idx}___" # Case insensitive substitution (first occurrence) pattern = re.compile(r'\b' + re.escape(indicator) + r'\b', re.IGNORECASE) highlighted_text, count = pattern.subn(placeholder, highlighted_text, 1) if count > 0: replacements[placeholder] = f'{indicator}' for placeholder, span in replacements.items(): highlighted_text = highlighted_text.replace(placeholder, span) # Generate stats Plotly Chart df_counts = pd.DataFrame({ "Fallacy Category": list(summary_counts.keys()), "Count": list(summary_counts.values()) }) fig = px.bar( df_counts[df_counts["Count"] > 0], x="Count", y="Fallacy Category", orientation="h", color="Fallacy Category", template="plotly_dark", title="Detected Rhetorical Fallacy Counts" ) fig.update_layout(showlegend=False, paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', font_family="Inter") # Generate deconstructed warnings table warnings_html = "" if fallacies_found: df_fallacies = pd.DataFrame(fallacies_found) table_html = df_fallacies.to_html(classes="styled-table", index=False) warnings_html = f"""
{table_html}
""" else: warnings_html = "
🎉 Clean Argument Scorecard! No major fallacious connectors detected in this text.
" highlighted_container = f"""
{highlighted_text}
""" return fig, highlighted_container, warnings_html # Premium styling custom_css = """ body, .gradio-container { background-color: #0b0f19 !important; font-family: 'Inter', sans-serif !important; } h1, h2, h3 { color: #f8fafc !important; } .gr-button-primary { background: linear-gradient(135deg, #ef4444 0%, #b91c1c 100%) !important; border: none !important; } """ with gr.Blocks(theme=gr.themes.Soft(primary_hue="red", secondary_hue="slate"), css=custom_css) as demo: gr.HTML("""

⚖️ Argument Fallacy Mapper & Rhetoric Deconstructor

Scan speeches, debate transcripts, and editorials for logical fallacies and receive constructive reasoning suggestions.

""") with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📜 Paste Speech / Debate Transcript") text_input = gr.Textbox( label="Argument Text", placeholder="Paste speech copy here...", lines=12 ) audit_btn = gr.Button("🔍 Deconstruct Arguments", variant="primary") with gr.Column(scale=1): gr.Markdown("### 📊 Fallacy Incidence Breakdown") chart_output = gr.Plot() gr.Markdown("### 🎨 Annotated Rhetorical Passage Visualizer") highlighted_output = gr.HTML("
Submit text to analyze and highlight fallacious reasoning.
") gr.Markdown("### ⚡ Deconstructed Logic & Constructive Recommendations") warnings_output = gr.HTML("
Scorecard grid will render here.
") audit_btn.click( fn=deconstruct_rhetoric, inputs=[text_input], outputs=[chart_output, highlighted_output, warnings_output] ) if __name__ == "__main__": demo.launch()