mgbam commited on
Commit
fbb4b8d
Β·
verified Β·
1 Parent(s): a7d3db7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -99
app.py CHANGED
@@ -9,7 +9,6 @@ os.environ["XDG_STATE_HOME"] = "/tmp"
9
  os.environ["STREAMLIT_BROWSER_GATHERUSAGESTATS"] = "false"
10
  pathlib.Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True)
11
 
12
- # ── Std-lib / third-party imports ────────────────────────────────────
13
  import asyncio, re
14
  from pathlib import Path
15
  import streamlit as st
@@ -18,17 +17,15 @@ import plotly.express as px
18
  from fpdf import FPDF
19
  from streamlit_agraph import agraph
20
 
21
- # ── Internal helpers ────────────────────────────────────────────────
22
- from mcp.orchestrator import orchestrate_search, answer_ai_question
23
- from mcp.workspace import get_workspace, save_query
24
  from mcp.knowledge_graph import build_agraph
25
- from mcp.graph_metrics import build_nx, get_top_hubs, get_density
26
- from mcp.alerts import check_alerts
27
 
28
  ROOT = Path(__file__).parent
29
  LOGO = ROOT / "assets" / "logo.png"
30
 
31
- # ── PDF export helper (UTF-8 β†’ Latin-1 β€œsafe”) ──────────────────────
32
  def _latin1_safe(txt: str) -> str:
33
  return txt.encode("latin-1", "replace").decode("latin-1")
34
 
@@ -37,24 +34,17 @@ def _pdf(papers):
37
  pdf.set_auto_page_break(auto=True, margin=15)
38
  pdf.add_page()
39
  pdf.set_font("Helvetica", size=11)
40
- pdf.cell(200, 8, _latin1_safe("MedGenesis AI – Results"), ln=True, align="C")
41
  pdf.ln(3)
42
-
43
  for i, p in enumerate(papers, 1):
44
  pdf.set_font("Helvetica", "B", 11)
45
  pdf.multi_cell(0, 7, _latin1_safe(f"{i}. {p['title']}"))
46
  pdf.set_font("Helvetica", "", 9)
47
- body = (
48
- f"{p['authors']}\n"
49
- f"{p['summary']}\n"
50
- f"{p['link']}\n"
51
- )
52
  pdf.multi_cell(0, 6, _latin1_safe(body))
53
  pdf.ln(1)
54
-
55
  return pdf.output(dest="S").encode("latin-1", "replace")
56
 
57
- # ── Sidebar workspace ───────────────────────────────────────────────
58
  def _workspace_sidebar():
59
  with st.sidebar:
60
  st.header("πŸ—‚οΈ Workspace")
@@ -66,9 +56,15 @@ def _workspace_sidebar():
66
  with st.expander(f"{i}. {item['query']}"):
67
  st.write(item["result"]["ai_summary"])
68
 
69
- # ── Main UI ─────────────────────────────────────────────────────────
70
  def render_ui():
71
  st.set_page_config("MedGenesis AI", layout="wide")
 
 
 
 
 
 
 
72
  _workspace_sidebar()
73
 
74
  c1, c2 = st.columns([0.15, 0.85])
@@ -79,7 +75,7 @@ def render_ui():
79
  st.markdown("## 🧬 **MedGenesis AI**")
80
  st.caption("Multi-source biomedical assistant Β· OpenAI / Gemini")
81
 
82
- llm = st.radio("LLM engine", ["openai", "gemini"], horizontal=True)
83
  query = st.text_input("Enter biomedical question", placeholder="e.g. CRISPR glioblastoma therapy")
84
 
85
  # Alert check
@@ -94,98 +90,111 @@ def render_ui():
94
  except Exception:
95
  pass
96
 
97
- # Run search
98
  if st.button("Run Search πŸš€") and query:
99
  with st.spinner("Collecting literature & biomedical data …"):
100
  res = asyncio.run(orchestrate_search(query, llm=llm))
101
  st.success(f"Completed with **{res['llm_used'].title()}**")
102
-
103
- tabs = st.tabs(["Results", "Genes", "Trials", "Graph", "Metrics", "Visuals"])
104
-
105
- with tabs[0]:
106
- for i, p in enumerate(res["papers"], 1):
107
- st.markdown(f"**{i}. [{p['title']}]({p['link']})** *{p['authors']}*")
108
- st.write(p["summary"])
109
-
110
- col1, col2 = st.columns(2)
111
- with col1:
112
- st.download_button("CSV", pd.DataFrame(res["papers"]).to_csv(index=False), "papers.csv", "text/csv")
113
- with col2:
114
- st.download_button("PDF", _pdf(res["papers"]), "papers.pdf", "application/pdf")
115
-
116
- if st.button("πŸ’Ύ Save"):
117
- save_query(query, res)
118
- st.success("Saved to workspace")
119
-
120
- st.subheader("UMLS concepts")
121
- for c in res["umls"]:
122
- if c.get("cui"):
123
- st.write(f"- **{c['name']}** ({c['cui']})")
124
-
125
- st.subheader("OpenFDA safety")
126
- for d in res["drug_safety"]:
127
- st.json(d)
128
-
129
- st.subheader("AI summary")
130
- st.info(res["ai_summary"])
131
-
132
- with tabs[1]:
133
- st.header("Gene / Variant signals")
134
- for g in res["genes"]:
135
- st.write(f"- **{g.get('name', g.get('geneid'))}** {g.get('description', '')}")
136
- if res["gene_disease"]:
137
- st.markdown("### DisGeNET links")
138
- st.json(res["gene_disease"][:15])
139
- if res["mesh_defs"]:
140
- st.markdown("### MeSH definitions")
141
- for d in res["mesh_defs"]:
142
- if d:
143
- st.write("-", d)
144
-
145
- with tabs[2]:
146
- st.header("Clinical trials")
147
- if not res["clinical_trials"]:
148
- st.info("No trials (rate-limited or none found).")
149
- for t in res["clinical_trials"]:
150
- st.markdown(f"**{t['NCTId'][0]}** – {t['BriefTitle'][0]}")
151
- st.write(f"Phase {t.get('Phase',[''])[0]} | Status {t['OverallStatus'][0]}")
152
-
153
- with tabs[3]:
154
- nodes, edges, cfg = build_agraph(res["papers"], res["umls"], res["drug_safety"])
155
- hl = st.text_input("Highlight node:", key="hl")
156
- if hl:
157
- pat = re.compile(re.escape(hl), re.I)
158
- for n in nodes:
159
- n.color = "#f1c40f" if pat.search(n.label) else "#d3d3d3"
160
- agraph(nodes, edges, cfg)
161
-
162
- with tabs[4]:
163
- G = build_nx([n.__dict__ for n in nodes], [e.__dict__ for e in edges])
164
- st.metric("Density", f"{get_density(G):.3f}")
165
- st.markdown("**Top hubs**")
166
- for nid, sc in get_top_hubs(G):
167
- lab = next((n.label for n in nodes if n.id == nid), nid)
168
- st.write(f"- {lab} {sc:.3f}")
169
-
170
- with tabs[5]:
171
- years = [p["published"] for p in res["papers"] if p.get("published")]
172
- if years:
173
- st.plotly_chart(px.histogram(years, nbins=12, title="Publication Year"))
174
-
175
- # ── Follow-up Q-A (fixed) ───────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
176
  st.markdown("---")
177
- follow = st.text_input("Ask follow-up question:", key="followup_input") # βœ… UPDATED
 
 
178
  if st.button("Ask AI"):
179
- if follow.strip(): # βœ… UPDATED
 
180
  with st.spinner("Generating AI response..."):
181
- ans = asyncio.run(answer_ai_question(follow, context=query, llm=llm))
 
182
  st.write(ans["answer"])
183
  else:
184
- st.warning("Please type a follow-up question before submitting.") # βœ… UPDATED
185
-
186
  else:
187
  st.info("Enter a question and press **Run Search πŸš€**")
188
 
189
- # entry-point
190
  if __name__ == "__main__":
191
  render_ui()
 
9
  os.environ["STREAMLIT_BROWSER_GATHERUSAGESTATS"] = "false"
10
  pathlib.Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True)
11
 
 
12
  import asyncio, re
13
  from pathlib import Path
14
  import streamlit as st
 
17
  from fpdf import FPDF
18
  from streamlit_agraph import agraph
19
 
20
+ from mcp.orchestrator import orchestrate_search, answer_ai_question
21
+ from mcp.workspace import get_workspace, save_query
 
22
  from mcp.knowledge_graph import build_agraph
23
+ from mcp.graph_metrics import build_nx, get_top_hubs, get_density
24
+ from mcp.alerts import check_alerts
25
 
26
  ROOT = Path(__file__).parent
27
  LOGO = ROOT / "assets" / "logo.png"
28
 
 
29
  def _latin1_safe(txt: str) -> str:
30
  return txt.encode("latin-1", "replace").decode("latin-1")
31
 
 
34
  pdf.set_auto_page_break(auto=True, margin=15)
35
  pdf.add_page()
36
  pdf.set_font("Helvetica", size=11)
37
+ pdf.cell(200, 8, _latin1_safe("MedGenesis AI – Results"), ln=True, align="C")
38
  pdf.ln(3)
 
39
  for i, p in enumerate(papers, 1):
40
  pdf.set_font("Helvetica", "B", 11)
41
  pdf.multi_cell(0, 7, _latin1_safe(f"{i}. {p['title']}"))
42
  pdf.set_font("Helvetica", "", 9)
43
+ body = f"{p['authors']}\n{p['summary']}\n{p['link']}\n"
 
 
 
 
44
  pdf.multi_cell(0, 6, _latin1_safe(body))
45
  pdf.ln(1)
 
46
  return pdf.output(dest="S").encode("latin-1", "replace")
47
 
 
48
  def _workspace_sidebar():
49
  with st.sidebar:
50
  st.header("πŸ—‚οΈ Workspace")
 
56
  with st.expander(f"{i}. {item['query']}"):
57
  st.write(item["result"]["ai_summary"])
58
 
 
59
  def render_ui():
60
  st.set_page_config("MedGenesis AI", layout="wide")
61
+
62
+ # Initialize session-state
63
+ if "followup_input" not in st.session_state:
64
+ st.session_state.followup_input = ""
65
+ if "tab_index" not in st.session_state:
66
+ st.session_state.tab_index = 0
67
+
68
  _workspace_sidebar()
69
 
70
  c1, c2 = st.columns([0.15, 0.85])
 
75
  st.markdown("## 🧬 **MedGenesis AI**")
76
  st.caption("Multi-source biomedical assistant Β· OpenAI / Gemini")
77
 
78
+ llm = st.radio("LLM engine", ["openai", "gemini"], horizontal=True)
79
  query = st.text_input("Enter biomedical question", placeholder="e.g. CRISPR glioblastoma therapy")
80
 
81
  # Alert check
 
90
  except Exception:
91
  pass
92
 
93
+ # Trigger search
94
  if st.button("Run Search πŸš€") and query:
95
  with st.spinner("Collecting literature & biomedical data …"):
96
  res = asyncio.run(orchestrate_search(query, llm=llm))
97
  st.success(f"Completed with **{res['llm_used'].title()}**")
98
+ st.session_state.query_result = res
99
+ # Reset follow-up input
100
+ st.session_state.followup_input = ""
101
+ st.session_state.tab_index = 0
102
+ else:
103
+ res = st.session_state.get("query_result", None)
104
+
105
+ if res:
106
+ tabs_list = ["Results", "Genes", "Trials", "Graph", "Metrics", "Visuals"]
107
+ tabs = st.tabs(tabs_list, index=st.session_state.tab_index)
108
+
109
+ for idx, name in enumerate(tabs_list):
110
+ with tabs[idx]:
111
+ st.session_state.tab_index = idx
112
+ if name == "Results":
113
+ for i, p in enumerate(res["papers"], 1):
114
+ st.markdown(f"**{i}. [{p['title']}]({p['link']})** *{p['authors']}*")
115
+ st.write(p["summary"])
116
+ col1, col2 = st.columns(2)
117
+ with col1:
118
+ st.download_button("CSV", pd.DataFrame(res["papers"]).to_csv(index=False),
119
+ "papers.csv", "text/csv")
120
+ with col2:
121
+ st.download_button("PDF", _pdf(res["papers"]), "papers.pdf", "application/pdf")
122
+ if st.button("πŸ’Ύ Save"):
123
+ save_query(query, res)
124
+ st.success("Saved to workspace")
125
+ st.subheader("UMLS concepts")
126
+ for c in res["umls"]:
127
+ if c.get("cui"):
128
+ st.write(f"- **{c['name']}** ({c['cui']})")
129
+ st.subheader("OpenFDA safety")
130
+ for d in res["drug_safety"]:
131
+ st.json(d)
132
+ st.subheader("AI summary")
133
+ st.info(res["ai_summary"])
134
+
135
+ elif name == "Genes":
136
+ st.header("Gene / Variant signals")
137
+ for g in res["genes"]:
138
+ st.write(f"- **{g.get('name', g.get('geneid'))}** "
139
+ f"{g.get('description', '')}")
140
+ if res["gene_disease"]:
141
+ st.markdown("### DisGeNET links")
142
+ st.json(res["gene_disease"][:15])
143
+ if res["mesh_defs"]:
144
+ st.markdown("### MeSH definitions")
145
+ for d in res["mesh_defs"]:
146
+ if d:
147
+ st.write("-", d)
148
+
149
+ elif name == "Trials":
150
+ st.header("Clinical trials")
151
+ if not res["clinical_trials"]:
152
+ st.info("No trials (rate-limited or none found).")
153
+ for t in res["clinical_trials"]:
154
+ st.markdown(f"**{t['NCTId'][0]}** – {t['BriefTitle'][0]}")
155
+ st.write(f"Phase {t.get('Phase',[''])[0]} | "
156
+ f"Status {t['OverallStatus'][0]}")
157
+
158
+ elif name == "Graph":
159
+ nodes, edges, cfg = build_agraph(res["papers"], res["umls"], res["drug_safety"])
160
+ hl = st.text_input("Highlight node:", key="hl")
161
+ if hl:
162
+ pat = re.compile(re.escape(hl), re.I)
163
+ for n in nodes:
164
+ n.color = "#f1c40f" if pat.search(n.label) else "#d3d3d3"
165
+ agraph(nodes, edges, cfg)
166
+
167
+ elif name == "Metrics":
168
+ nodes, edges, _ = build_agraph(res["papers"], res["umls"], res["drug_safety"])
169
+ G = build_nx([n.__dict__ for n in nodes], [e.__dict__ for e in edges])
170
+ st.metric("Density", f"{get_density(G):.3f}")
171
+ st.markdown("**Top hubs**")
172
+ for nid, sc in get_top_hubs(G):
173
+ lab = next((n.label for n in nodes if n.id == nid), nid)
174
+ st.write(f"- {lab} {sc:.3f}")
175
+
176
+ elif name == "Visuals":
177
+ years = [p["published"] for p in res["papers"] if p.get("published")]
178
+ if years:
179
+ st.plotly_chart(px.histogram(years, nbins=12,
180
+ title="Publication Year"))
181
+
182
+ # Follow-up Q-A persistently under tabs
183
  st.markdown("---")
184
+ follow = st.text_input("Ask follow‑up question:",
185
+ value=st.session_state.followup_input,
186
+ key="followup_input")
187
  if st.button("Ask AI"):
188
+ st.session_state.followup_input = follow
189
+ if follow.strip():
190
  with st.spinner("Generating AI response..."):
191
+ ans = asyncio.run(answer_ai_question(
192
+ follow, context=query, llm=llm))
193
  st.write(ans["answer"])
194
  else:
195
+ st.warning("Please type a follow-up question before submitting.")
 
196
  else:
197
  st.info("Enter a question and press **Run Search πŸš€**")
198
 
 
199
  if __name__ == "__main__":
200
  render_ui()