File size: 5,190 Bytes
39bbca0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""
Decision Engine for VeriLens AI
Combines ML prediction, verification similarity, source credibility,
and NLP analysis into a final verdict.
"""

from __future__ import annotations
from dataclasses import dataclass, field

@dataclass
class Decision:
    prediction: str        # "REAL", "FAKE", or "UNCERTAIN"
    confidence: int        # 0 – 100
    explanation: str
    factors: dict = field(default_factory=dict)

def make_decision(
    ml_label: str,
    ml_confidence: float,
    similarity_score: float,
    sources_verified: bool,
    suspicious_info: dict,
    high_trust_count: int = 0,
    low_trust_count: int = 0,
) -> Decision:
    """Weighted decision combining multiple signals."""

    # ── ML score contribution (0-45) ────────────────────────────────────────
    if ml_label == "FAKE":
        ml_score = (1 - ml_confidence) * 45  
    elif ml_label == "REAL":
        ml_score = ml_confidence * 45         
    else:
        ml_score = 22.5  

    # ── Verification score contribution (0-25) ──────────────────────────────
    if sources_verified:
        verify_score = similarity_score * 25
    else:
        verify_score = 12.5  

    # ── Source credibility contribution (0-15) ──────────────────────────────
    if high_trust_count + low_trust_count > 0:
        cred_ratio = high_trust_count / (high_trust_count + low_trust_count)
        cred_score = cred_ratio * 15
    elif sources_verified:
        cred_score = 7.5
    else:
        cred_score = 7.5

    # ── Suspicious language penalty (0-15) ──────────────────────────────────
    sus_count = suspicious_info.get("total_suspicious_count", 0)
    if sus_count == 0:
        sus_score = 15
    elif sus_count <= 2:
        sus_score = 10
    elif sus_count <= 5:
        sus_score = 5
    else:
        sus_score = 0

    # ── Aggregate ───────────────────────────────────────────────────────────
    total = ml_score + verify_score + cred_score + sus_score  
    total = max(0, min(100, total))

    # ── Guard: prevent FAKE ML prediction from flipping to Real ─────────
    ml_fake_overridden = False
    if ml_label == "FAKE" and ml_confidence >= 0.6 and total >= 65:
        total = 55
        ml_fake_overridden = True

    # ── Decide verdict (STANDARDIZED TO UPPERCASE) ──────────────────────
    if total >= 65:
        prediction = "REAL"
    elif total <= 40:
        prediction = "FAKE"
    else:
        prediction = "UNCERTAIN"

    # ── Confidence relative to the prediction ───────────────────────────
    if prediction == "REAL":
        confidence = int(round(total))           
    elif prediction == "FAKE":
        confidence = 100 - int(round(total))     
    else:
        distance = abs(total - 52.5)
        confidence = max(30, min(50, int(round(50 - distance))))

    # ── Build explanation ───────────────────────────────────────────────────
    explanations: list[str] = []

    if ml_label == "FAKE":
        explanations.append(f"The AI model classified this as FAKE with {ml_confidence:.0%} confidence.")
        if ml_fake_overridden:
            explanations.append("Although related articles exist online, they may be debunking the claim rather than confirming it.")
    elif ml_label == "REAL":
        explanations.append(f"The AI model classified this as REAL with {ml_confidence:.0%} confidence.")
    else:
        explanations.append("The AI model could not reach a strong conclusion.")

    if sources_verified:
        if similarity_score > 0.6:
            explanations.append("The claim is well-corroborated by multiple online sources.")
        elif similarity_score > 0.3:
            explanations.append("Some related articles were found, but corroboration is partial.")
        else:
            explanations.append("Very few matching sources were found online.")
    else:
        explanations.append("Internet verification was not available; the verdict relies on AI analysis.")

    if sus_count > 3:
        explanations.append("High levels of suspicious, sensationalist, or emotional language detected.")
    elif sus_count > 0:
        explanations.append("Minor suspicious language patterns were noted.")

    explanation = " ".join(explanations)

    factors = {
        "ml_score": round(ml_score, 2),
        "verification_score": round(verify_score, 2),
        "credibility_score": round(cred_score, 2),
        "language_score": round(sus_score, 2),
    }

    return Decision(
        prediction=prediction,
        confidence=confidence,
        explanation=explanation,
        factors=factors,
    )