import gradio as gr import uuid import os import tempfile import logging import preload from dotenv import load_dotenv from src.graph import run_recon from src.memory import init_db, load_session load_dotenv() logging.basicConfig(level=logging.WARNING) logger = logging.getLogger(__name__) init_db() # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- VERDICT_META = { "PASS": ("โ ", "#22c55e", "Pass"), "FORCED_PASS": ("โ ๏ธ", "#f59e0b", "Forced Pass"), "STALE": ("๐ฐ๏ธ", "#f59e0b", "Stale"), "CONTRADICTED":("โก", "#ef4444", "Contradicted"), "INSUFFICIENT":("๐", "#ef4444", "Insufficient"), } CONF_META = { "high": ("๐ข", "#22c55e"), "medium": ("๐ก", "#f59e0b"), "low": ("๐ด", "#ef4444"), } def _highlight_citations(text: str) -> str: """Wrap [Author et al., Year] citations in styled spans.""" import re return re.sub( r"(\[[A-Za-z][^,\[\]]{1,40},?\s*(?:et al\.?)?,?\s*\d{4}[a-z]?\])", r'\1', text ) SIGNAL_COLORS = { "FOUNDATIONAL": "#22c55e", "CURRENT": "#3b82f6", "DECLINING": "#f59e0b", "SUPERSEDED": "#ef4444", } def _paper_cards_html(papers, reliability_scores: dict = {}) -> str: """Render retrieved papers as styled cards.""" if not papers: return "
No papers retrieved.
" cards = [] for p in papers[:8]: score_color = "#22c55e" if p.hybrid_score >= 0.6 else "#f59e0b" if p.hybrid_score >= 0.4 else "#ef4444" authors = ", ".join(p.authors[:2]) + (" et al." if len(p.authors) > 2 else "") if p.authors else "Unknown" abstract_preview = (p.abstract[:180] + "...") if p.abstract and len(p.abstract) > 180 else (p.abstract or "") rs = reliability_scores.get(p.paper_id) if rs: dominant = rs.get("dominant_signal", "DECLINING") if isinstance(rs, dict) else rs.dominant_signal sig_color = SIGNAL_COLORS.get(dominant, "#6b7280") signal_badge = ( f'' f'{dominant}' ) else: signal_badge = "" cards.append(f"""No claims extracted.
" rows = "" for c in claims: emoji, color = CONF_META.get(c.confidence, ("โช", "#6b7280")) flag = " โ ๏ธ" if c.flagged else "" rows += f"""| Confidence | Claim | Source | Year |
|---|
Session {session_id[:8]}... โ no turns yet.
{session_id[:8]}...
{turns} turn{"s" if turns != 1 else ""}
โ {e}
", "", "", "", "", None return position = result.get("synthesized_position", "No position generated.") highlighted = _highlight_citations(position) history = history + [{"role": "assistant", "content": highlighted}] verdict = result.get("critic_verdict", "N/A") critic_notes = result.get("critic_notes", "") retry_count = result.get("retry_count", 0) latency = result.get("latency_ms", 0) papers_used = len(result.get("retrieved_papers") or []) rewritten = result.get("rewritten_questions") or [] verdict_html = _verdict_badge_html(verdict, critic_notes, retry_count, papers_used, latency, decay_config, rewritten) claims_html = _claims_html(result.get("claim_confidences") or []) papers_html = _paper_cards_html(result.get("retrieved_papers") or [], result.get("paper_reliability_scores") or {}) session_ctx = load_session(session_id) session_html = _session_html(session_ctx, session_id) export_md = result.get("export_md", "") yield history, session_id, verdict_html, claims_html, papers_html, session_html, export_md, None def export_md_file(export_md_content, session_id): if not export_md_content.strip(): return None try: path = os.path.join(tempfile.gettempdir(), f"recon_{session_id[:8]}.md") with open(path, "w", encoding="utf-8") as f: f.write(export_md_content) return path except Exception as e: logger.error(f"Export failed: {e}") return None def new_session(): new_id = str(uuid.uuid4()) return new_id, [], "", "", "", "", "", None # --------------------------------------------------------------------------- # UI # --------------------------------------------------------------------------- CSS = """ .gradio-container { font-family: 'Inter', system-ui, sans-serif !important; } .chatbot-wrap .message-wrap { font-size: 0.92em; line-height: 1.7; } footer { display: none !important; } """ with gr.Blocks(title="RECON") as demo: gr.HTML("""Temporally-aware ML literature research ยท Live Semantic Scholar ยท Staleness detection ยท Contradiction flagging
Run a query to see retrieved papers.
" ) with gr.Accordion("๐ Claim Confidence Table", open=True): claims_output = gr.HTML( value="Run a query to see claim confidence scores.
" ) # โโ Right column โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ with gr.Column(scale=2): gr.HTML("Critic verdict will appear here.
" ) gr.HTML("Session history will appear here.
" ) gr.HTML("