File size: 6,591 Bytes
657c936
 
dfdbee3
657c936
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dfdbee3
 
 
 
 
657c936
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c8134bb
657c936
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dfdbee3
657c936
dfdbee3
 
 
657c936
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dfdbee3
657c936
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import os
import traceback
import uuid

from google.adk.agents import Agent, LlmAgent, SequentialAgent
from google.adk.models.google_llm import Gemini
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.memory import InMemoryMemoryService
from google.adk.tools import google_search, load_memory
from google.genai import types

# ---------- 1. API KEY SETUP (Hugging Face Secret) ----------

GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")

if not GOOGLE_API_KEY:
    # On HF, set this in: Settings -> Variables and secrets -> New secret: GOOGLE_API_KEY
    raise RuntimeError(
        "GOOGLE_API_KEY is not set. Please add it as a secret in your Hugging Face Space."
    )

os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY

# ---------- 2. ADK CONFIG ----------

retry_config = types.HttpRetryOptions(
    attempts=5,
    exp_base=7,
    initial_delay=1,
    http_status_codes=[429, 500, 503, 504],
)

APP_NAME = "FakeNewsApp"
USER_ID = "demo_user"

session_service = InMemorySessionService()
memory_service = InMemoryMemoryService()

# ----------3. generate session_id ------------
def generate_session_id():
    return str(uuid.uuid4())

# ---------- 4. AGENTS ----------

claim_extractor_agent = Agent(
    name="ClaimExtractorAgent",
    model=Gemini(
        model="gemini-2.5-flash-lite",
        retry_options=retry_config,
    ),
    instruction="""
    You will receive a WhatsApp forward or viral message.

    TASK:
    1. Identify ONE main factual claim in clean, simple form.
    2. Rewrite it in one sentence.
    3. Extract 3–5 search keywords.

    OUTPUT FORMAT:
    Claim: <cleaned claim>
    Keywords: <comma separated keywords>
    """,
    output_key="extracted_claim",
)

evidence_search_agent = Agent(
    name="EvidenceSearchAgent",
    model=Gemini(
        model="gemini-2.5-flash-lite",
        retry_options=retry_config,
    ),
    tools=[google_search],
    instruction="""
    You will receive extracted claim info:

    {extracted_claim}

    TASK:
    1. Use google_search with 2–3 queries:
       - "<claim> fact check"
       - "<keywords> news"
       - "<keywords> official site"
    2. Return 5–7 useful results with:
       - title
       - url
       - snippet
       - source type (gov, fact-check, news, blog)

    OUTPUT FORMAT:
    <list of sources in bullet points>
    """,
    output_key="search_results",
)

verdict_agent = Agent(
    name="VerdictAgent",
    model=Gemini(
        model="gemini-2.5-flash", # earlier using gemini-2.5-flash-lite
        retry_options=retry_config,
    ),
    instruction="""
    You will receive search results:

    {search_results}

    TASK:
    1. For each source, decide SUPPORT / REFUTE / IRRELEVANT.
    2. Produce a Markdown table:
       | Source | Type | Stance | Summary |
    3. Decide the final verdict:
       - Mostly refute β†’ Likely FALSE
       - Mostly support β†’ Likely TRUE
       - Mixed β†’ Partly true/misleading
       - No credible sources β†’ Unverified – Do not share
    4. Write a SIMPLE explanation for elderly users.

    OUTPUT FORMAT:
    Evidence Table:
    <table>

    Verdict:
    <verdict>

    Explanation:
    <simple explanation>
    """,
    output_key="final_report",
)

memory_agent = LlmAgent(
    name="MemoryAgent",
    model=Gemini(
        model="gemini-2.5-flash-lite",
        retry_options=retry_config,
    ),
    tools=[load_memory],
    instruction="""
    You are a background history fetcher. You are NOT a chatbot. 
    
    YOUR STRICT COMMANDS:
    1. IGNORE any input text you receive from previous agents.
    2. IMMEDIATELY call the function `load_memory` to get the user's past sessions.
    3. Once you receive the memory data, extract and list the distinct claims found.
    4. Return ONLY a bulleted list of the last 2 claims.
    
    IF NO MEMORY DATA IS RETURNED:
    - Output: "No previous checks found."
    
    DO NOT ask "Would you like me to?". DO NOT explain what you are doing. JUST RUN THE TOOL.
    """,
    output_key="recent_claims",
)

root_agent = SequentialAgent(
    name="FakeNewsPipeline",
    sub_agents=[
        claim_extractor_agent,
        evidence_search_agent,
        verdict_agent,
        memory_agent,
    ],
)

runner = Runner(
    agent=root_agent,
    app_name=APP_NAME,
    session_service=session_service,
    memory_service=memory_service,
)

# ---------- 5. PUBLIC FUNCTION USED BY GRADIO ----------

async def run_eldersafe(query: str, session_id: str = None) -> dict:
    if session_id is None:
        session_id = generate_session_id()
    """
    Runs the full ElderSafe pipeline and returns a dict:
    {
        "clean_claim": str,
        "final_report": str (markdown),
        "memory_context": str,
    }
    This is the function Gradio will call.
    """
    try:
        # Ensure session exists (ignore if already created)
        try:
            await session_service.create_session(
                app_name=APP_NAME,
                user_id=USER_ID,
                session_id=session_id,
            )
        except Exception:
            pass
        
        # Prepare ADK content input
        user_msg = types.Content(
            role="user",
            parts=[types.Part(text=query)],
        )

        # Run pipeline silently
        async for _ in runner.run_async(
            user_id=USER_ID,
            session_id=session_id,
            new_message=user_msg,
        ):
            pass

        # Get session and store in memory
        session = await session_service.get_session(
            app_name=APP_NAME,
            user_id=USER_ID,
            session_id=session_id,
        )

        await memory_service.add_session_to_memory(session)

        # Extract outputs
        claim = session.state.get("extracted_claim", "No claim extracted.")
        if isinstance(claim, str) and "Claim:" in claim:
            clean_claim = claim.split("Keywords:")[0].replace("Claim:", "").strip()
        else:
            clean_claim = str(claim)

        final_report = session.state.get("final_report", "Analysis failed.")
        memory_context = session.state.get("recent_claims", "")

        return {
            "clean_claim": clean_claim,
            "final_report": final_report,
            "memory_context": memory_context,
        }

    except Exception:
        # In case something goes wrong, return a debug string
        return {
            "clean_claim": query,
            "final_report": "❌ An error occurred:\n\n" + traceback.format_exc(),
            "memory_context": "",
        }