Upload 4 files
Browse files- README.md +60 -14
- app.py +107 -0
- modal_clinic_lookup.py +56 -0
- requirements.txt +7 -0
README.md
CHANGED
@@ -1,14 +1,60 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 🏥 HealthMate: AI Medical Triage Assistant
|
2 |
+
|
3 |
+
HealthMate is an AI-powered medical triage assistant that helps users assess their symptoms and find nearby medical facilities. It uses Claude AI for medical analysis and DuckDuckGo for clinic lookups.
|
4 |
+
|
5 |
+
## Features
|
6 |
+
|
7 |
+
- 🤖 AI-powered symptom analysis using Claude
|
8 |
+
- 🏥 Urgency classification (emergency/routine/home care)
|
9 |
+
- 📋 Possible medical conditions assessment
|
10 |
+
- 🔍 Nearby clinic recommendations
|
11 |
+
- 🌐 DuckDuckGo-powered clinic search
|
12 |
+
|
13 |
+
## Setup
|
14 |
+
|
15 |
+
1. Clone this repository:
|
16 |
+
```bash
|
17 |
+
git clone https://github.com/yourusername/health-mate.git
|
18 |
+
cd health-mate
|
19 |
+
```
|
20 |
+
|
21 |
+
2. Install dependencies:
|
22 |
+
```bash
|
23 |
+
pip install -r requirements.txt
|
24 |
+
```
|
25 |
+
|
26 |
+
3. Create a `.env` file with your Anthropic API key:
|
27 |
+
```
|
28 |
+
ANTHROPIC_API_KEY=your_api_key_here
|
29 |
+
```
|
30 |
+
|
31 |
+
4. Deploy the Modal backend:
|
32 |
+
```bash
|
33 |
+
modal deploy modal_clinic_lookup.py
|
34 |
+
```
|
35 |
+
|
36 |
+
5. Run the Gradio app:
|
37 |
+
```bash
|
38 |
+
python app.py
|
39 |
+
```
|
40 |
+
|
41 |
+
## Usage
|
42 |
+
|
43 |
+
1. Enter your symptoms in the text area
|
44 |
+
2. (Optional) Enter your city for nearby clinic recommendations
|
45 |
+
3. Click "Get Medical Guidance"
|
46 |
+
4. Review the AI-generated assessment and clinic recommendations
|
47 |
+
|
48 |
+
## Important Notes
|
49 |
+
|
50 |
+
- This is not a replacement for professional medical advice
|
51 |
+
- Always consult a healthcare provider for serious symptoms
|
52 |
+
- The clinic recommendations are based on web search results
|
53 |
+
|
54 |
+
## Demo
|
55 |
+
|
56 |
+
[Add your demo video link here]
|
57 |
+
|
58 |
+
## Tags
|
59 |
+
|
60 |
+
agent-demo-track
|
app.py
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import gradio as gr
|
3 |
+
from anthropic import Anthropic
|
4 |
+
import requests
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
|
7 |
+
# Load environment variables
|
8 |
+
load_dotenv()
|
9 |
+
|
10 |
+
# Initialize Anthropic client
|
11 |
+
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
|
12 |
+
|
13 |
+
MODAL_CLINIC_ENDPOINT = "https://aayushraj0324--healthmate-clinic-lookup-search-clinics.modal.run"
|
14 |
+
|
15 |
+
def classify_urgency(symptoms: str) -> str:
|
16 |
+
"""Classify the urgency level of the symptoms using Claude."""
|
17 |
+
prompt = f"""You are a medical triage assistant. Given this symptom description: {symptoms}, \nclassify it as: emergency / routine visit / home care. Explain briefly."""
|
18 |
+
message = client.messages.create(
|
19 |
+
model="claude-sonnet-4-20250514",
|
20 |
+
#model="claude-3-sonnet-20240229",
|
21 |
+
max_tokens=150,
|
22 |
+
temperature=0.1,
|
23 |
+
system="You are a medical triage assistant. Provide clear, concise classifications.",
|
24 |
+
messages=[{"role": "user", "content": prompt}]
|
25 |
+
)
|
26 |
+
return message.content[0].text
|
27 |
+
|
28 |
+
def get_possible_conditions(symptoms: str) -> str:
|
29 |
+
"""Get possible medical conditions based on symptoms using Claude."""
|
30 |
+
prompt = f"""List 2–4 possible medical conditions that match these symptoms: {symptoms}. \nKeep it non-technical and easy to understand."""
|
31 |
+
message = client.messages.create(
|
32 |
+
model="claude-sonnet-4-20250514",
|
33 |
+
#model="claude-3-sonnet-20240229",
|
34 |
+
max_tokens=200,
|
35 |
+
temperature=0.1,
|
36 |
+
system="You are a medical assistant. Provide clear, non-technical explanations of possible conditions.",
|
37 |
+
messages=[{"role": "user", "content": prompt}]
|
38 |
+
)
|
39 |
+
return message.content[0].text
|
40 |
+
|
41 |
+
def lookup_clinics(city: str) -> str:
|
42 |
+
try:
|
43 |
+
response = requests.get(MODAL_CLINIC_ENDPOINT, params={"city": city}, timeout=20)
|
44 |
+
response.raise_for_status()
|
45 |
+
clinics = response.json()
|
46 |
+
if clinics and isinstance(clinics, list) and "error" not in clinics[0]:
|
47 |
+
return "\n\n".join([
|
48 |
+
f"🏥 {clinic['name']}\n🔗 {clinic['link']}\n📝 {clinic['description']}"
|
49 |
+
for clinic in clinics
|
50 |
+
])
|
51 |
+
else:
|
52 |
+
return clinics[0].get("error", "No clinics found.")
|
53 |
+
except Exception as e:
|
54 |
+
return f"Error finding clinics: {str(e)}"
|
55 |
+
|
56 |
+
def process_input(symptoms: str, city: str) -> tuple:
|
57 |
+
"""Process the input and return all results."""
|
58 |
+
# Get urgency classification
|
59 |
+
urgency = classify_urgency(symptoms)
|
60 |
+
|
61 |
+
# Get possible conditions
|
62 |
+
conditions = get_possible_conditions(symptoms)
|
63 |
+
|
64 |
+
# Get nearby clinics if city is provided
|
65 |
+
if city:
|
66 |
+
clinic_text = lookup_clinics(city)
|
67 |
+
else:
|
68 |
+
clinic_text = "Please provide a city to find nearby clinics."
|
69 |
+
|
70 |
+
return urgency, conditions, clinic_text
|
71 |
+
|
72 |
+
# Create the Gradio interface
|
73 |
+
with gr.Blocks(css=".gradio-container {max-width: 800px; margin: auto;}") as demo:
|
74 |
+
gr.Markdown(
|
75 |
+
"""
|
76 |
+
# 🏥 HealthMate: AI Medical Triage Assistant
|
77 |
+
Enter your symptoms and optionally your city to get medical guidance and nearby clinic recommendations.
|
78 |
+
"""
|
79 |
+
)
|
80 |
+
|
81 |
+
with gr.Row():
|
82 |
+
with gr.Column():
|
83 |
+
symptoms = gr.Textbox(
|
84 |
+
label="Describe your symptoms",
|
85 |
+
placeholder="Example: I have a severe headache and fever for the past 2 days...",
|
86 |
+
lines=4
|
87 |
+
)
|
88 |
+
city = gr.Textbox(
|
89 |
+
label="Your city (optional)",
|
90 |
+
placeholder="Example: San Francisco"
|
91 |
+
)
|
92 |
+
submit_btn = gr.Button("Get Medical Guidance", variant="primary")
|
93 |
+
|
94 |
+
with gr.Row():
|
95 |
+
with gr.Column():
|
96 |
+
urgency = gr.Textbox(label="Urgency Classification")
|
97 |
+
conditions = gr.Textbox(label="Possible Conditions")
|
98 |
+
clinics = gr.Textbox(label="Nearby Clinics")
|
99 |
+
|
100 |
+
submit_btn.click(
|
101 |
+
fn=process_input,
|
102 |
+
inputs=[symptoms, city],
|
103 |
+
outputs=[urgency, conditions, clinics]
|
104 |
+
)
|
105 |
+
|
106 |
+
if __name__ == "__main__":
|
107 |
+
demo.launch(share=True, pwa=True)
|
modal_clinic_lookup.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import modal
|
2 |
+
from duckduckgo_search import DDGS
|
3 |
+
from bs4 import BeautifulSoup
|
4 |
+
import requests
|
5 |
+
|
6 |
+
# Create a Modal app
|
7 |
+
app = modal.App("healthmate-clinic-lookup")
|
8 |
+
|
9 |
+
# Define the base image with required dependencies
|
10 |
+
image = modal.Image.debian_slim().pip_install(
|
11 |
+
"duckduckgo_search",
|
12 |
+
"beautifulsoup4",
|
13 |
+
"requests",
|
14 |
+
"fastapi[standard]"
|
15 |
+
)
|
16 |
+
|
17 |
+
@app.function(image=image)
|
18 |
+
@modal.fastapi_endpoint()
|
19 |
+
def search_clinics(city: str) -> list:
|
20 |
+
"""
|
21 |
+
Search for clinics near the specified city using DuckDuckGo.
|
22 |
+
Returns a list of dictionaries containing clinic information.
|
23 |
+
"""
|
24 |
+
if not city:
|
25 |
+
return [{"error": "Please provide a city name"}]
|
26 |
+
|
27 |
+
try:
|
28 |
+
# Initialize DuckDuckGo search
|
29 |
+
with DDGS() as ddgs:
|
30 |
+
# Search for clinics
|
31 |
+
search_query = f"top medical clinics near {city}"
|
32 |
+
results = list(ddgs.text(search_query, max_results=3))
|
33 |
+
|
34 |
+
if not results:
|
35 |
+
return [{"error": f"No clinics found near {city}"}]
|
36 |
+
|
37 |
+
# Process and format results
|
38 |
+
clinics = []
|
39 |
+
for result in results:
|
40 |
+
clinic_info = {
|
41 |
+
"name": result.get("title", "Unknown Clinic"),
|
42 |
+
"link": result.get("link", "#"),
|
43 |
+
"description": result.get("body", "No description available")
|
44 |
+
}
|
45 |
+
clinics.append(clinic_info)
|
46 |
+
|
47 |
+
return clinics
|
48 |
+
|
49 |
+
except Exception as e:
|
50 |
+
return [{"error": f"Error searching for clinics: {str(e)}"}]
|
51 |
+
|
52 |
+
@app.local_entrypoint()
|
53 |
+
def main():
|
54 |
+
# Test the function locally
|
55 |
+
results = search_clinics.remote("San Francisco")
|
56 |
+
print(results)
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
modal>=0.57.0
|
2 |
+
gradio>=4.19.2
|
3 |
+
anthropic>=0.18.1
|
4 |
+
duckduckgo_search>=4.4.3
|
5 |
+
beautifulsoup4>=4.12.3
|
6 |
+
requests>=2.31.0
|
7 |
+
python-dotenv>=1.0.1
|