# app.py — Multi-Agent Research Generator # Dark theme, tabbed layout, animated agent pipeline import streamlit as st import time from graph.research_graph import build_graph # ── Page config ──────────────────────────────────────────────── st.set_page_config( page_title="ResearchOS — Multi-Agent AI", page_icon="⬡", layout="wide", initial_sidebar_state="collapsed" ) # ── Init session state ─────────────────────────────────────────── if "running" not in st.session_state: st.session_state["running"] = False if "result" not in st.session_state: st.session_state["result"] = None if "error" not in st.session_state: st.session_state["error"] = None if "topic_run" not in st.session_state: st.session_state["topic_run"] = "" # ── Global CSS ───────────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ── Header ────────────────────────────────────────────────────── st.markdown("""
ResearchOS
Multi-Agent AI Research Pipeline · LangGraph + Groq + Tavily
""", unsafe_allow_html=True) # ── Agent card renderer ───────────────────────────────────────── def render_agents(placeholder, active=None, done=None): done = done or [] agents = [ ("🔍", "Research", "Tavily web search · 3 queries"), ("📊", "Analyst", "Synthesizing findings"), ("🔎", "Critic", "Cross-referencing sources"), ("✍️", "Writer", "Generating report"), ] cards = "" for icon, name, desc in agents: key = name.lower() if key in done: cls, status, color = "done", ' Complete', "#4ADE80" elif active and key == active.lower(): cls, status, color = "active", ' Running', "#38BDF8" else: cls, status, color = "", ' Waiting', "#334155" cards += f"""
{icon}
{name}
{desc}
{status}
""" placeholder.markdown(f'
{cards}
', unsafe_allow_html=True) # ── Tabs ──────────────────────────────────────────────────────── tab1, tab2, tab3 = st.tabs(["⬡ Research", "◈ Pipeline", "◉ Report"]) # ══════════════════════════════════════════════════════ # TAB 1 — INPUT # ══════════════════════════════════════════════════════ with tab1: st.markdown('

Enter any research topic. Four specialized agents will autonomously search the web, synthesize findings, fact-check against sources, and produce a structured report with confidence scoring.

', unsafe_allow_html=True) topic = st.text_input("Research Topic", placeholder="e.g. Impact of AI on Pakistan's job market", key="topic_input") st.markdown("""
AI impact on Pakistan jobs Quantum computing 2025 EV market growth trends
""", unsafe_allow_html=True) run_btn = st.button("⬡ Launch Pipeline", key="run_btn") if run_btn and topic: st.session_state["running"] = True st.session_state["result"] = None st.session_state["error"] = None st.session_state["topic_run"] = topic st.toast("⬡ Pipeline launched — agents initializing...", icon="🔬") st.markdown("""
⬡ Pipeline launched — switch to Pipeline tab to track live progress
""", unsafe_allow_html=True) elif run_btn and not topic: st.warning("Enter a research topic to begin.") st.markdown("""
Agent Architecture
🔍
Research
3 targeted Tavily searches · real-time web data
📊
Analyst
Synthesizes findings · identifies patterns
🔎
Critic
Cross-references claims against sources
✍️
Writer
Structured report · cited sources
""", unsafe_allow_html=True) # ══════════════════════════════════════════════════════ # TAB 2 — PIPELINE # ══════════════════════════════════════════════════════ with tab2: if not st.session_state.get("running") and not st.session_state.get("result"): st.markdown("""
Pipeline idle — launch from Research tab
""", unsafe_allow_html=True) elif st.session_state.get("running"): topic_run = st.session_state.get("topic_run", "") st.markdown(f"""
PIPELINE RUNNING — processing: {topic_run}
""", unsafe_allow_html=True) agent_placeholder = st.empty() log_placeholder = st.empty() render_agents(agent_placeholder, active="research") def log(msg): log_placeholder.markdown(f"""
{msg}
""", unsafe_allow_html=True) try: log("Research Agent initializing Tavily searches...") graph = build_graph() initial_state = { "topic": topic_run, "search_results": [], "analysis": "", "critic_feedback": "", "confidence_scores": {}, "final_report": "", "current_step": "starting", "iteration_count": 0 } final_state = initial_state.copy() for event in graph.stream(initial_state): node_name = list(event.keys())[0] final_state.update(event[node_name]) if node_name == "researcher": render_agents(agent_placeholder, active="analyst", done=["research"]) log("Research complete — Analyst synthesizing findings...") elif node_name == "analyst": render_agents(agent_placeholder, active="critic", done=["research", "analyst"]) log("Analysis complete — Critic cross-referencing sources...") elif node_name == "critic": score = final_state.get("confidence_scores", {}).get("score", "?") render_agents(agent_placeholder, active="writer", done=["research", "analyst", "critic"]) log(f"Critic review done (confidence: {score}/100) — Writer generating report...") elif node_name == "iterate": itr = final_state.get("iteration_count", 0) log(f"Revision cycle {itr} — sending back to Analyst for refinement...") render_agents(agent_placeholder, active="analyst", done=["research"]) elif node_name == "writer": render_agents(agent_placeholder, done=["research", "analyst", "critic", "writer"]) log("Report complete.") st.session_state["result"] = final_state st.session_state["running"] = False confidence = final_state.get("confidence_scores", {}).get("score", 0) st.markdown(f"""
Confidence Score{confidence}/100
""", unsafe_allow_html=True) st.success("✅ Report ready — view in Report tab") except Exception as e: st.session_state["running"] = False st.session_state["error"] = str(e) render_agents(agent_placeholder) st.error(f"Pipeline failed: {e}") elif st.session_state.get("result"): result = st.session_state["result"] confidence = result.get("confidence_scores", {}).get("score", 0) agent_placeholder = st.empty() render_agents(agent_placeholder, done=["research", "analyst", "critic", "writer"]) st.markdown(f"""
Confidence Score{confidence}/100
""", unsafe_allow_html=True) st.success("✅ Pipeline complete — view report in Report tab") # ══════════════════════════════════════════════════════ # TAB 3 — REPORT # ══════════════════════════════════════════════════════ with tab3: if not st.session_state.get("result"): st.markdown("""
No report yet — run the pipeline first
""", unsafe_allow_html=True) else: result = st.session_state["result"] confidence = result.get("confidence_scores", {}).get("score", 0) iterations = result.get("iteration_count", 0) sources = len(result.get("search_results", [])) st.markdown(f"""
{confidence}/100
Confidence Score
{iterations}
Revision Cycles
{sources}
Sources Searched
""", unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) st.markdown(result.get("final_report", "No report generated.")) st.markdown('
', unsafe_allow_html=True) topic_run = st.session_state.get("topic_run", "report") st.markdown("
", unsafe_allow_html=True) st.download_button( label="↓ Download Report (.md)", data=result.get("final_report", ""), file_name=f"research_{topic_run[:30].replace(' ', '_')}.md", mime="text/markdown" ) st.markdown("
", unsafe_allow_html=True)