File size: 3,584 Bytes
c9f668a
5a1d02b
c9f668a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a1d02b
c9f668a
 
 
 
 
 
 
4e11885
5a1d02b
c9f668a
 
4e11885
c9f668a
 
5a1d02b
4e11885
46d2bf5
 
c9f668a
46d2bf5
 
c9f668a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import os
from groq import Groq
from pathlib import Path

BASE_DIR = Path(__file__).parent.parent

FALLACY_DEFINITIONS = {
    "no fallacy": "The text contains no logical fallacy and the reasoning is sound",
    "ad hominem": "Attacking the person making the argument rather than the argument itself",
    "ad populum": "Claiming something is true because many people believe it",
    "appeal to emotion": "Manipulating emotions rather than using logical reasoning",
    "circular reasoning": "Using the conclusion as a premise in the argument",
    "equivocation": "Using an ambiguous term in multiple senses within the same argument",
    "fallacy of credibility": "Misusing or fabricating authority or credentials to support a claim",
    "fallacy of extension": "Misrepresenting someone's argument to make it easier to attack",
    "fallacy of logic": "A general error in the logical structure of the argument",
    "fallacy of relevance": "Using irrelevant information to support a conclusion",
    "false causality": "Assuming that because one thing follows another, it was caused by it",
    "false dilemma": "Presenting only two options when more alternatives exist",
    "faulty generalization": "Drawing a broad conclusion from insufficient or unrepresentative evidence",
    "intentional": "A deliberate and deceptive use of misleading reasoning"
}

DEFAULT_MODEL_ID = "llama-3.1-8b-instant"


class FallacyExplainer:

    DEFAULT_FALLACY_CLASSES = list(FALLACY_DEFINITIONS.keys())

    def __init__(self, fallacy_classes=None, fallacy_definitions=None):
        self._model_id = os.environ.get("EXPLAIN_MODEL_ID", DEFAULT_MODEL_ID)
        self._client = Groq(api_key=os.environ["GROQ_API_KEY"])
        self.fallacy_classes = fallacy_classes or self.DEFAULT_FALLACY_CLASSES
        self.fallacy_definitions = fallacy_definitions or FALLACY_DEFINITIONS
        print(f"FallacyExplainer ready (model: {self._model_id} via Groq)")

    def _generate_with_prompt(self, prompt_text, max_new_tokens=128):
        result = self._client.chat.completions.create(
            model=self._model_id,
            messages=[{"role": "user", "content": prompt_text}],
            max_tokens=max_new_tokens,
            temperature=0.1,
        )
        return result.choices[0].message.content.strip()

    def _load_prompt(self, path):
        with open(BASE_DIR / path, "r") as f:
            return f.read()

    def _fill_template(self, template, replacements):
        result = template
        for key, value in replacements.items():
            result = result.replace("{{" + key + "}}", value)
        return result

    def explain(self, input_text, final_label, query_results):
        template = self._load_prompt("proposed_prompts/explain.txt")
        prompt = self._fill_template(template, {
            "INPUT_TEXT": input_text,
            "DETECTED_FALLACY": final_label,
            "REASONING_CONTEXT": query_results["explanation"]
        })
        response = self._generate_with_prompt(prompt, max_new_tokens=120)

        explanation = ""
        highlighted_phrase = ""

        if "<explanation>" in response and "</explanation>" in response:
            explanation = response.split("<explanation>")[1].split("</explanation>")[0].strip()
        if "<fallacious_phrase>" in response and "</fallacious_phrase>" in response:
            highlighted_phrase = response.split("<fallacious_phrase>")[1].split("</fallacious_phrase>")[0].strip()

        return {
            "explanation": explanation,
            "highlighted_phrase": highlighted_phrase
        }