File size: 6,578 Bytes
1190b8e
 
 
 
 
a535254
1190b8e
 
5db8377
a535254
1190b8e
a535254
1190b8e
 
b90e46a
 
 
 
 
 
 
 
a535254
 
 
5db8377
 
1190b8e
 
 
 
5db8377
a535254
1190b8e
3b47028
 
 
 
a535254
1190b8e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a535254
5db8377
 
1190b8e
a535254
5db8377
 
1190b8e
a535254
 
 
 
 
6133cad
1190b8e
a535254
1190b8e
a535254
1190b8e
a535254
1190b8e
a535254
1190b8e
a535254
1190b8e
a535254
 
b90e46a
 
 
a535254
 
 
 
1190b8e
a535254
6133cad
 
 
a535254
6133cad
 
a535254
6133cad
a535254
 
 
 
 
6133cad
a535254
1190b8e
 
a535254
 
 
 
 
b90e46a
 
 
a535254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1190b8e
 
 
a535254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186a3cb
a535254
 
 
6133cad
a535254
 
b90e46a
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import gradio as gr
from newspaper import Article
from modules.online_search import search_online
from modules.validation import calculate_truthfulness_score
from modules.knowledge_graph import search_kg
from modules.generate_explanation import generate_explanation
from dotenv import load_dotenv
import os
from concurrent.futures import ThreadPoolExecutor
from modules.record import DatabaseComponent  # Import DatabaseComponent

# Load environment variables
load_dotenv()

# Initialize database (handle connection failures)
db = None
try:
    db = DatabaseComponent()
except Exception as e:
    print(f"[ERROR] Database connection failed: {str(e)}")

# Initialize thread executor
executor = ThreadPoolExecutor(max_workers=3)

# Constants for file paths and API keys
KG_INDEX_PATH = "KG/news_category_index.faiss"
KG_DATASET_PATH = "KG/News_Category_Dataset_v3.json"
SEARCH_API_KEY = os.getenv("SEARCH_API_KEY")
SEARCH_BASE_URL = os.getenv("SEARCH_BASE_URL")
SEARCH_MODEL = os.getenv("SEARCH_MODEL")


# Function to process and verify news
def evaluate_news(news_input):
    if not news_input.strip():
        yield "**⚠️ Warning:** Input cannot be empty. Please enter news text or a URL."
        return

    yield "**Processing... Please wait.** ⏳"

    # Handle URL input
    if news_input.startswith("http"):
        try:
            article = Article(news_input)
            article.download()
            article.parse()
            news_text = article.title + ". " + article.text
        except Exception as e:
            yield f"**Error processing the URL:** {str(e)}"
            return
    else:
        news_text = news_input

    try:
        # Run search tasks concurrently
        future_kg = executor.submit(search_kg, news_text, KG_INDEX_PATH, KG_DATASET_PATH)
        future_online = executor.submit(search_online, news_text, SEARCH_API_KEY, SEARCH_BASE_URL, SEARCH_MODEL)

        # Wait for results
        kg_content = future_kg.result()
        online_search_results = future_online.result()

        # Extract citations from the search results
        citations = online_search_results.get("citations", [])  # List of sources
        first_citation = citations[0] if citations else "N/A"  # Store first citation in DB

        # Combine context
        context = online_search_results['message_content'] + '\n' + kg_content + '\n' + 'Device set to use cpu'

        # Compute truth score
        truth_score = calculate_truthfulness_score(info=news_text, context=context)
        truth_percentage = truth_score * 100  # Convert to percentage

        # Determine truth status
        if truth_score > 0.7:
            status = f"**{truth_percentage:.0f}% chances to be true** - This news is likely true."
        elif truth_score > 0.4:
            status = f"**{truth_percentage:.0f}% chances to be true** - This news is uncertain. Please verify further."
        else:
            status = f"**{truth_percentage:.0f}% chances to be true** - This news is unlikely to be true. Proceed with caution."

        # Save result in database if connection is available
        if db is not None:
            db.save_news_verification(news_text[:100], truth_score, first_citation)

        # Initial result
        result = f"**News:** \"{news_text[:300]}...\"\n\n"
        result += f"**Truthfulness Score:** {truth_score:.2f} ({status})\n\n"

        yield result  # Display initial results

        # Generate explanation asynchronously
        future_explanation = executor.submit(generate_explanation, news_text, context, truth_score)
        explanation = future_explanation.result()

        if explanation:
            result += f"**Explanation:** {explanation}\n\n"

        # Display sources
        if citations:
            result += "\n**Sources & References:**\n"
            for i, source in enumerate(citations[:5]):  # Show up to 5 sources
                result += f"{i + 1}. [{source}]({source})\n"

        yield result  # Final output with sources

    except Exception as e:
        yield f"**Error:** {str(e)}"


# Function to fetch dashboard data
def fetch_dashboard_data():
    if db is None:
        return "**⚠️ Database unavailable. Recent verification records cannot be displayed.**"

    total_news = db.get_total_news_count()
    last_10_news = db.get_last_10_news()

    # Generate table-style layout for recent verifications
    dashboard_info = f"**Total News Verified:** {total_news}\n\n"

    if last_10_news:
        table = "| # | News Title | Score (%) | Date Verified | Citation |\n"
        table += "|---|------------|-----------|--------------|----------|\n"

        for i, news in enumerate(last_10_news, start=1):
            truth_percentage = news['score'] * 100  # Convert to percentage
            citation = f"[Source]({news['citation']})" if news['citation'] != "N/A" else "N/A"
            table += f"| {i} | {news['title'][:50]}... | {truth_percentage:.0f}% | {news['timestamp']} | {citation} |\n"

        dashboard_info += table
    else:
        dashboard_info += "_No records found._"

    return dashboard_info


# Gradio Interface
with gr.Blocks(css="""
    .gradio-container { font-family: 'Georgia', serif; font-size: 16px; }
    h1, h2, h3 { font-family: 'Times New Roman', serif; }
    table { width: 100%; border-collapse: collapse; }
    th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
""") as demo:
    with gr.Tabs() as tabs:
        with gr.Tab("πŸ” Verify News"):
            gr.Markdown("# πŸ“° EchoTruth: News Verification")
            gr.Markdown("""
            **How it Works:**
            - Enter a news article **or** a URL.
            - Click **Check Truthfulness**.
            - Get a **truth score**, an explanation, and references.
            """)

            input_box = gr.Textbox(placeholder="Paste news text or URL...", label="News Input", lines=5)
            submit_btn = gr.Button("Check Truthfulness")
            output_box = gr.Markdown()
            submit_btn.click(fn=evaluate_news, inputs=[input_box], outputs=[output_box])

        with gr.Tab("πŸ“Š Dashboard") as dashboard_tab:
            gr.Markdown("# πŸ“Š Verification Dashboard")
            dashboard_output = gr.Markdown("_Click 'Refresh Data' to load latest records._")
            refresh_btn = gr.Button("πŸ”„ Refresh Data")
            refresh_btn.click(fn=fetch_dashboard_data, inputs=[], outputs=[dashboard_output])

    gr.Markdown("### **About EchoTruth**")
    gr.Markdown("EchoTruth uses AI to help users verify news authenticity in real-time.")

demo.launch()