File size: 6,694 Bytes
994feb6 590f907 517de74 994feb6 5e95a20 a7d3db7 d26962d 517de74 590f907 36986a7 94febc8 994feb6 590f907 2b2ae99 994feb6 2b2ae99 978c4cf 4372b0a a4f7e5c 994feb6 7e2c73b 994feb6 7e2c73b 994feb6 7e2c73b 994feb6 7e2c73b 5e95a20 7e2c73b 994feb6 fbb4b8d 994feb6 7e2c73b 994feb6 b6ee928 4372b0a b6ee928 a4f7e5c b6ee928 994feb6 36986a7 a4f7e5c 994feb6 fbb4b8d 994feb6 4372b0a 994feb6 42d374e 590f907 994feb6 4372b0a 5e95a20 4372b0a 994feb6 36986a7 994feb6 590f907 d26962d 590f907 994feb6 590f907 994feb6 590f907 994feb6 36986a7 994feb6 590f907 994feb6 590f907 994feb6 590f907 36986a7 994feb6 36986a7 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 590f907 994feb6 5e95a20 994feb6 |
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 |
# ββββββββββββββββββββββββββββ app.py βββββββββββββββββββββββββββββββββ
"""Streamlit UI β MedGenesis v2 with gene + variant + trial integration."""
import os, pathlib, 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_utils import build_nx, get_top_hubs, get_density
from mcp.alerts import check_alerts
# ---- Streamlit telemetry patch -------------------------------------
os.environ.update({
"STREAMLIT_DATA_DIR": "/tmp/.streamlit",
"XDG_STATE_HOME": "/tmp",
"STREAMLIT_BROWSER_GATHERUSAGESTATS": "false",
})
pathlib.Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True)
ROOT = Path(__file__).parent
LOGO = ROOT / "assets" / "logo.png"
# ---------------- helpers -------------------------------------------
def _latin1_safe(t: str) -> str:
return t.encode("latin-1", "replace").decode("latin-1")
def _export_pdf(papers):
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
pdf.set_font("Helvetica", size=11)
pdf.cell(200, 8, _latin1_safe("MedGenesis AI β Results"), ln=True, align="C")
pdf.ln(3)
for i, p in enumerate(papers, 1):
pdf.set_font("Helvetica", "B", 11)
pdf.multi_cell(0, 7, _latin1_safe(f"{i}. {p['title']}"))
pdf.set_font("Helvetica", size=9)
body = f"{p['authors']}\n{p['summary']}\n{p['link']}\n"
pdf.multi_cell(0, 6, _latin1_safe(body))
pdf.ln(1)
return pdf.output(dest="S").encode("latin-1", "replace")
# ---------------- sidebar -------------------------------------------
def _workspace_sidebar():
with st.sidebar:
st.header("ποΈ Workspace")
ws = get_workspace()
if not ws:
st.info("Run a search then press **Save** to populate this list.")
return
for i, item in enumerate(ws, 1):
with st.expander(f"{i}. {item['query']}"):
st.write(item["result"]["ai_summary"])
# ---------------- main ----------------------------------------------
def render_ui():
st.set_page_config("MedGenesis AI", layout="wide")
_workspace_sidebar()
# header ---------------------------------------------------------
c1, c2 = st.columns([0.15, 0.85])
if LOGO.exists():
with c1: st.image(str(LOGO), width=105)
with c2:
st.markdown("## 𧬠**MedGenesis AI**")
st.caption("Multiβsource biomedical assistant Β· OpenAI / Gemini")
llm = st.radio("LLM engine", ["openai", "gemini"], horizontal=True)
query = st.text_input("Enter biomedical question", "CRISPR glioblastoma therapy")
if st.button("Run Search π") and query:
with st.spinner("Collecting literature & biomedical data β¦"):
res = asyncio.run(orchestrate_search(query, llm=llm))
st.success(f"Completed with **{res['llm_used'].title()}**")
st.session_state.result = res
st.session_state.last_query = query
st.session_state.last_llm = llm
res = st.session_state.get("result")
if not res:
st.info("Enter a question and press **Run Search π**")
return
tabs = st.tabs(["Results", "Genes", "Trials", "Graph", "Metrics", "Visuals"])
# results --------------------------------------------------------
with tabs[0]:
for i, p in enumerate(res["papers"], 1):
st.markdown(f"**{i}. [{p['title']}]({p['link']})** *{p['authors']}*")
st.write(p["summary"])
c1, c2 = st.columns(2)
with c1:
st.download_button("CSV", pd.DataFrame(res["papers"]).to_csv(index=False), "papers.csv")
with c2:
st.download_button("PDF", _export_pdf(res["papers"]), "papers.pdf", mime="application/pdf")
if st.button("πΎ Save"):
save_query(query, res)
st.success("Saved to workspace")
st.subheader("AI summary")
st.info(res["ai_summary"])
# gene tab -------------------------------------------------------
with tabs[1]:
if not res["genes"]:
st.info("No gene hits (rateβlimited or none found).")
for g in res["genes"]:
st.json(g)
if res["variants"]:
st.markdown("### Tumour variants (cBioPortal)")
for k, v in res["variants"].items():
st.write(f"**{k}** β {len(v)} variants")
# trials tab -----------------------------------------------------
with tabs[2]:
st.header("Clinical trials")
if not res["clinical_trials"]:
st.info("No trials (rateβlimited or none found).")
for t in res["clinical_trials"]:
st.markdown(f"**{t['nctId']}** β {t['briefTitle']}")
st.write(f"Phase {t.get('phase')} | Status {t.get('status')}")
# graph tab ------------------------------------------------------
with tabs[3]:
nodes, edges, cfg = build_agraph(res["papers"], res["umls"], res["drug_safety"])
hl = st.text_input("Highlight node:")
if hl:
pat = re.compile(re.escape(hl), re.I)
for n in nodes:
n.color = "#f1c40f" if pat.search(n.label) else "#d3d3d3"
agraph(nodes, edges, cfg)
# metrics tab ----------------------------------------------------
with tabs[4]:
G = build_nx([n.__dict__ for n in nodes], [e.__dict__ for e in edges])
st.metric("Density", f"{get_density(G):.3f}")
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.get("published", "")[:4] for p in res["papers"] if p.get("published")]
if years:
fig = px.histogram(years, nbins=12, title="Publication Year")
st.plotly_chart(fig)
# followβup QA ---------------------------------------------------
st.markdown("---")
q = st.text_input("Ask followβup question:")
if st.button("Ask AI"):
with st.spinner("Querying LLM β¦"):
ans = asyncio.run(answer_ai_question(q, context=st.session_state.last_query, llm=st.session_state.last_llm))
st.write(ans["answer"])
if __name__ == "__main__":
render_ui() |