mgbam commited on
Commit
b6ee928
Β·
verified Β·
1 Parent(s): 3f06a92

Update app.py

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