mgbam commited on
Commit
7e2c73b
Β·
verified Β·
1 Parent(s): c6240ca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -35
app.py CHANGED
@@ -1,23 +1,23 @@
1
  #!/usr/bin/env python3
2
- """MedGenesis AI β€” CPU-only, dual-LLM (OpenAI / Gemini)"""
3
 
4
- # ────────────── FIX: create a writable Streamlit data dir ──────────────
5
  import os, pathlib
6
- os.environ["STREAMLIT_DATA_DIR"] = "/tmp/.streamlit"
7
- os.environ["XDG_STATE_HOME"] = "/tmp"
8
  os.environ["STREAMLIT_BROWSER_GATHERUSAGESTATS"] = "false"
9
  pathlib.Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True)
10
- # ───────────────────────────────────────────────────────────────────────
11
 
 
12
  import asyncio, re
13
  from pathlib import Path
14
-
15
  import streamlit as st
16
  import pandas as pd
17
  import plotly.express as px
18
- from fpdf import FPDF
19
  from streamlit_agraph import agraph
20
 
 
21
  from mcp.orchestrator import orchestrate_search, answer_ai_question
22
  from mcp.workspace import get_workspace, save_query
23
  from mcp.knowledge_graph import build_agraph
@@ -27,19 +27,35 @@ from mcp.alerts import check_alerts
27
  ROOT = Path(__file__).parent
28
  LOGO = ROOT / "assets" / "logo.png"
29
 
30
- # ──────────────── small helpers ─────────────────
 
 
 
 
31
  def _pdf(papers):
32
- pdf = FPDF(); pdf.add_page(); pdf.set_font("Arial", size=11)
33
- pdf.cell(200, 8, "MedGenesis AI – Results", ln=True, align="C"); pdf.ln(3)
 
 
 
 
 
34
  for i, p in enumerate(papers, 1):
35
- pdf.set_font("Arial", "B", 11)
36
- pdf.multi_cell(0, 7, f"{i}. {p['title']}")
37
- pdf.set_font("Arial", "", 9)
38
- pdf.multi_cell(0, 6, f"{p['authors']}\n{p['summary']}\n{p['link']}\n")
 
 
 
 
 
39
  pdf.ln(1)
40
- return pdf.output(dest="S").encode("latin-1")
41
 
42
- def _sidebar_workspace():
 
 
 
43
  with st.sidebar:
44
  st.header("πŸ—‚οΈ Workspace")
45
  ws = get_workspace()
@@ -50,12 +66,12 @@ def _sidebar_workspace():
50
  with st.expander(f"{i}. {item['query']}"):
51
  st.write(item["result"]["ai_summary"])
52
 
53
- # ──────────────── Streamlit UI ──────────────────
54
  def render_ui():
55
  st.set_page_config("MedGenesis AI", layout="wide")
56
- _sidebar_workspace()
57
 
58
- # header
59
  c1, c2 = st.columns([0.15, 0.85])
60
  with c1:
61
  if LOGO.exists():
@@ -64,21 +80,23 @@ def render_ui():
64
  st.markdown("## 🧬 **MedGenesis AI**")
65
  st.caption("Multi-source biomedical assistant Β· OpenAI / Gemini")
66
 
67
- llm = st.radio("LLM engine", ["openai", "gemini"], horizontal=True)
68
  query = st.text_input("Enter biomedical question",
69
  placeholder="e.g. CRISPR glioblastoma therapy")
70
 
71
- # alert check
72
  if get_workspace():
73
  try:
74
  news = asyncio.run(check_alerts([w["query"] for w in get_workspace()]))
75
  if news:
76
- st.sidebar.subheader("πŸ”” New papers")
77
- for q, lst in news.items():
78
- st.sidebar.write(f"**{q}** – {len(lst)} new")
79
- except Exception as e:
80
- st.sidebar.warning(f"Alert check failed: {e}")
81
-
 
 
82
  if st.button("Run Search πŸš€") and query:
83
  with st.spinner("Collecting literature & biomedical data …"):
84
  res = asyncio.run(orchestrate_search(query, llm=llm))
@@ -87,7 +105,7 @@ def render_ui():
87
  tabs = st.tabs(["Results", "Genes", "Trials", "Graph",
88
  "Metrics", "Visuals"])
89
 
90
- # Results -----------------------------------------------------
91
  with tabs[0]:
92
  for i, p in enumerate(res["papers"], 1):
93
  st.markdown(f"**{i}. [{p['title']}]({p['link']})** *{p['authors']}*")
@@ -118,7 +136,7 @@ def render_ui():
118
  st.subheader("AI summary")
119
  st.info(res["ai_summary"])
120
 
121
- # Genes -------------------------------------------------------
122
  with tabs[1]:
123
  st.header("Gene / Variant signals")
124
  for g in res["genes"]:
@@ -133,17 +151,17 @@ def render_ui():
133
  if d:
134
  st.write("-", d)
135
 
136
- # Trials ------------------------------------------------------
137
  with tabs[2]:
138
  st.header("Clinical trials")
139
  if not res["clinical_trials"]:
140
  st.info("No trials (rate-limited or none found).")
141
  for t in res["clinical_trials"]:
142
  st.markdown(f"**{t['NCTId'][0]}** – {t['BriefTitle'][0]}")
143
- st.write(f"Phase {t.get('Phase', [''])[0]} | "
144
- f"Status {t['OverallStatus'][0]}")
145
 
146
- # Graph -------------------------------------------------------
147
  with tabs[3]:
148
  nodes, edges, cfg = build_agraph(res["papers"],
149
  res["umls"],
@@ -155,7 +173,7 @@ def render_ui():
155
  n.color = "#f1c40f" if pat.search(n.label) else "#d3d3d3"
156
  agraph(nodes, edges, cfg)
157
 
158
- # Metrics -----------------------------------------------------
159
  with tabs[4]:
160
  G = build_nx([n.__dict__ for n in nodes],
161
  [e.__dict__ for e in edges])
@@ -165,13 +183,14 @@ def render_ui():
165
  lab = next((n.label for n in nodes if n.id == nid), nid)
166
  st.write(f"- {lab} {sc:.3f}")
167
 
168
- # Visuals -----------------------------------------------------
169
  with tabs[5]:
170
  years = [p["published"] for p in res["papers"] if p.get("published")]
171
  if years:
172
  st.plotly_chart(px.histogram(years, nbins=12,
173
  title="Publication Year"))
174
 
 
175
  st.markdown("---")
176
  follow = st.text_input("Ask follow-up:")
177
  if st.button("Ask AI"):
 
1
  #!/usr/bin/env python3
2
+ # MedGenesis AI Β· CPU-only Streamlit app (OpenAI / Gemini)
3
 
4
+ # ── Streamlit telemetry dir fix ───────────────────────────────────────
5
  import os, pathlib
6
+ os.environ["STREAMLIT_DATA_DIR"] = "/tmp/.streamlit"
7
+ os.environ["XDG_STATE_HOME"] = "/tmp"
8
  os.environ["STREAMLIT_BROWSER_GATHERUSAGESTATS"] = "false"
9
  pathlib.Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True)
 
10
 
11
+ # ── Std-lib / third-party imports ────────────────────────────────────
12
  import asyncio, re
13
  from pathlib import Path
 
14
  import streamlit as st
15
  import pandas as pd
16
  import plotly.express as px
17
+ from fpdf import FPDF # classic FPDF β†’ Latin-1 only
18
  from streamlit_agraph import agraph
19
 
20
+ # ── Internal helpers ────────────────────────────────────────────────
21
  from mcp.orchestrator import orchestrate_search, answer_ai_question
22
  from mcp.workspace import get_workspace, save_query
23
  from mcp.knowledge_graph import build_agraph
 
27
  ROOT = Path(__file__).parent
28
  LOGO = ROOT / "assets" / "logo.png"
29
 
30
+ # ── PDF export helper (UTF-8 β†’ Latin-1 β€œsafe”) ──────────────────────
31
+ def _latin1_safe(txt: str) -> str:
32
+ """Return text that FPDF(latin-1) can embed; replace unknown chars."""
33
+ return txt.encode("latin-1", "replace").decode("latin-1")
34
+
35
  def _pdf(papers):
36
+ pdf = FPDF()
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")
61
  ws = get_workspace()
 
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
+ # Header
75
  c1, c2 = st.columns([0.15, 0.85])
76
  with c1:
77
  if LOGO.exists():
 
80
  st.markdown("## 🧬 **MedGenesis AI**")
81
  st.caption("Multi-source biomedical assistant Β· OpenAI / Gemini")
82
 
83
+ llm = st.radio("LLM engine", ["openai", "gemini"], horizontal=True)
84
  query = st.text_input("Enter biomedical question",
85
  placeholder="e.g. CRISPR glioblastoma therapy")
86
 
87
+ # Alert check
88
  if get_workspace():
89
  try:
90
  news = asyncio.run(check_alerts([w["query"] for w in get_workspace()]))
91
  if news:
92
+ with st.sidebar:
93
+ st.subheader("πŸ”” New papers")
94
+ for q, lnks in news.items():
95
+ st.write(f"**{q}** – {len(lnks)} new")
96
+ except Exception:
97
+ pass
98
+
99
+ # Run search
100
  if st.button("Run Search πŸš€") and query:
101
  with st.spinner("Collecting literature & biomedical data …"):
102
  res = asyncio.run(orchestrate_search(query, llm=llm))
 
105
  tabs = st.tabs(["Results", "Genes", "Trials", "Graph",
106
  "Metrics", "Visuals"])
107
 
108
+ # Results
109
  with tabs[0]:
110
  for i, p in enumerate(res["papers"], 1):
111
  st.markdown(f"**{i}. [{p['title']}]({p['link']})** *{p['authors']}*")
 
136
  st.subheader("AI summary")
137
  st.info(res["ai_summary"])
138
 
139
+ # Genes
140
  with tabs[1]:
141
  st.header("Gene / Variant signals")
142
  for g in res["genes"]:
 
151
  if d:
152
  st.write("-", d)
153
 
154
+ # Trials
155
  with tabs[2]:
156
  st.header("Clinical trials")
157
  if not res["clinical_trials"]:
158
  st.info("No trials (rate-limited or none found).")
159
  for t in res["clinical_trials"]:
160
  st.markdown(f"**{t['NCTId'][0]}** – {t['BriefTitle'][0]}")
161
+ st.write(f"Phase {t.get('Phase', [''])[0]} "
162
+ f"| Status {t['OverallStatus'][0]}")
163
 
164
+ # Graph
165
  with tabs[3]:
166
  nodes, edges, cfg = build_agraph(res["papers"],
167
  res["umls"],
 
173
  n.color = "#f1c40f" if pat.search(n.label) else "#d3d3d3"
174
  agraph(nodes, edges, cfg)
175
 
176
+ # Metrics
177
  with tabs[4]:
178
  G = build_nx([n.__dict__ for n in nodes],
179
  [e.__dict__ for e in edges])
 
183
  lab = next((n.label for n in nodes if n.id == nid), nid)
184
  st.write(f"- {lab} {sc:.3f}")
185
 
186
+ # Visuals
187
  with tabs[5]:
188
  years = [p["published"] for p in res["papers"] if p.get("published")]
189
  if years:
190
  st.plotly_chart(px.histogram(years, nbins=12,
191
  title="Publication Year"))
192
 
193
+ # Follow-up Q-A
194
  st.markdown("---")
195
  follow = st.text_input("Ask follow-up:")
196
  if st.button("Ask AI"):