mgbam commited on
Commit
1786f57
·
verified ·
1 Parent(s): 1e92bb0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -257
app.py CHANGED
@@ -1,262 +1,41 @@
1
- #!/usr/bin/env python3
2
- """
3
- MedGenesis AI Streamlit front-end (v3)
4
- --------------------------------------
5
- Supports **OpenAI** and **Gemini** engines and the enriched backend
6
- payload introduced in orchestrator v3:
7
- • papers, umls, drug_safety, genes, mesh_defs, gene_disease,
8
- clinical_trials, variants, ai_summary
9
- Tabs:
10
- Results | Genes | Trials | Variants | Graph | Metrics | Visuals
11
- """
12
-
13
- import os
14
- import pathlib
15
- import asyncio
16
- from pathlib import Path
17
-
18
- import streamlit as st
19
- import pandas as pd
20
- import plotly.express as px
21
- from fpdf import FPDF
22
- from streamlit_agraph import agraph
23
-
24
- from mcp.orchestrator import orchestrate_search, answer_ai_question
25
- from mcp.workspace import get_workspace, save_query
26
- from mcp.knowledge_graph import build_agraph
27
- from mcp.graph_metrics import build_nx, get_top_hubs, get_density
28
- from mcp.alerts import check_alerts
29
-
30
- # Streamlit telemetry directory → /tmp
31
- os.environ.update({
32
- "STREAMLIT_DATA_DIR": "/tmp/.streamlit",
33
- "XDG_STATE_HOME": "/tmp",
34
- "STREAMLIT_BROWSER_GATHERUSAGESTATS": "false",
35
- })
36
- pathlib.Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True)
37
-
38
- ROOT = Path(__file__).parent
39
- LOGO = ROOT / "assets" / "logo.png"
40
-
41
-
42
- def _latin1_safe(txt: str) -> str:
43
- """Coerce UTF-8 → Latin-1 with replacement (for FPDF)."""
44
- return txt.encode("latin-1", "replace").decode("latin-1")
45
-
46
-
47
- def _pdf(papers: list[dict]) -> bytes:
48
- pdf = FPDF()
49
- pdf.set_auto_page_break(auto=True, margin=15)
50
- pdf.add_page()
51
- pdf.set_font("Helvetica", size=11)
52
- pdf.cell(200, 8, _latin1_safe("MedGenesis AI – Results"), ln=True, align="C")
53
- pdf.ln(3)
54
- for i, p in enumerate(papers, 1):
55
- pdf.set_font("Helvetica", "B", 11)
56
- pdf.multi_cell(0, 7, _latin1_safe(f"{i}. {p.get('title','')}"))
57
- pdf.set_font("Helvetica", "", 9)
58
- body = (
59
- f"{p.get('authors','')}\n"
60
- f"{p.get('summary','')}\n"
61
- f"{p.get('link','')}\n"
62
- )
63
- pdf.multi_cell(0, 6, _latin1_safe(body))
64
- pdf.ln(1)
65
- return pdf.output(dest="S").encode("latin-1", "replace")
66
-
67
-
68
- def _workspace_sidebar():
69
- with st.sidebar:
70
- st.header("🗂️ Workspace")
71
- ws = get_workspace()
72
- if not ws:
73
- st.info("Run a search then press **Save** to populate this list.")
74
- return
75
- for i, item in enumerate(ws, 1):
76
- with st.expander(f"{i}. {item['query']}"):
77
- st.write(item['result']['ai_summary'])
78
-
79
-
80
- def render_ui():
81
- st.set_page_config("MedGenesis AI", layout="wide")
82
-
83
- # Session-state defaults
84
- defaults = dict(
85
- query_result=None,
86
- followup_input="",
87
- followup_response=None,
88
- last_query="",
89
- last_llm="openai",
90
- )
91
- for k, v in defaults.items():
92
- st.session_state.setdefault(k, v)
93
-
94
- _workspace_sidebar()
95
-
96
- # Header
97
- col1, col2 = st.columns([0.15, 0.85])
98
- with col1:
99
- if LOGO.exists():
100
- st.image(str(LOGO), width=105)
101
- with col2:
102
- st.markdown("## 🧬 **MedGenesis AI**")
103
- st.caption("Multi-source biomedical assistant · OpenAI / Gemini")
104
-
105
- # Controls
106
- engine = st.radio("LLM engine", ["openai", "gemini"], horizontal=True)
107
- query = st.text_input("Enter biomedical question", placeholder="e.g. CRISPR glioblastoma therapy")
108
-
109
- # Alerts
110
- if get_workspace():
111
- try:
112
- alerts = asyncio.run(check_alerts([w["query"] for w in get_workspace()]))
113
- if alerts:
114
- with st.sidebar:
115
- st.subheader("🔔 New papers")
116
- for q, lnks in alerts.items():
117
- st.write(f"**{q}** – {len(lnks)} new")
118
- except Exception:
119
- pass
120
-
121
- # Run Search
122
- if st.button("Run Search 🚀") and query:
123
- with st.spinner("Collecting literature & biomedical data …"):
124
- res = asyncio.run(orchestrate_search(query, llm=engine))
125
- st.session_state.update(
126
- query_result=res,
127
- last_query=query,
128
- last_llm=engine,
129
- followup_input="",
130
- followup_response=None,
131
- )
132
- st.success(f"Completed with **{res['llm_used'].title()}**")
133
-
134
- res = st.session_state.query_result
135
- if not res:
136
- st.info("Enter a question and press **Run Search 🚀**")
137
- return
138
-
139
- # Tabs
140
- tabs = st.tabs(["Results", "Genes", "Trials", "Variants", "Graph", "Metrics", "Visuals"])
141
-
142
- # --- Results tab ---
143
  with tabs[0]:
144
- st.subheader("Literature")
145
- for i, p in enumerate(res['papers'], 1):
146
- st.markdown(f"**{i}. [{p.get('title','')}]({p.get('link','')})** *{p.get('authors','')}*")
147
- st.write(p.get('summary',''))
148
- c1, c2 = st.columns(2)
149
- with c1:
150
- st.download_button("CSV", pd.DataFrame(res['papers']).to_csv(index=False), "papers.csv", "text/csv")
151
- with c2:
152
- st.download_button("PDF", _pdf(res['papers']), "papers.pdf", "application/pdf")
153
- if st.button("💾 Save"):
154
- save_query(st.session_state.last_query, res)
155
- st.success("Saved to workspace")
156
-
157
- st.subheader("UMLS concepts")
158
- for c in res['umls']:
159
- if c.get('cui'):
160
- st.write(f"- **{c.get('name','')}** ({c.get('cui')})")
161
-
162
- st.subheader("OpenFDA safety signals")
163
- for d in res['drug_safety']:
164
- st.json(d)
165
-
166
- st.subheader("AI summary")
167
- st.info(res['ai_summary'])
168
-
169
- # --- Genes tab ---
170
  with tabs[1]:
171
- st.header("Gene / Variant signals")
172
- valid_genes = [g for g in res['genes'] if isinstance(g, dict)]
173
- if valid_genes:
174
- for g in valid_genes:
175
- sym = g.get('symbol') or g.get('name') or ''
176
- st.write(f"- **{sym}**")
177
  else:
178
- st.info("No gene signals returned.")
179
-
180
- mesh_list = [d for d in res['mesh_defs'] if isinstance(d, str) and d]
181
- if mesh_list:
182
- st.markdown("### MeSH definitions")
183
- for d in mesh_list:
184
- st.write(f"- {d}")
185
-
186
- gene_disease = [d for d in res['gene_disease'] if isinstance(d, dict)]
187
- if gene_disease:
188
- st.markdown("### DisGeNET links")
189
- st.json(gene_disease[:15])
190
-
191
- # --- Trials tab ---
192
  with tabs[2]:
193
- st.header("Clinical trials")
194
- trials = res['clinical_trials']
195
- if not trials:
196
- st.info(
197
- "No trials found. Try a disease name (e.g. ‘Breast Neoplasms’) "
198
- "or specific drug (e.g. ‘Pembrolizumab’)."
199
- )
200
  else:
201
- for t in trials:
202
- st.markdown(
203
- f"**{t.get('nctId','')}** {t.get('briefTitle','')} "
204
- f"Phase {t.get('phase','?')} | Status {t.get('status','?')}"
205
- )
206
-
207
- # --- Variants tab ---
208
- with tabs[3]:
209
- st.header("Cancer variants (cBioPortal)")
210
- variants = res['variants']
211
- if not variants:
212
- st.info(
213
- "No variants found. Try a well-known gene symbol like ‘TP53’ or ‘BRCA1’."
214
- )
215
- else:
216
- st.json(variants[:30])
217
-
218
- # --- Graph tab ---
219
- with tabs[4]:
220
- nodes, edges, cfg = build_agraph(res['papers'], res['umls'], res['drug_safety'])
221
- agraph(nodes, edges, cfg)
222
-
223
- # --- Metrics tab ---
224
- with tabs[5]:
225
- G = build_nx([n.__dict__ for n in nodes], [e.__dict__ for e in edges])
226
- st.metric("Density", f"{get_density(G):.3f}")
227
- st.markdown("**Top hubs**")
228
- for nid, sc in get_top_hubs(G):
229
- lab = next((n.label for n in nodes if n.id == nid), nid)
230
- st.write(f"- {lab} {sc:.3f}")
231
-
232
- # --- Visuals tab ---
233
- with tabs[6]:
234
- years = [p.get('published') for p in res['papers'] if p.get('published')]
235
- if years:
236
- st.plotly_chart(px.histogram(years, nbins=12, title="Publication Year"))
237
-
238
- # Follow-up QA (outside tabs)
239
- st.markdown("---")
240
- input_col, button_col = st.columns([4, 1])
241
- with input_col:
242
- followup = st.text_input("Ask follow-up question:", key="followup_input")
243
- with button_col:
244
- if st.button("Ask AI"):
245
- if followup.strip():
246
- with st.spinner("Querying LLM …"):
247
- ans = asyncio.run(
248
- answer_ai_question(
249
- question=followup,
250
- context=st.session_state.last_query,
251
- llm=st.session_state.last_llm,
252
- )
253
- )
254
- st.session_state.followup_response = ans.get('answer', '')
255
- else:
256
- st.warning("Please type a question first.")
257
- if st.session_state.followup_response:
258
- st.write(st.session_state.followup_response)
259
-
260
-
261
- if __name__ == "__main__":
262
- render_ui()
 
1
+ # app.py
2
+ import asyncio, streamlit as st, pandas as pd
3
+ from mcp.orchestrator import orchestrate_search
4
+
5
+ st.set_page_config(layout="wide", page_title="MedGenesis AI")
6
+ if "res" not in st.session_state: st.session_state.res = None
7
+
8
+ st.title("🧬 MedGenesis AI")
9
+ llm = st.radio("LLM engine", ["openai","gemini"], horizontal=True)
10
+ q = st.text_input("Enter biomedical question")
11
+ if st.button("Run Search") and q:
12
+ with st.spinner("Fetching data…"):
13
+ st.session_state.res = asyncio.run(orchestrate_search(q, llm=llm))
14
+
15
+ res = st.session_state.res
16
+ if res:
17
+ st.subheader("🔬 Papers")
18
+ for p in res["papers"]:
19
+ st.markdown(f"**[{p['title']}]({p['link']})** – {p['authors']}")
20
+ st.write(p["summary"])
21
+ st.subheader("💡 AI Summary")
22
+ st.info(res["ai_summary"])
23
+
24
+ tabs = st.tabs(["Graph","Variants","Trials"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  with tabs[0]:
26
+ from mcp.knowledge_graph import build_agraph
27
+ nodes, edges, cfg = build_agraph(res)
28
+ from streamlit_agraph import agraph
29
+ agraph(nodes, edges, cfg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  with tabs[1]:
31
+ if res["variants"]:
32
+ st.json(res["variants"])
 
 
 
 
33
  else:
34
+ st.warning("No variants found. Try TP53 or BRCA1.")
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  with tabs[2]:
36
+ if res["trials"]:
37
+ st.json(res["trials"])
 
 
 
 
 
38
  else:
39
+ st.warning("No trials. Try a disease e.g. ‘Breast Neoplasms’ or a drug.")
40
+ else:
41
+ st.info("Enter a query and press Run Search.")