# 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("""
""", 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)