File size: 7,794 Bytes
4372b0a b6ee928 517de74 5e95a20 d26962d 517de74 5e95a20 94febc8 0f74db4 978c4cf 4372b0a a4f7e5c 4372b0a a4f7e5c 5e95a20 4372b0a 5e95a20 a4f7e5c 94febc8 42d374e 4372b0a b6ee928 4372b0a b6ee928 a4f7e5c b6ee928 4372b0a 3987ef0 a4f7e5c 4372b0a 978c4cf 4372b0a 5e95a20 4372b0a 0f74db4 5e95a20 42d374e 4372b0a 42d374e 4372b0a a4f7e5c 5e95a20 a4f7e5c 4372b0a 5e95a20 a4f7e5c 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a c1cd51c 4372b0a c1cd51c 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 0f74db4 94febc8 4372b0a c1cd51c 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a d26962d 4372b0a 5e95a20 a4f7e5c 5e95a20 4372b0a a4f7e5c 4372b0a d26962d 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a d26962d 5e95a20 4372b0a 5e95a20 4372b0a 5e95a20 4372b0a a4f7e5c 5e95a20 a4f7e5c 5e95a20 4372b0a 5e95a20 a4f7e5c 5e95a20 d26962d 978c4cf 4372b0a 5e95a20 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 180 181 182 183 184 185 186 187 188 189 |
#!/usr/bin/env python3
"""MedGenesis AI β CPU-only, dual-LLM (OpenAI / Gemini)"""
# ββββββββββββββ FIX: create a writable Streamlit data dir ββββββββββββββ
import os, pathlib
os.environ["STREAMLIT_DATA_DIR"] = "/tmp/.streamlit"
os.environ["XDG_STATE_HOME"] = "/tmp"
os.environ["STREAMLIT_BROWSER_GATHERUSAGESTATS"] = "false"
pathlib.Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
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"
# ββββββββββββββββ small helpers βββββββββββββββββ
def _pdf(papers):
pdf = FPDF(); pdf.add_page(); pdf.set_font("Arial", size=11)
pdf.cell(200, 8, "MedGenesis AI β Results", ln=True, align="C"); pdf.ln(3)
for i, p in enumerate(papers, 1):
pdf.set_font("Arial", "B", 11)
pdf.multi_cell(0, 7, 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(1)
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 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"])
# ββββββββββββββββ Streamlit UI ββββββββββββββββββ
def render_ui():
st.set_page_config("MedGenesis AI", layout="wide")
_sidebar_workspace()
# header
c1, c2 = st.columns([0.15, 0.85])
with c1:
if LOGO.exists():
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",
placeholder="e.g. CRISPR glioblastoma therapy")
# alert check
if get_workspace():
try:
news = asyncio.run(check_alerts([w["query"] for w in get_workspace()]))
if news:
st.sidebar.subheader("π New papers")
for q, lst in news.items():
st.sidebar.write(f"**{q}** β {len(lst)} new")
except Exception as e:
st.sidebar.warning(f"Alert check failed: {e}")
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()}**")
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"])
col1, col2 = st.columns(2)
with col1:
st.download_button("CSV",
pd.DataFrame(res["papers"]).to_csv(index=False),
"papers.csv", "text/csv")
with col2:
st.download_button("PDF", _pdf(res["papers"]),
"papers.pdf", "application/pdf")
if st.button("πΎ Save"):
save_query(query, res)
st.success("Saved to workspace")
st.subheader("UMLS concepts")
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 -------------------------------------------------------
with tabs[1]:
st.header("Gene / Variant signals")
for g in res["genes"]:
st.write(f"- **{g.get('name', g.get('geneid'))}** "
f"{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"]:
if d:
st.write("-", d)
# Trials ------------------------------------------------------
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'][0]}** β {t['BriefTitle'][0]}")
st.write(f"Phase {t.get('Phase', [''])[0]} | "
f"Status {t['OverallStatus'][0]}")
# Graph -------------------------------------------------------
with tabs[3]:
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:
n.color = "#f1c40f" if pat.search(n.label) else "#d3d3d3"
agraph(nodes, edges, cfg)
# Metrics -----------------------------------------------------
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}")
st.markdown("**Top hubs**")
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"))
st.markdown("---")
follow = st.text_input("Ask follow-up:")
if st.button("Ask AI"):
ans = asyncio.run(answer_ai_question(follow,
context=query,
llm=llm))
st.write(ans["answer"])
else:
st.info("Enter a question and press **Run Search π**")
# entry-point
if __name__ == "__main__":
render_ui()
|