"""ResearchPath Streamlit app. Two tabs: 1. Reading Path β prerequisite-chain planner (the unique agentic feature) 2. Ask β grounded Q&A over the indexed RL corpus """ from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) import streamlit as st from researchpath.corpus import CANONICAL_RL_PAPERS from researchpath.planning import plan_reading_path ROOT = Path(__file__).resolve().parent INDEX_PATH = ROOT / "data" / "index.faiss" _TAG_TO_ID = {p.tag.lower(): p.arxiv_id for p in CANONICAL_RL_PAPERS} _ID_TO_PAPER = {p.arxiv_id: p for p in CANONICAL_RL_PAPERS} _ERA_ORDER = ["value-based", "policy-gradient", "actor-critic", "model-based", "rlhf"] st.set_page_config( page_title="ResearchPath", page_icon="πΊοΈ", layout="wide", initial_sidebar_state="collapsed", ) # ββ minimal style ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ st.markdown( """ """, unsafe_allow_html=True, ) # ββ header βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ st.title("πΊοΈ ResearchPath") st.caption( "An agentic research-onboarding companion for Reinforcement Learning. " "Give it a target paper and your background; it builds a personalized, " "dependency-ordered reading plan." ) tab_plan, tab_ask = st.tabs(["π Reading Path", "π¬ Ask a Question"]) # ββ helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ def _resolve(token: str) -> str | None: token = token.strip() if token in _ID_TO_PAPER: return token if token.lower() in _TAG_TO_ID: return _TAG_TO_ID[token.lower()] return None def _paper_label(arxiv_id: str) -> str: p = _ID_TO_PAPER[arxiv_id] return f"{p.tag} ({p.year})" @st.cache_resource(show_spinner="Loading index and embedderβ¦") def _load_retrieval(): """Load FAISS index + embedder once per session.""" from researchpath.embeddings import Embedder from researchpath.index import load_index from researchpath.retrieval import HybridRetriever if not INDEX_PATH.exists(): return None, None, None index, chunks = load_index(INDEX_PATH) embedder = Embedder() retriever = HybridRetriever(index, chunks, embedder) return index, chunks, retriever # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ # TAB 1 β Reading Path # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ with tab_plan: st.subheader("Build your personalized reading path") st.markdown( "Select a **target paper** you want to understand, then tell us what you **already know**. " "ResearchPath traces the prerequisite chain and gives you a topologically-sorted reading list " "with *why each paper is next*." ) # Group papers by era for a nicer selectbox era_groups: dict[str, list] = {era: [] for era in _ERA_ORDER} for p in CANONICAL_RL_PAPERS: era_groups[p.era].append(p) paper_options = [] for era in _ERA_ORDER: for p in era_groups[era]: paper_options.append(p.arxiv_id) def _format_option(arxiv_id: str) -> str: p = _ID_TO_PAPER[arxiv_id] return f"{p.tag} β {p.title[:60]}{'β¦' if len(p.title) > 60 else ''} ({p.year})" col_target, col_known = st.columns([1, 1]) with col_target: target_id = st.selectbox( "Target paper", options=paper_options, format_func=_format_option, index=paper_options.index("1710.02298"), # default: Rainbow ) with col_known: known_options = [aid for aid in paper_options if aid != target_id] known_ids_raw = st.multiselect( "Papers you already know (optional)", options=known_options, format_func=_format_option, default=[], ) if st.button("Generate reading path", type="primary", use_container_width=True): plan = plan_reading_path(target_id, known_ids=set(known_ids_raw)) target_paper = _ID_TO_PAPER[target_id] if not plan.steps: st.success(f"You're already ready to read **{target_paper.tag}** directly!") else: st.markdown( f"**{len(plan.steps)} paper(s)** to read before (and including) " f"**{target_paper.tag}**, in order:" ) for i, step in enumerate(plan.steps, 1): p = step.paper is_target = p.arxiv_id == target_id border_color = "#f6c90e" if is_target else "#4f8ef7" label = "π― TARGET" if is_target else f"Step {i}" concepts_html = "".join( f'{c}' for c in step.concepts ) bridges_text = ( "β " + ", ".join(_paper_label(b) for b in step.bridges_to) if step.bridges_to else "" ) st.markdown( f"""