mgbam commited on
Commit
0f74db4
Β·
verified Β·
1 Parent(s): d26962d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +102 -86
app.py CHANGED
@@ -1,148 +1,164 @@
1
- # app.py
2
 
3
- import asyncio, os, re
4
  from pathlib import Path
5
-
6
  import streamlit as st
7
  import pandas as pd
8
  import plotly.express as px
9
  from fpdf import FPDF
10
  from streamlit_agraph import agraph
11
 
12
- from mcp.orchestrator import orchestrate_search, answer_ai_question
13
- from mcp.workspace import get_workspace, save_query
14
  from mcp.knowledge_graph import build_agraph
 
 
15
 
16
  ROOT = Path(__file__).parent
17
  LOGO = ROOT / "assets" / "logo.png"
18
 
19
- # ------------------------- helpers -------------------------
20
- def generate_pdf(papers):
21
  pdf = FPDF(); pdf.add_page(); pdf.set_font("Arial", size=12)
22
- pdf.cell(200, 10, "MedGenesis AI - Search Results", ln=True, align="C")
23
- pdf.ln(10)
24
  for i, p in enumerate(papers, 1):
25
- pdf.set_font("Arial", "B", 12)
26
- pdf.multi_cell(0, 10, f"{i}. {p['title']}")
27
- pdf.set_font("Arial", "", 10)
28
- pdf.multi_cell(0, 8,
29
- f"Authors: {p['authors']}\nLink: {p['link']}\nSummary: {p['summary']}\n")
30
  pdf.ln(2)
31
  return pdf.output(dest="S").encode("latin-1")
32
 
33
- # ------------------------- UI -------------------------
34
  def render_ui():
35
  st.set_page_config(page_title="MedGenesis AI", layout="wide")
36
 
37
- # ----- Sidebar ----------
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  with st.sidebar:
39
  st.header("πŸ—‚οΈ Workspace")
40
- for i, it in enumerate(get_workspace(), 1):
41
- with st.expander(f"{i}. {it['query']}"):
42
- st.write("**AI Summary:**", it["result"]["ai_summary"])
43
- df = pd.DataFrame(it["result"]["papers"])
44
- st.download_button("CSV", df.to_csv(index=False), f"ws_{i}.csv", "text/csv")
 
 
45
  if not get_workspace():
46
- st.info("Run and save searches to populate workspace.")
47
 
48
- # ----- Header ----------
49
  col1, col2 = st.columns([0.15, 0.85])
50
  with col1:
51
  if LOGO.exists(): st.image(str(LOGO), width=100)
52
  with col2:
53
- st.markdown("## 🧬 MedGenesis AI")
54
- st.write("*Unified PubMed, ArXiv, OpenFDA, UMLS, NCBI, DisGeNET, ClinicalTrials & GPT-4o*")
55
 
56
  st.markdown("---")
57
- query = st.text_input("πŸ” Biomedical research question:", placeholder="e.g. CRISPR glioblastoma")
 
58
 
59
- # ------------- Search -------------
60
  if st.button("Run Search πŸš€") and query:
61
- with st.spinner("Collecting literature & biomedical data…"):
62
- results = asyncio.run(orchestrate_search(query))
63
- st.success("Completed!")
64
 
65
- # -------- Tabs ---------
66
  tabs = st.tabs([
67
- "πŸ“ Results", "🧬 Genes & Variants", "πŸ’Š Clinical Trials",
68
- "πŸ—ΊοΈ Graph", "πŸ“Š Visuals"
69
  ])
70
 
71
- # -- Results ----------
72
  with tabs[0]:
73
  st.header("πŸ“š Top Papers")
74
- for i, p in enumerate(results["papers"], 1):
75
- st.markdown(f"**{i}. [{p['title']}]({p['link']})** \n*{p['authors']}* ({p['source']})")
76
  st.markdown(f"<span style='color:gray'>{p['summary']}</span>", unsafe_allow_html=True)
77
-
78
- if st.button("Save to Workspace"):
79
- save_query(query, results); st.success("Saved!")
80
-
81
- df = pd.DataFrame(results["papers"])
82
- st.download_button("CSV", df.to_csv(index=False), "results.csv", "text/csv")
83
- st.download_button("PDF", generate_pdf(results["papers"]), "results.pdf", "application/pdf")
84
-
85
- st.subheader("🧠 UMLS Concepts")
86
- for c in results["umls"]:
87
  if c.get("cui"):
88
- st.markdown(f"- **{c['name']}** (`{c['cui']}`)")
89
 
90
  st.subheader("πŸ’Š Drug Safety (OpenFDA)")
91
- for d in results["drug_safety"]:
92
- st.json(d)
93
 
94
- st.subheader("πŸ€– AI Summary")
95
- st.info(results["ai_summary"])
96
 
97
- # -- Genes & Variants ----------
98
  with tabs[1]:
99
- st.header("🧬 Gene Associations (NCBI / DisGeNET)")
100
- for g in results["genes"]:
101
  st.write(f"- **{g.get('name', g.get('geneid'))}** – {g.get('description','')}")
102
- if results["gene_disease"]:
103
- st.markdown("#### DisGeNET Disease β†’ Gene links")
104
- st.json(results["gene_disease"][:15])
105
- if results["mesh_definitions"]:
106
- st.markdown("#### MeSH Definitions")
107
- for d in results["mesh_definitions"]:
108
- if d: st.write(f"- {d}")
109
-
110
- # -- Clinical Trials ----------
111
  with tabs[2]:
112
  st.header("πŸ’Š Registered Clinical Trials")
113
- for t in results["clinical_trials"]:
 
 
114
  st.markdown(f"**{t['NCTId'][0]}** – {t['BriefTitle'][0]}")
115
- st.write(f"Condition: {', '.join(t['Condition'])} | Phase: {t.get('Phase',[None])[0]} | Status: {t['OverallStatus'][0]}")
116
 
117
- # -- Graph ----------
118
  with tabs[3]:
119
- search_term = st.text_input("Highlight node containing:", key="graphsearch")
120
- try:
121
- nodes, edges, cfg = build_agraph(results["papers"], results["umls"], results["drug_safety"])
122
- if search_term:
123
- pat = re.compile(re.escape(search_term), re.I)
124
- for n in nodes:
125
- if pat.search(n.label): n.color, n.size = "#f1c40f", max(n.size, 30)
126
- else: n.color = "#ddd"
127
- agraph(nodes=nodes, edges=edges, config=cfg)
128
- except Exception as e:
129
- st.error(f"Graph error: {e}")
130
-
131
- # -- Visualizations ----------
132
  with tabs[4]:
133
- yrs = [p["published"] for p in results["papers"] if p.get("published")]
 
 
 
 
 
 
 
 
 
 
 
134
  if yrs: st.plotly_chart(px.histogram(yrs, nbins=10, title="Publication Year"))
135
 
136
- # -- Follow-up Q&A ----------
137
  st.markdown("---")
138
- q = st.text_input("Ask follow-up:", key="follow")
139
  if st.button("Ask AI"):
140
- ans = asyncio.run(answer_ai_question(q, context=query))
141
- st.write(ans["answer"])
142
-
143
  else:
144
  st.info("Enter a question and press **Run Search πŸš€**")
145
 
146
- # ------------- Run -------------
147
  if __name__ == "__main__":
148
  render_ui()
 
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
7
  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
16
 
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])
63
  with col1:
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")
145
+ for nid, sc in get_top_hubs(G):
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()