Danielchris145/TruthCheck-AI
0
1# app.py2import os3from flask import Flask, render_template, request, jsonify4from functools import lru_cache5import hashlib6import sqlite37import datetime8import json9 10from models.claim_extractor import ClaimExtractor11from models.keyword_extractor import KeywordExtractor12from models.evidence_retriever import EvidenceRetriever13from models.nli_classifier import NLIClassifier14from utils.similarity import calculate_similarity15from utils.config import Config16 17 18# Initialize models globally19claim_extractor = ClaimExtractor()20keyword_extractor = KeywordExtractor()21evidence_retriever = EvidenceRetriever()22nli_classifier = NLIClassifier()23 24 25class TruthCheckSystem:26 def __init__(self):27 self.claim_extractor = claim_extractor28 self.keyword_extractor = keyword_extractor29 self.evidence_retriever = evidence_retriever30 self.nli_classifier = nli_classifier31 self.cache = {}32 33 def _get_cache_key(self, text):34 """Generate cache key for claim"""35 return hashlib.md5(text.encode()).hexdigest()36 37 def verify_claim(self, text):38 """39 Enhanced fact verification with multi-evidence aggregation40 and consensus mechanism (similar to FactCheck system)41 """42 try:43 # Check cache44 cache_key = self._get_cache_key(text)45 if cache_key in self.cache:46 print("Returning cached result")47 return self.cache[cache_key]48 49 # Step 1: Extract claims50 claims = self.claim_extractor.extract_claims(text)51 if not claims:52 result = ("Low Confidence", 0.3, "No valid claims found. Please provide a clear factual statement.")53 self.cache[cache_key] = result54 return result55 56 claim = claims[0]57 58 # Step 2: Extract keywords59 keywords = self.keyword_extractor.extract_keywords(claim)60 61 # Step 3: Retrieve evidence from multiple sources62 evidence_items = self.evidence_retriever.get_evidence(keywords)63 64 if not evidence_items:65 result = ("Low Confidence", 0.3, "Not enough reliable evidence found.")66 self.cache[cache_key] = result67 return result68 69 # Step 4: Filter by semantic similarity70 relevant_evidence = []71 for item in evidence_items:72 similarity = calculate_similarity(claim, item['content'])73 if similarity > Config.SIMILARITY_THRESHOLD:74 item['similarity_score'] = similarity75 relevant_evidence.append(item)76 77 if not relevant_evidence:78 result = ("Low Confidence", 0.4, "No semantically relevant evidence found.")79 self.cache[cache_key] = result80 return result81 82 # Step 5: Sort by combined score (credibility + similarity)83 for item in relevant_evidence:84 item['combined_score'] = (85 item.get('credibility_score', 0.5) * 0.6 +86 item.get('similarity_score', 0.5) * 0.487 )88 89 relevant_evidence.sort(key=lambda x: x['combined_score'], reverse=True)90 91 # Step 6: Multi-Evidence NLI with Consensus Mechanism92 # Use top 4 evidence sources (as per FactCheck research)93 top_evidence = relevant_evidence[:4]94 95 nli_results = []96 for evidence_item in top_evidence:97 nli_result = self.nli_classifier.classify(claim, evidence_item['content'])98 nli_results.append({99 'nli': nli_result,100 'credibility': evidence_item.get('credibility_score', 0.5),101 'similarity': evidence_item.get('similarity_score', 0.5),102 'source': evidence_item.get('source', 'Unknown'),103 'url': evidence_item.get('url', '')104 })105 106 # Step 7: Weighted Consensus Voting107 entailment_score = 0108 contradiction_score = 0109 neutral_score = 0110 111 total_weight = 0112 for result in nli_results:113 # Weight by credibility and confidence114 weight = result['credibility'] * result['nli']['confidence']115 total_weight += weight116 117 if result['nli']['label'] == 'ENTAILMENT':118 entailment_score += weight119 elif result['nli']['label'] == 'CONTRADICTION':120 contradiction_score += weight121 else:122 neutral_score += weight123 124 # Normalize scores125 if total_weight > 0:126 entailment_score /= total_weight127 contradiction_score /= total_weight128 neutral_score /= total_weight129 130 # Step 8: Determine final label with consensus threshold131 consensus_threshold = 0.6 # Require 60% agreement132 133 max_score = max(entailment_score, contradiction_score, neutral_score)134 135 if max_score == entailment_score and entailment_score >= consensus_threshold:136 label = "True"137 final_confidence = entailment_score138 elif max_score == contradiction_score and contradiction_score >= consensus_threshold:139 label = "False"140 final_confidence = contradiction_score141 else:142 label = "Low Confidence"143 final_confidence = max(entailment_score, contradiction_score, neutral_score)144 145 # Step 9: Prepare evidence summary146 evidence_summary = self._format_evidence_summary(nli_results, top_evidence)147 148 result = (label, final_confidence, evidence_summary)149 150 # Cache result151 self.cache[cache_key] = result152 153 return result154 155 except Exception as e:156 print(f"Error during claim verification: {e}")157 import traceback158 traceback.print_exc()159 return ("Error", 0.0, f"An internal error occurred: {str(e)}")160 161 def _format_evidence_summary(self, nli_results, evidence_items):162 """Format evidence summary with sources and verdicts"""163 summary_parts = []164 165 summary_parts.append(f"**Analyzed {len(nli_results)} sources:**\n")166 167 for i, (nli_res, evidence) in enumerate(zip(nli_results, evidence_items), 1):168 source = nli_res['source']169 verdict = nli_res['nli']['label']170 confidence = nli_res['nli']['confidence']171 credibility = nli_res['credibility']172 url = nli_res['url']173 174 # Get snippet175 content = evidence.get('content', '')[:300]176 177 summary_parts.append(178 f"\n**Source {i}: {source}**\n"179 f"Verdict: {verdict} (Confidence: {confidence:.2%})\n"180 f"Credibility Score: {credibility:.2f}\n"181 f"Excerpt: {content}...\n"182 f"URL: {url}\n"183 )184 185 return "\n".join(summary_parts)186 187 188# Initialize system189truthcheck_system_instance = TruthCheckSystem()190 191 192DB_PATH = os.path.join(os.getcwd(), 'history.db')193 194def init_db():195 """Initialize SQLite database"""196 conn = sqlite3.connect(DB_PATH)197 c = conn.cursor()198 c.execute('''199 CREATE TABLE IF NOT EXISTS verifications (200 id INTEGER PRIMARY KEY AUTOINCREMENT,201 claim TEXT NOT NULL,202 label TEXT NOT NULL,203 confidence REAL,204 date TIMESTAMP DEFAULT CURRENT_TIMESTAMP205 )206 ''')207 conn.commit()208 conn.close()209 210init_db()211 212 213def create_app():214 app = Flask(__name__, static_folder='static', template_folder='templates')215 app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', Config.SECRET_KEY)216 app.config['DEBUG'] = Config.DEBUG217 218 @app.route('/')219 def index():220 return render_template('index.html')221 222 @app.route('/how-it-works')223 def how_it_works():224 return render_template('how_it_works.html')225 226 @app.route('/api-docs')227 def api_docs():228 return render_template('api.html')229 230 @app.route('/dashboard')231 def dashboard():232 return render_template('dashboard.html')233 234 @app.route('/api/history')235 def get_history():236 try:237 conn = sqlite3.connect(DB_PATH)238 conn.row_factory = sqlite3.Row239 c = conn.cursor()240 c.execute('SELECT * FROM verifications ORDER BY date DESC LIMIT 50')241 rows = c.fetchall()242 conn.close()243 244 history = []245 for row in rows:246 history.append({247 'id': row['id'],248 'claim': row['claim'],249 'label': row['label'],250 'confidence': row['confidence'],251 'date': row['date']252 })253 return jsonify(history)254 except Exception as e:255 return jsonify({'error': str(e)}), 500256 257 @app.route('/api/verify', methods=['POST'])258 def verify_claim_api():259 try:260 data = request.get_json()261 claim_text = data.get('claim', '')262 263 if not claim_text:264 return jsonify({'error': 'No claim provided'}), 400265 266 label, confidence, evidence = truthcheck_system_instance.verify_claim(claim_text)267 268 # Save to DB269 try:270 conn = sqlite3.connect(DB_PATH)271 c = conn.cursor()272 c.execute('INSERT INTO verifications (claim, label, confidence) VALUES (?, ?, ?)',273 (claim_text, label, float(confidence)))274 conn.commit()275 conn.close()276 except Exception as e:277 print(f"DB Error: {e}")278 279 result = {280 'label': label,281 'confidence': round(confidence, 3),282 'evidence': evidence,283 'claim': claim_text284 }285 286 return jsonify(result)287 288 except Exception as e:289 print(f"API error: {e}")290 return jsonify({'error': f'Server error: {str(e)}'}), 500291 292 @app.route('/health')293 def health_check():294 return jsonify({'status': 'healthy', 'message': 'TruthCheck is running.'})295 296 return app297 