File size: 6,524 Bytes
3987ef0
978c4cf
3987ef0
 
 
 
94febc8
517de74
9efa522
 
31dadea
a9e8fde
517de74
94febc8
 
 
 
a9e8fde
978c4cf
94febc8
 
42d374e
 
517de74
42d374e
94febc8
42d374e
94febc8
978c4cf
3987ef0
 
 
978c4cf
3987ef0
 
 
978c4cf
9efa522
94febc8
 
9efa522
94febc8
 
 
 
9efa522
94febc8
42d374e
3987ef0
517de74
978c4cf
94febc8
31dadea
94febc8
 
 
 
31dadea
 
94febc8
31dadea
94febc8
31dadea
94febc8
31dadea
94febc8
42d374e
 
94febc8
 
42d374e
94febc8
 
42d374e
 
94febc8
42d374e
9efa522
42d374e
94febc8
517de74
94febc8
42d374e
9efa522
c1cd51c
 
94febc8
c1cd51c
94febc8
 
 
 
 
 
 
c1cd51c
94febc8
 
 
 
c1cd51c
94febc8
 
 
 
 
 
 
 
 
 
 
 
a9e8fde
c1cd51c
94febc8
a9e8fde
cc1d6cf
94febc8
a9e8fde
 
 
 
 
 
 
 
 
94febc8
cc1d6cf
94febc8
c1cd51c
94febc8
c1cd51c
 
 
 
 
978c4cf
94febc8
3987ef0
94febc8
 
3987ef0
94febc8
 
 
42d374e
 
94febc8
978c4cf
3987ef0
 
 
94febc8
3987ef0
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# app.py

import os
import streamlit as st
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import asyncio
from pathlib import Path
import pandas as pd
from fpdf import FPDF
import plotly.express as px
import re

from mcp.orchestrator import orchestrate_search, answer_ai_question
from mcp.schemas import UnifiedSearchInput, UnifiedSearchResult
from mcp.workspace import get_workspace, save_query
from mcp.knowledge_graph import build_agraph
from streamlit_agraph import agraph, Node, Edge, Config

ROOT = Path(__file__).parent
LOGO = ROOT / "assets" / "logo.png"

api = FastAPI(
    title="MedGenesis MCP Server",
    version="2.0.0",
    description="MedGenesis AI – Unified Intelligence from PubMed, ArXiv, OpenFDA, UMLS, GPT-4o"
)
api.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])

@api.post("/unified_search", response_model=UnifiedSearchResult)
async def unified_search_endpoint(data: UnifiedSearchInput):
    return await orchestrate_search(data.query)

@api.post("/ask_ai")
async def ask_ai_endpoint(question: str, context: str = ""):
    return await answer_ai_question(question, context)

def generate_pdf(papers):
    pdf = FPDF(); pdf.add_page(); pdf.set_font("Arial", size=12)
    pdf.cell(200, 10, "MedGenesis AI - Search Results", ln=True, align="C")
    pdf.ln(10)
    for i, p in enumerate(papers, 1):
        pdf.set_font("Arial", "B", 12); pdf.multi_cell(0, 10, f"{i}. {p['title']}")
        pdf.set_font("Arial", "", 10)
        pdf.multi_cell(0, 8, f"Authors: {p['authors']}\nLink: {p['link']}\nSummary: {p['summary']}\n")
        pdf.ln(2)
    return pdf.output(dest="S").encode("latin-1")

def render_ui():
    st.set_page_config(page_title="MedGenesis AI", layout="wide")

    # Sidebar workspace
    with st.sidebar:
        st.header("πŸ—‚οΈ Workspace")
        ws = get_workspace()
        if ws:
            for i, item in enumerate(ws, 1):
                with st.expander(f"{i}. {item['query']}"):
                    st.write("**AI Summary:**", item["result"]["ai_summary"])
                    st.write("**Top Paper:**", item["result"]["papers"][0]["title"] if item["result"]["papers"] else "None")
                    df = pd.DataFrame(item["result"]["papers"])
                    st.download_button("πŸ“₯ CSV", df.to_csv(index=False), f"query_{i}.csv", "text/csv")
        else:
            st.info("Run & save searches here.")

    # Header
    col1, col2 = st.columns([0.15, 0.85])
    with col1:
        if LOGO.exists():
            st.image(str(LOGO), width=100)
    with col2:
        st.markdown("## 🧬 MedGenesis AI\n*Unified Intelligence from PubMed, ArXiv, OpenFDA, UMLS, GPT‑4o*")
        st.caption("Built by Oluwafemi Idiakhoa β€’ Hugging Face Spaces")

    st.markdown("---")
    query = st.text_input("πŸ” Enter biomedical research question:", placeholder="e.g. Glioblastoma CRISPR treatments")

    results = None
    if st.button("Run Search πŸš€"):
        with st.spinner("Fetching & synthesizing..."):
            results = asyncio.run(orchestrate_search(query))
            st.success("βœ… Search complete")

    if results:
        tabs = st.tabs(["πŸ“ Results", "πŸ—ΊοΈ Knowledge Graph", "πŸ“Š Visualizations"])

        # Tab 1: Results
        with tabs[0]:
            st.header("πŸ“š Top Papers")
            for i, p in enumerate(results["papers"], 1):
                st.markdown(f"**{i}. [{p['title']}]({p['link']})**  \n*{p['authors']}* ({p['source']})")
                st.markdown(f"<div style='color:gray'>{p['summary']}</div>", unsafe_allow_html=True)

            if st.button("Save to Workspace"):
                save_query(query, results); st.success("Saved!")

            df = pd.DataFrame(results["papers"])
            st.download_button("πŸ“₯ Download CSV", df.to_csv(index=False), "results.csv", "text/csv")
            pdf = generate_pdf(results["papers"])
            st.download_button("πŸ“„ Download PDF", pdf, "results.pdf", "application/pdf")

            st.subheader("🧠 UMLS Concepts")
            for c in results["umls"]:
                if c.get("cui"):
                    st.markdown(f"- **{c['name']}** (CUI: `{c['cui']}`): {c.get('definition','No definition')}")

            st.subheader("πŸ’Š Drug Safety (OpenFDA)")
            for d in results["drug_safety"]:
                st.json(d)

            st.subheader("πŸ€– AI Summary")
            st.info(results["ai_summary"])

        # Tab 2: Knowledge Graph with Search & Highlight
        with tabs[1]:
            st.header("πŸ—ΊοΈ Knowledge Graph Explorer")
            search_term = st.text_input("πŸ”Ž Highlight nodes containing:", value="")
            try:
                nodes, edges, config = build_agraph(results["papers"], results["umls"], results["drug_safety"])
                # Highlight logic
                if search_term.strip():
                    pattern = re.compile(re.escape(search_term), re.IGNORECASE)
                    for node in nodes:
                        if pattern.search(node.label) or (hasattr(node, "tooltip") and pattern.search(getattr(node, "tooltip", ""))):
                            node.color = "#f1c40f"
                            node.size = max(node.size, 30)
                        else:
                            node.color = "#ddd"
                agraph(nodes=nodes, edges=edges, config=config)
            except Exception as e:
                st.warning("Knowledge graph unavailable: " + str(e))

        # Tab 3: Visualizations
        with tabs[2]:
            pub_years = [p["published"] for p in results["papers"] if p.get("published")]
            if pub_years:
                fig = px.histogram(pub_years, nbins=10, title="Publication Year Distribution")
                st.plotly_chart(fig)

    # Follow-up Q&A
    st.markdown("---")
    st.subheader("πŸ’¬ Ask Follow-up AI Question")
    follow_up = st.text_input("Based on above:", placeholder="e.g. Most promising therapy?")
    if st.button("Ask AI"):
        with st.spinner("Thinking..."):
            ai_ans = asyncio.run(answer_ai_question(follow_up, context=query))
            st.write(ai_ans.get("answer", ai_ans))

    st.markdown("---")
    st.caption("✨ Built by Oluwafemi Idiakhoa β€’ Powered by FastAPI, Streamlit, OpenAI, UMLS, OpenFDA, NCBI")

if __name__ == "__main__":
    import sys
    if "runserver" in sys.argv:
        import uvicorn; uvicorn.run(api, host="0.0.0.0", port=7860)
    else:
        render_ui()