import os from dotenv import load_dotenv import gradio as gr from groq import Groq from exa_py import Exa load_dotenv() groq_client = Groq(api_key=os.getenv("GROQ_API_KEY")) groq_model = "llama-3.3-70b-versatile" exa_client = Exa(api_key=os.getenv("EXA_API")) def generate_verification_query(text, speaker): prompt = f"""Someone said: "{text}" The speaker is: {speaker} Generate a specific, concise search query that would verify if this statement is factually true or false. The query should be specific enough to fact-check this exact claim. Return ONLY the search query, nothing else.""" try: response = groq_client.chat.completions.create( model=groq_model, messages=[{"role": "user", "content": prompt}], temperature=0.1, max_tokens=100 ) return response.choices[0].message.content.strip() except Exception as e: return f"Error generating query: {e}" def search_with_exa(query): try: response = exa_client.search( query=query, num_results=10, type="neural" ) sources = [] for result in response.results: sources.append({ "title": result.title or "No title", "url": result.url, "text": result.text[:500] if result.text else "" }) return sources except Exception as e: return f"Error searching: {e}" def agent_claim_made(claim, speaker, sources): sources_text = "\n\n".join([ f"Source {i+1}: {s['title']}\nURL: {s['url']}\nContent: {s['text']}" for i, s in enumerate(sources) ]) source_refs = "\n".join([f"Source {i+1}: [{s['title']}]({s['url']})" for i, s in enumerate(sources)]) prompt = f"""Based on the verification sources below, answer ONLY this question: CLAIM: "{claim}" SPEAKER: {speaker} SOURCES: {sources_text} ## 1. Was this claim made by the sayer? [Answer based on sources - did this person actually make this statement? Provide a clear, evidence-based answer with specific citations. IMPORTANT: Inline cite sources using markdown links like this: "According to [Source 1](URL), ..." or "As stated in [Source 2](URL), ..." Use the exact source titles and URLs from this reference list: {source_refs}]""" try: response = groq_client.chat.completions.create( model=groq_model, messages=[{"role": "user", "content": prompt}], temperature=0.2, max_tokens=800 ) return response.choices[0].message.content.strip() except Exception as e: return f"Error: {e}" def agent_action_done(claim, speaker, sources): sources_text = "\n\n".join([ f"Source {i+1}: {s['title']}\nURL: {s['url']}\nContent: {s['text']}" for i, s in enumerate(sources) ]) source_refs = "\n".join([f"Source {i+1}: [{s['title']}]({s['url']})" for i, s in enumerate(sources)]) prompt = f"""Based on the verification sources below, answer ONLY this question: CLAIM: "{claim}" SPEAKER: {speaker} SOURCES: {sources_text} ## 2. Was this action/event done by the sayer? [Answer based on sources - did the person actually do what they claimed? Provide a clear, evidence-based answer with specific citations. IMPORTANT: Inline cite sources using markdown links like this: "According to [Source 1](URL), ..." or "As stated in [Source 2](URL), ..." Use the exact source titles and URLs from this reference list: {source_refs}]""" try: response = groq_client.chat.completions.create( model=groq_model, messages=[{"role": "user", "content": prompt}], temperature=0.2, max_tokens=800 ) return response.choices[0].message.content.strip() except Exception as e: return f"Error: {e}" def agent_consequences(claim, speaker, sources): sources_text = "\n\n".join([ f"Source {i+1}: {s['title']}\nURL: {s['url']}\nContent: {s['text']}" for i, s in enumerate(sources) ]) source_refs = "\n".join([f"Source {i+1}: [{s['title']}]({s['url']})" for i, s in enumerate(sources)]) prompt = f"""Based on the verification sources below, answer ONLY this question: CLAIM: "{claim}" SPEAKER: {speaker} SOURCES: {sources_text} ## 3. What were the consequences of this act/statement? [Detail the consequences, outcomes, or impact based on sources. Provide a clear, evidence-based answer with specific citations. IMPORTANT: Inline cite sources using markdown links like this: "According to [Source 1](URL), ..." or "As stated in [Source 2](URL), ..." Use the exact source titles and URLs from this reference list: {source_refs}]""" try: response = groq_client.chat.completions.create( model=groq_model, messages=[{"role": "user", "content": prompt}], temperature=0.2, max_tokens=800 ) return response.choices[0].message.content.strip() except Exception as e: return f"Error: {e}" def agent_sources(sources): prompt = f"""Format these sources as clickable markdown links, one per line: SOURCES: {chr(10).join([f"{i+1}. {s['title']} - {s['url']}" for i, s in enumerate(sources)])} Return ONLY the markdown list, nothing else.""" try: response = groq_client.chat.completions.create( model=groq_model, messages=[{"role": "user", "content": prompt}], temperature=0.1, max_tokens=500 ) return response.choices[0].message.content.strip() except Exception as e: return "\n".join([f"- [{s['title']}]({s['url']})" for s in sources]) def verify_claim(text, speaker, progress=gr.Progress()): if not text or not speaker: return "Please provide both claim and speaker name." progress(0.1, desc="Generating search query...") query = generate_verification_query(text, speaker) if query.startswith("Error"): return query progress(0.3, desc="Searching Exa API...") sources = search_with_exa(query) if isinstance(sources, str) and sources.startswith("Error"): return sources if not sources: return "No sources found for verification." progress(0.5, desc="Agent 1: Verifying claim attribution...") part1 = agent_claim_made(text, speaker, sources) progress(0.65, desc="Agent 2: Verifying action/event...") part2 = agent_action_done(text, speaker, sources) progress(0.8, desc="Agent 3: Analyzing consequences...") part3 = agent_consequences(text, speaker, sources) progress(0.9, desc="Agent 4: Formatting sources...") part4 = agent_sources(sources) progress(1.0, desc="Complete!") return f"""## 1. Was this claim made by the sayer? {part1} ## 2. Was this action/event done by the sayer? {part2} ## 3. What were the consequences of this act/statement? {part3} ## Sources {part4}""" with gr.Blocks() as demo: gr.Markdown("## Fact Verification") gr.Markdown("Enter a claim and who said it, then click Verify. Four specialized agents will analyze the evidence.") with gr.Row(): selected_text = gr.Textbox(label="Claim", placeholder="Enter the claim to fact-check...") speaker = gr.Textbox(label="Who said this?", placeholder="e.g., Jeff Bezos, Elon Musk, etc.") verify_btn = gr.Button("Verify Claim", variant="primary", size="lg") status_msg = gr.Markdown(value="", visible=False) verification_output = gr.Markdown(label="Verification Result") def start_verification(text, speaker): # Show status message and make it visible return gr.update(value="🤖 **Agents are handling your research — just sit tight!**", visible=True) verify_btn.click( fn=start_verification, inputs=[selected_text, speaker], outputs=status_msg ).then( verify_claim, inputs=[selected_text, speaker], outputs=verification_output, show_progress="full" ).then( lambda: gr.update(visible=False), outputs=status_msg ) if __name__ == "__main__": demo.launch()