Spaces:
Sleeping
Sleeping
Update
Browse files
README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
---
|
2 |
-
title: "
|
3 |
-
emoji: "
|
4 |
-
colorFrom: "
|
5 |
-
colorTo: "
|
6 |
sdk: "gradio"
|
7 |
app_file: "app.py"
|
8 |
pinned: false
|
@@ -10,36 +10,61 @@ secrets:
|
|
10 |
- MODAL_BACKEND_URL
|
11 |
---
|
12 |
|
13 |
-
#
|
14 |
|
15 |
-
**
|
16 |
|
17 |
-
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
-
|
22 |
-
-
|
23 |
-
-
|
24 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
---
|
27 |
|
28 |
-
##
|
|
|
|
|
29 |
|
30 |
```plaintext
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: "MediAgent AI: Your Trusted AI Health & Drug Navigator"
|
3 |
+
emoji: "π§¬"
|
4 |
+
colorFrom: "indigo"
|
5 |
+
colorTo: "purple"
|
6 |
sdk: "gradio"
|
7 |
app_file: "app.py"
|
8 |
pinned: false
|
|
|
10 |
- MODAL_BACKEND_URL
|
11 |
---
|
12 |
|
13 |
+
# 𧬠MediAgent AI: Your Trusted AI Health & Drug Navigator
|
14 |
|
15 |
+
**MediAgent AI** is a sophisticated, multilingual health assistant designed to provide reliable, sourced, and easy-to-understand answers to a wide range of medical questions. It leverages a powerful agentic framework to reason, select tools, and synthesize information from trusted sources.
|
16 |
|
17 |
+
This application is built with a decoupled architecture: a modern Gradio UI hosted on Hugging Face Spaces and a powerful, serverless GPU backend running on [Modal](https://modal.com).
|
18 |
|
19 |
+
## π Key Features
|
20 |
+
|
21 |
+
- π¬ **Multilingual Support**: Automatically detects and responds in **English**, **Hindi**, **Spanish**, and **French**.
|
22 |
+
- π οΈ **Agentic Tool Use**: Instead of a simple search, MediAgent AI actively chooses the best tool for your question from its specialized toolkit:
|
23 |
+
- `search_pubmed`: For technical and clinical research.
|
24 |
+
- `search_web`: For general health and symptom information.
|
25 |
+
- `check_drug_interactions`: To check for interactions between multiple drugs via the openFDA API.
|
26 |
+
- `check_drug_side_effects`: To find adverse reactions for a specific drug via the openFDA API.
|
27 |
+
- π‘οΈ **Advanced Safety Layers**: Features a critical pre-check system that identifies queries related to **physical medical emergencies** or **mental health crises** and provides immediate, appropriate guidance instead of attempting an answer.
|
28 |
+
- π§ **Agentic Reasoning Engine**: Powered by **Mistral-7B-Instruct-v0.2** and a **LlamaIndex ReAct Agent**, allowing the model to think, act, and observe in a loop to arrive at a well-reasoned conclusion.
|
29 |
+
- π **Sourced & Explained Answers**: All information is presented with citations (links to PubMed/web sources or notes from the FDA database) and complex medical terms are automatically explained in simple language.
|
30 |
+
- β¨ **Modern & User-Friendly UI**: A clean, responsive interface built with [Gradio](https://www.gradio.app) that includes example prompts and a one-click "Copy to Clipboard" button for convenience.
|
31 |
|
32 |
---
|
33 |
|
34 |
+
## π οΈ Architecture Overview
|
35 |
+
|
36 |
+
MediAgent AI uses an advanced agentic architecture, which is more dynamic than a standard RAG pipeline.
|
37 |
|
38 |
```plaintext
|
39 |
+
User Input (any language)
|
40 |
+
β
|
41 |
+
βββββββββββββββ΄ββββββββββββββ
|
42 |
+
β Gradio UI on HF Spaces β
|
43 |
+
βββββββββββββββ¬ββββββββββββββ
|
44 |
+
β (API Call to Modal)
|
45 |
+
βββββββββββββββββββββββββββββ
|
46 |
+
β Modal Serverless Backend β
|
47 |
+
βββββββββββββββ¬ββββββββββββββ
|
48 |
+
β
|
49 |
+
1. π‘οΈ Critical Safety Pre-Check
|
50 |
+
(Is it a medical or mental health emergency?)
|
51 |
+
β
|
52 |
+
2. π Language Detection & Translation to English
|
53 |
+
β
|
54 |
+
3. π€ LlamaIndex ReAct Agent (Mistral-7B)
|
55 |
+
(Agentic Loop Begins)
|
56 |
+
a. π€ Thought: "Which tool should I use based on the user's query?"
|
57 |
+
b. π¬ Action: Selects and executes a tool (e.g., check_drug_interactions)
|
58 |
+
c. π Observation: Receives data back from the tool (e.g., FDA results)
|
59 |
+
(Loop continues until the agent has enough information)
|
60 |
+
β
|
61 |
+
4. π Final Answer Synthesis
|
62 |
+
(Agent composes a complete answer, explaining complex terms)
|
63 |
+
β
|
64 |
+
5. π Back-Translation to Original Language
|
65 |
+
β
|
66 |
+
6. π€ Formatted Markdown Response
|
67 |
+
β
|
68 |
+
βββββββββββββββββββββββββββββ
|
69 |
+
β Final Answer Displayed in UI β
|
70 |
+
βββββββββββββββββββββββββββββββ
|
app.py
CHANGED
@@ -7,21 +7,19 @@ import uuid
|
|
7 |
MODAL_BACKEND_URL = os.getenv("MODAL_BACKEND_URL")
|
8 |
|
9 |
# --- Session ID ---
|
10 |
-
SESSION_ID = str(uuid.uuid4())
|
11 |
|
12 |
# --- Backend Communication ---
|
13 |
def call_modal_backend(user_input):
|
|
|
14 |
if not MODAL_BACKEND_URL:
|
15 |
return {
|
16 |
-
"final_markdown": "### β Configuration Error\n**Reason**: `MODAL_BACKEND_URL` is not set.",
|
17 |
-
"detected_lang": "
|
18 |
}
|
19 |
|
20 |
headers = {"Content-Type": "application/json"}
|
21 |
-
data = {
|
22 |
-
"user_input": user_input,
|
23 |
-
"session_id": SESSION_ID,
|
24 |
-
}
|
25 |
|
26 |
try:
|
27 |
response = requests.post(MODAL_BACKEND_URL, headers=headers, json=data, timeout=300)
|
@@ -33,52 +31,99 @@ def call_modal_backend(user_input):
|
|
33 |
"detected_lang": "Error"
|
34 |
}
|
35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
# --- Gradio UI Definition ---
|
37 |
-
|
|
|
|
|
38 |
gr.Markdown("""
|
39 |
-
#
|
40 |
-
|
41 |
""")
|
42 |
|
43 |
-
|
|
|
44 |
inp = gr.Textbox(
|
45 |
-
label="β
|
46 |
-
placeholder="e.g., What are the symptoms of dengue?",
|
47 |
-
|
|
|
48 |
)
|
49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
|
51 |
-
|
52 |
-
output = gr.Markdown(label="β
Answer & Explanation")
|
53 |
backend_result = gr.State()
|
54 |
|
|
|
55 |
def start_processing(user_input):
|
56 |
if not user_input.strip():
|
57 |
return {
|
58 |
-
|
59 |
-
btn: gr.update(),
|
60 |
-
output: "Please enter a question before submitting.",
|
61 |
lang_detected: gr.update(visible=False),
|
|
|
62 |
}
|
63 |
return {
|
64 |
inp: gr.update(interactive=False),
|
65 |
-
btn: gr.update(interactive=False, value="β³
|
66 |
output: "",
|
67 |
-
lang_detected: gr.update(visible=True, value="π
|
|
|
68 |
}
|
69 |
|
70 |
def finish_processing(response_json):
|
|
|
|
|
|
|
|
|
|
|
71 |
return {
|
72 |
-
inp: gr.update(interactive=True),
|
73 |
-
btn: gr.update(interactive=True, value="π
|
74 |
-
output:
|
75 |
-
lang_detected: gr.update(visible=True, value=f"π Language detected: **{
|
|
|
76 |
}
|
77 |
-
|
78 |
-
|
|
|
79 |
fn=start_processing,
|
80 |
inputs=[inp],
|
81 |
-
outputs=[inp, btn, output, lang_detected]
|
82 |
).then(
|
83 |
fn=call_modal_backend,
|
84 |
inputs=[inp],
|
@@ -86,8 +131,20 @@ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important;}")
|
|
86 |
).then(
|
87 |
fn=finish_processing,
|
88 |
inputs=[backend_result],
|
89 |
-
outputs=[inp, btn, output, lang_detected]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
)
|
91 |
|
92 |
if __name__ == "__main__":
|
93 |
-
demo.launch()
|
|
|
7 |
MODAL_BACKEND_URL = os.getenv("MODAL_BACKEND_URL")
|
8 |
|
9 |
# --- Session ID ---
|
10 |
+
SESSION_ID = str(uuid.uuid4())
|
11 |
|
12 |
# --- Backend Communication ---
|
13 |
def call_modal_backend(user_input):
|
14 |
+
"""Sends the user's question to the Modal backend and returns the response."""
|
15 |
if not MODAL_BACKEND_URL:
|
16 |
return {
|
17 |
+
"final_markdown": "### β Configuration Error\n**Reason**: `MODAL_BACKEND_URL` environment variable is not set.",
|
18 |
+
"detected_lang": "Error"
|
19 |
}
|
20 |
|
21 |
headers = {"Content-Type": "application/json"}
|
22 |
+
data = {"user_input": user_input, "session_id": SESSION_ID}
|
|
|
|
|
|
|
23 |
|
24 |
try:
|
25 |
response = requests.post(MODAL_BACKEND_URL, headers=headers, json=data, timeout=300)
|
|
|
31 |
"detected_lang": "Error"
|
32 |
}
|
33 |
|
34 |
+
# --- UI Styling ---
|
35 |
+
CUSTOM_CSS = """
|
36 |
+
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
|
37 |
+
.gradio-container { max-width: 800px !important; margin: auto; padding-top: 2rem; }
|
38 |
+
.output-area {
|
39 |
+
background-color: #f9f9f9;
|
40 |
+
padding: 1.5rem;
|
41 |
+
border-radius: 0.5rem;
|
42 |
+
border: 1px solid #e0e0e0;
|
43 |
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
44 |
+
}
|
45 |
+
footer { display: none !important; }
|
46 |
+
#user-input textarea { font-size: 1rem; line-height: 1.5; }
|
47 |
+
"""
|
48 |
+
|
49 |
# --- Gradio UI Definition ---
|
50 |
+
# UPDATED: Title and header to reflect the new name "MediAgent AI"
|
51 |
+
with gr.Blocks(theme=gr.themes.Soft(), css=CUSTOM_CSS, title="MediAgent AI") as demo:
|
52 |
+
# --- Header ---
|
53 |
gr.Markdown("""
|
54 |
+
# 𧬠MediAgent AI
|
55 |
+
_Your Trusted AI Health & Drug Navigator_
|
56 |
""")
|
57 |
|
58 |
+
# --- Input Section ---
|
59 |
+
with gr.Group():
|
60 |
inp = gr.Textbox(
|
61 |
+
label="β Ask any health question (e.g., about symptoms, drugs, or diseases)",
|
62 |
+
placeholder="e.g., What are the symptoms of dengue? or Do aspirin and warfarin interact?",
|
63 |
+
lines=3,
|
64 |
+
elem_id="user-input"
|
65 |
)
|
66 |
+
with gr.Row():
|
67 |
+
btn = gr.Button("π Get Answer", variant="primary", scale=2)
|
68 |
+
clear_btn = gr.ClearButton(value="Clear", scale=1)
|
69 |
+
|
70 |
+
gr.Examples(
|
71 |
+
examples=[
|
72 |
+
["What are symptoms of malaria?"],
|
73 |
+
["Do aspirin and warfarin interact?"],
|
74 |
+
["ΒΏCuΓ‘les son los efectos secundarios del ibuprofeno?"],
|
75 |
+
["COVID-19 ΰ€ΰ₯ ΰ€²ΰ€ΰ₯ΰ€·ΰ€£ ΰ€ΰ₯ΰ€―ΰ€Ύ ΰ€Ήΰ₯ΰ€?"],
|
76 |
+
],
|
77 |
+
inputs=inp,
|
78 |
+
label="Example Questions"
|
79 |
+
)
|
80 |
+
|
81 |
+
# --- Output Section ---
|
82 |
+
lang_detected = gr.Label(value="", visible=False)
|
83 |
+
|
84 |
+
with gr.Column(elem_classes=["output-area"]):
|
85 |
+
output = gr.Markdown(label="β
Answer & Explanation")
|
86 |
+
|
87 |
+
copy_btn = gr.Button("π Copy Answer to Clipboard", visible=False)
|
88 |
|
89 |
+
# --- Backend State ---
|
|
|
90 |
backend_result = gr.State()
|
91 |
|
92 |
+
# --- UI Logic Functions ---
|
93 |
def start_processing(user_input):
|
94 |
if not user_input.strip():
|
95 |
return {
|
96 |
+
output: gr.Markdown("Please enter a question before submitting."),
|
|
|
|
|
97 |
lang_detected: gr.update(visible=False),
|
98 |
+
copy_btn: gr.update(visible=False),
|
99 |
}
|
100 |
return {
|
101 |
inp: gr.update(interactive=False),
|
102 |
+
btn: gr.update(interactive=False, value="β³ Thinking..."),
|
103 |
output: "",
|
104 |
+
lang_detected: gr.update(visible=True, value="π Analyzing your question..."),
|
105 |
+
copy_btn: gr.update(visible=False),
|
106 |
}
|
107 |
|
108 |
def finish_processing(response_json):
|
109 |
+
final_markdown = response_json.get("final_markdown", "Error: No response from backend.")
|
110 |
+
detected_lang = response_json.get('detected_lang', 'Unknown')
|
111 |
+
|
112 |
+
show_copy = "error" not in detected_lang.lower() and "error" not in final_markdown.lower()
|
113 |
+
|
114 |
return {
|
115 |
+
inp: gr.update(interactive=True, value=""),
|
116 |
+
btn: gr.update(interactive=True, value="π Get Answer"),
|
117 |
+
output: final_markdown,
|
118 |
+
lang_detected: gr.update(visible=True, value=f"π Language detected: **{detected_lang}**"),
|
119 |
+
copy_btn: gr.update(visible=show_copy),
|
120 |
}
|
121 |
+
|
122 |
+
# --- Event Listeners ---
|
123 |
+
event_chain = btn.click(
|
124 |
fn=start_processing,
|
125 |
inputs=[inp],
|
126 |
+
outputs=[inp, btn, output, lang_detected, copy_btn]
|
127 |
).then(
|
128 |
fn=call_modal_backend,
|
129 |
inputs=[inp],
|
|
|
131 |
).then(
|
132 |
fn=finish_processing,
|
133 |
inputs=[backend_result],
|
134 |
+
outputs=[inp, btn, output, lang_detected, copy_btn]
|
135 |
+
)
|
136 |
+
|
137 |
+
clear_btn.add([inp, output, lang_detected, copy_btn])
|
138 |
+
|
139 |
+
copy_btn.click(
|
140 |
+
fn=None,
|
141 |
+
inputs=[output],
|
142 |
+
outputs=None,
|
143 |
+
_js="""(text) => {
|
144 |
+
navigator.clipboard.writeText(text);
|
145 |
+
alert('Answer copied to clipboard!');
|
146 |
+
}"""
|
147 |
)
|
148 |
|
149 |
if __name__ == "__main__":
|
150 |
+
demo.launch()
|