File size: 7,302 Bytes
b6ee928 978c4cf b6ee928 517de74 d26962d 9efa522 31dadea d26962d 517de74 b6ee928 94febc8 0f74db4 978c4cf 94febc8 42d374e b6ee928 d26962d b6ee928 94febc8 b6ee928 0f74db4 b6ee928 9efa522 94febc8 42d374e b6ee928 3987ef0 517de74 978c4cf b6ee928 0f74db4 b6ee928 0f74db4 b6ee928 0f74db4 b6ee928 0f74db4 b6ee928 d26962d 0f74db4 42d374e d26962d 42d374e 0f74db4 b6ee928 42d374e b6ee928 42d374e b6ee928 d26962d b6ee928 0f74db4 b6ee928 42d374e d26962d 0f74db4 d26962d c1cd51c b6ee928 c1cd51c b6ee928 0f74db4 b6ee928 0f74db4 b6ee928 d26962d b6ee928 0f74db4 d26962d b6ee928 0f74db4 94febc8 b6ee928 c1cd51c b6ee928 0f74db4 d26962d 0f74db4 b6ee928 0f74db4 b6ee928 0f74db4 b6ee928 d26962d b6ee928 0f74db4 b6ee928 0f74db4 d26962d b6ee928 d26962d b6ee928 d26962d 0f74db4 b6ee928 0f74db4 b6ee928 d26962d 0f74db4 b6ee928 0f74db4 b6ee928 978c4cf b6ee928 d26962d b6ee928 d26962d b6ee928 d26962d 978c4cf b6ee928 3987ef0 40a1d7a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# app.py — MedGenesis AI (CPU edition)
import asyncio, re
from pathlib import Path
import streamlit as st
import pandas as pd
import plotly.express as px
from fpdf import FPDF
from streamlit_agraph import agraph
from mcp.orchestrator import orchestrate_search, answer_ai_question
from mcp.workspace import get_workspace, save_query
from mcp.knowledge_graph import build_agraph
from mcp.graph_metrics import build_nx, get_top_hubs, get_density
from mcp.alerts import check_alerts
ROOT = Path(__file__).parent
LOGO = ROOT / "assets" / "logo.png"
# ---------------------------------------------------------------------
def pdf_from_papers(papers):
pdf = FPDF(); pdf.add_page(); pdf.set_font("Arial", size=12)
pdf.cell(200, 10, "MedGenesis AI — Results", ln=True, align="C"); pdf.ln(8)
for i, p in enumerate(papers, 1):
pdf.set_font("Arial", "B", 12)
pdf.multi_cell(0, 8, f"{i}. {p['title']}")
pdf.set_font("Arial", "", 9)
pdf.multi_cell(0, 6, f"{p['authors']}\n{p['summary']}\n{p['link']}\n")
pdf.ln(2)
return pdf.output(dest="S").encode("latin-1")
# ---------------------------------------------------------------------
def sidebar_workspace():
with st.sidebar:
st.header("🗂️ Workspace")
ws = get_workspace()
if not ws:
st.info("Run a search and click **Save** to build your workspace.")
return
for i, item in enumerate(ws, 1):
with st.expander(f"{i}. {item['query']}"):
st.write("**AI Summary**:", item["result"]["ai_summary"])
df = pd.DataFrame(item["result"]["papers"])
st.download_button("📥 CSV", df.to_csv(index=False),
f"workspace_{i}.csv", "text/csv")
# ---------------------------------------------------------------------
def render_ui():
st.set_page_config(page_title="MedGenesis AI", layout="wide")
# 🔔 quick alert check
saved_q = [q["query"] for q in get_workspace()]
if saved_q:
try:
alerts = asyncio.run(check_alerts(saved_q))
if alerts:
with st.sidebar:
st.subheader("🔔 New Papers")
for q, links in alerts.items():
st.write(f"**{q}** – {len(links)} new")
except Exception as e:
st.sidebar.warning(f"Alert check failed: {e}")
sidebar_workspace()
# Header
col1, col2 = st.columns([0.15, 0.85])
with col1:
if LOGO.exists(): st.image(str(LOGO), width=100)
with col2:
st.markdown("## 🧬 **MedGenesis AI**")
st.caption("PubMed·ArXiv·OpenFDA·UMLS·NCBI·DisGeNET·ClinicalTrials·GPT-4o")
st.markdown("---")
query = st.text_input("🔍 Ask your biomedical question:",
placeholder="e.g. CRISPR for glioblastoma")
# -----------------------------------------------------------------
if st.button("Run Search 🚀") and query:
with st.spinner("Synthesizing multi-source biomedical intel…"):
res = asyncio.run(orchestrate_search(query))
st.success("Ready!")
tabs = st.tabs([
"Results", "Genes", "Trials", "Graph", "Metrics", "Visuals"
])
# ----------- RESULTS -----------------
with tabs[0]:
st.header("📚 Literature")
for i, p in enumerate(res["papers"], 1):
st.markdown(f"**{i}. [{p['title']}]({p['link']})** *{p['authors']}*")
st.markdown(f"<span style='color:gray'>{p['summary']}</span>",
unsafe_allow_html=True)
colA, colB = st.columns(2)
with colA:
if st.button("💾 Save to Workspace"):
save_query(query, res); st.success("Saved!")
with colB:
st.download_button("📥 CSV", pd.DataFrame(res["papers"]).to_csv(index=False),
"papers.csv", "text/csv")
st.download_button("📄 PDF", pdf_from_papers(res["papers"]),
"papers.pdf", "application/pdf")
st.subheader("🧠 UMLS")
for c in res["umls"]:
if c.get("cui"): st.write(f"- **{c['name']}** ({c['cui']})")
st.subheader("💊 OpenFDA Safety")
for d in res["drug_safety"]: st.json(d)
st.subheader("🤖 AI Summary")
st.info(res["ai_summary"])
# ----------- GENES & VARIANTS --------
with tabs[1]:
st.header("🧬 Gene Signals")
for g in res["genes"]:
st.write(f"- **{g.get('name', g.get('geneid'))}** – {g.get('description','')}")
if res["gene_disease"]:
st.markdown("### DisGeNET Links"); st.json(res["gene_disease"][:15])
if res["mesh_defs"]:
st.markdown("### MeSH Definitions")
for d in res["mesh_defs"]: st.write("-", d)
# ----------- TRIALS ------------------
with tabs[2]:
st.header("💊 Clinical Trials")
if not res["clinical_trials"]:
st.info("No trials retrieved (rate-limited or none found).")
for t in res["clinical_trials"]:
st.markdown(f"**{t['NCTId'][0]}** – {t['BriefTitle'][0]}")
st.write(f"Phase: {t.get('Phase',[''])[0]} | Status: {t['OverallStatus'][0]}")
# ----------- GRAPH -------------------
with tabs[3]:
st.header("🗺️ Knowledge Graph")
nodes, edges, cfg = build_agraph(res["papers"],
res["umls"],
res["drug_safety"])
hl = st.text_input("Highlight node:", key="hl")
if hl:
pat = re.compile(re.escape(hl), re.I)
for n in nodes:
if pat.search(n.label): n.color, n.size = "#f1c40f", 30
else: n.color = "#d3d3d3"
agraph(nodes=nodes, edges=edges, config=cfg)
# ----------- METRICS -----------------
with tabs[4]:
st.header("📈 Graph Metrics")
G = build_nx([n.__dict__ for n in nodes], [e.__dict__ for e in edges])
st.metric("Density", f"{get_density(G):.3f}")
st.markdown("#### Hub Nodes")
for nid, sc in get_top_hubs(G):
lab = next((n.label for n in nodes if n.id == nid), nid)
st.write(f"- **{lab}** – {sc:.3f}")
# ----------- VISUALS -----------------
with tabs[5]:
years = [p["published"] for p in res["papers"] if p.get("published")]
if years: st.plotly_chart(px.histogram(years, nbins=12, title="Publication Year"))
# -------- Follow-up AI ---------------
st.markdown("---")
follow = st.text_input("Ask follow-up question:")
if st.button("Ask AI"):
st.write(asyncio.run(answer_ai_question(follow, context=query))["answer"])
else:
st.info("Enter a question and press **Run Search 🚀**")
# ---------------------------------------------------------------------
if __name__ == "__main__":
render_ui()
|