Skip to content

Commit cf2aefe

Browse files
committed
update multi-modal-medical agent
1 parent 6457d91 commit cf2aefe

File tree

7 files changed

+372
-0
lines changed

7 files changed

+372
-0
lines changed

multi_modal_medical_agent/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Pynecone, Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# AI Medical Agent using Gemini 2.0 Flash
2+
3+
The **AI Medical Agent** leverages **Reflex**, **Agno**, and **Gemini 2.0 Flash** to provide detailed medical analysis on the provided images. It enables users to obtain comprehensive insights into medical conditions by analyzing images and simultaneously searching the web for additional information. The app generates detailed reports that assist in understanding possible diagnoses, conditions, and medical recommendations.
4+
5+
## Note
6+
7+
**Educational Purpose Only:** This project is intended for educational purposes only to demonstrate the power of AI in medical image analysis. It is **not** a substitute for professional medical advice, diagnosis, or treatment.
8+
9+
---
10+
11+
## Features
12+
13+
- **Medical Image Analysis:** Analyze images to detect potential medical conditions and provide insights based on AI-powered evaluation.
14+
- **Symptom & Condition Insights:** Extract information related to possible conditions based on image analysis and web data retrieval.
15+
- **Gemini 2.0 Flash Integration:** Utilizes Google's Gemini 2.0 Flash for fast, accurate, and dynamic responses.
16+
- **Web Search & Data Aggregation:** Cross-checks image analysis results with trusted medical sources for enhanced accuracy.
17+
- **Detailed Medical Reports:** Generates in-depth analysis, including professional insights, condition explanations, and potential next steps.
18+
19+
---
20+
21+
## Getting Started
22+
23+
### 1. Clone the Repository
24+
Clone the GitHub repository to your local machine:
25+
```bash
26+
git clone https://github.com/reflex-dev/reflex-llm-examples.git
27+
cd reflex-llm-examples/multi_modal_medical_agent
28+
```
29+
30+
### 2. Install Dependencies
31+
Install the required dependencies:
32+
```bash
33+
pip install -r requirements.txt
34+
```
35+
36+
### 3. Set Up Gemini API Key
37+
To use the Gemini 2.0 Flash model, you need a **Google API Key**. Follow these steps:
38+
Go to [Google AI Studio](https://aistudio.google.com/apikey), get your API Key, and set it as an environment variable:
39+
```bash
40+
export GOOGLE_API_KEY="your-api-key-here"
41+
```
42+
43+
### 4. Run the Reflex App
44+
Start the application:
45+
```bash
46+
reflex run
47+
```
48+
49+
---
50+
51+
## How It Works
52+
53+
1. **Medical Image Upload:** Upload an image for analysis.
54+
2. **Gemini 2.0 Flash Processing:** The app analyzes the image and cross-references web data to provide a detailed report.
55+
3. **Condition Insights:** The report includes potential conditions, symptom explanations, and possible next steps.
56+
4. **Trusted Sources:** The app retrieves data from verified medical sources to enhance accuracy.
57+
58+
---
59+
60+
## Why AI Medical Agent?
61+
62+
- **AI-Powered Medical Insights:** Provides advanced image analysis with AI to assist in medical understanding.
63+
- **Real-Time Data Access:** Retrieves relevant medical information from trusted sources for enhanced accuracy.
64+
- **User-Friendly:** Simple and intuitive experience, enabling easy image uploads and report generation.
65+
66+
---
67+
68+
## Contributing
69+
70+
We welcome contributions! Feel free to open issues or submit pull requests to improve the app.
71+
72+
---
73+

multi_modal_medical_agent/agent/__init__.py

Whitespace-only changes.
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
2+
import reflex as rx
3+
from typing import Optional
4+
import asyncio
5+
from phi.agent import Agent
6+
from phi.model.google import Gemini
7+
from phi.tools.duckduckgo import DuckDuckGo
8+
import os
9+
from PIL import Image
10+
import time
11+
import asyncio
12+
13+
# Set Google API Key from environment
14+
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
15+
16+
17+
class MedicalState(rx.State):
18+
"""State for the medical imaging analysis application."""
19+
processing: bool = False
20+
upload_status: str = ""
21+
analysis_result: str = ""
22+
image_filename: str = ""
23+
_temp_image_path: str = ""
24+
25+
query = """
26+
You are a highly skilled medical imaging expert with extensive knowledge in radiology and diagnostic imaging. Analyze the patient's medical image and structure your response as follows:
27+
28+
### 1. Image Type & Region
29+
- Specify imaging modality (X-ray/MRI/CT/Ultrasound/etc.)
30+
- Identify the patient's anatomical region and positioning
31+
- Comment on image quality and technical adequacy
32+
33+
### 2. Key Findings
34+
- List primary observations systematically
35+
- Note any abnormalities in the patient's imaging with precise descriptions
36+
- Include measurements and densities where relevant
37+
- Describe location, size, shape, and characteristics
38+
- Rate severity: Normal/Mild/Moderate/Severe
39+
40+
### 3. Diagnostic Assessment
41+
- Provide primary diagnosis with confidence level
42+
- List differential diagnoses in order of likelihood
43+
- Support each diagnosis with observed evidence from the patient's imaging
44+
- Note any critical or urgent findings
45+
46+
### 4. Patient-Friendly Explanation
47+
- Explain the findings in simple, clear language that the patient can understand
48+
- Avoid medical jargon or provide clear definitions
49+
- Include visual analogies if helpful
50+
- Address common patient concerns related to these findings
51+
52+
### 5. Research Context
53+
IMPORTANT: Use the DuckDuckGo search tool to:
54+
- Find recent medical literature about similar cases
55+
- Search for standard treatment protocols
56+
- Provide a list of relevant medical links of them too
57+
- Research any relevant technological advances
58+
- Include 2-3 key references to support your analysis
59+
60+
Format your response using clear markdown headers and bullet points. Be concise yet thorough.
61+
"""
62+
63+
@rx.event
64+
async def handle_upload(self, files: list[rx.UploadFile]):
65+
"""Handle medical image upload."""
66+
if not files:
67+
return
68+
69+
try:
70+
file = files[0]
71+
upload_data = await file.read()
72+
73+
filename = file.filename
74+
outfile = rx.get_upload_dir() / filename
75+
76+
# Save the file
77+
with outfile.open("wb") as file_object:
78+
file_object.write(upload_data)
79+
80+
self.image_filename = filename
81+
self._temp_image_path = str(outfile)
82+
self.upload_status = "Image uploaded successfully!"
83+
84+
except Exception as e:
85+
self.upload_status = f"Error uploading image: {str(e)}"
86+
87+
@rx.var
88+
def medical_agent(self):
89+
if GOOGLE_API_KEY:
90+
return Agent(
91+
model=Gemini(
92+
api_key=GOOGLE_API_KEY,
93+
id="gemini-2.0-flash-exp"
94+
),
95+
tools=[DuckDuckGo()],
96+
markdown=True
97+
)
98+
return None
99+
100+
101+
@rx.event(background=True)
102+
async def analyze_image(self):
103+
"""Process image using medical AI agent."""
104+
if not self.medical_agent:
105+
self.analysis_result = "API Key not configured in environment"
106+
return
107+
108+
async with self:
109+
self.processing = True
110+
self.analysis_result = ""
111+
yield
112+
await asyncio.sleep(1)
113+
114+
try:
115+
# Process image
116+
with Image.open(self._temp_image_path) as img:
117+
width, height = img.size
118+
aspect_ratio = width / height
119+
new_width = 500
120+
new_height = int(new_width / aspect_ratio)
121+
resized_img = img.resize((new_width, new_height))
122+
resized_img.save(self._temp_image_path)
123+
124+
# Run analysis
125+
result = self.medical_agent.run(self.query, images=[self._temp_image_path])
126+
127+
async with self:
128+
self.analysis_result = result.content
129+
self.processing = False
130+
131+
except Exception as e:
132+
async with self:
133+
self.processing = False
134+
self.analysis_result = f"An error occurred: {str(e)}"
135+
finally:
136+
if os.path.exists(self._temp_image_path):
137+
os.remove(self._temp_image_path)
138+
139+
140+
def medical_header() -> rx.Component:
141+
return rx.el.div(
142+
rx.el.div(
143+
rx.el.h1(
144+
"Medical Imaging Analysis Agent 🏥",
145+
class_name="text-3xl md:text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-cyan-600",
146+
),
147+
rx.el.p(
148+
"Advanced AI-powered medical image analysis using Gemini 2.0 Flash",
149+
class_name="text-gray-600 mt-2 text-lg",
150+
),
151+
class_name="text-center space-y-2",
152+
),
153+
class_name="w-full py-8 bg-gradient-to-r from-blue-50 to-cyan-50 border-b border-blue-100",
154+
)
155+
156+
157+
def upload_section() -> rx.Component:
158+
return rx.el.div(
159+
rx.el.div(
160+
rx.upload(
161+
rx.el.div(
162+
rx.el.div(
163+
rx.el.i(class_name="fas fa-upload text-3xl text-blue-500 mb-4"),
164+
rx.el.p(
165+
"Drop your medical image here",
166+
class_name="text-lg font-semibold text-gray-700 mb-2"
167+
),
168+
rx.el.p(
169+
"or click to browse",
170+
class_name="text-sm text-gray-500"
171+
),
172+
rx.el.p(
173+
"Supported formats: JPG, PNG",
174+
class_name="text-xs text-gray-400 mt-2"
175+
),
176+
class_name="text-center",
177+
),
178+
class_name="p-8 border-2 border-dashed border-blue-200 rounded-xl hover:border-blue-400 transition-colors duration-300",
179+
),
180+
max_files=1,
181+
accept={".jpg", ".jpeg", ".png"},
182+
id="medical_upload",
183+
class_name="cursor-pointer",
184+
),
185+
rx.cond(
186+
MedicalState.upload_status != "",
187+
rx.el.p(
188+
MedicalState.upload_status,
189+
class_name="mt-4 text-sm text-center text-blue-600",
190+
),
191+
),
192+
rx.el.button(
193+
"Upload Image",
194+
on_click=lambda: MedicalState.handle_upload(rx.upload_files(upload_id="medical_upload")),
195+
class_name="mt-4 w-full py-2 px-4 bg-gradient-to-r from-blue-500 to-cyan-500 text-white rounded-lg hover:from-blue-600 hover:to-cyan-600 transition-all duration-300 shadow-md hover:shadow-lg",
196+
),
197+
class_name="w-full max-w-md mx-auto",
198+
),
199+
class_name="w-full bg-white p-6 rounded-xl shadow-md",
200+
)
201+
202+
203+
def analysis_section() -> rx.Component:
204+
return rx.el.div(
205+
rx.cond(
206+
MedicalState.image_filename != "",
207+
rx.el.div(
208+
rx.el.div(
209+
rx.el.img(
210+
src=rx.get_upload_url(MedicalState.image_filename),
211+
class_name="mx-auto my-4 max-w-2xl h-auto rounded-lg shadow-lg border border-gray-200",
212+
),
213+
class_name="mb-6",
214+
),
215+
rx.el.div(
216+
rx.cond(
217+
MedicalState.processing,
218+
rx.el.div(
219+
rx.el.div(
220+
class_name="w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"
221+
),
222+
rx.el.p(
223+
"Analyzing image...",
224+
class_name="mt-2 text-sm text-gray-600"
225+
),
226+
class_name="flex flex-col items-center justify-center p-4",
227+
),
228+
rx.el.button(
229+
"Analyze Image",
230+
on_click=MedicalState.analyze_image,
231+
diabled=MedicalState.processing,
232+
class_name="w-full py-2 px-4 bg-gradient-to-r from-blue-500 to-cyan-500 text-white rounded-lg hover:from-blue-600 hover:to-cyan-600 transition-all duration-300 shadow-md hover:shadow-lg",
233+
),
234+
),
235+
),
236+
rx.cond(
237+
MedicalState.analysis_result != "",
238+
rx.el.div(
239+
rx.markdown(
240+
MedicalState.analysis_result,
241+
class_name="mt-4 p-4 bg-blue-50 text-blue-700 rounded-lg border border-blue-100",
242+
),
243+
),
244+
),
245+
class_name="space-y-4",
246+
),
247+
),
248+
class_name="w-full bg-white p-6 rounded-xl shadow-md mt-6",
249+
)
250+
251+
252+
def index() -> rx.Component:
253+
return rx.el.div(
254+
medical_header(),
255+
rx.el.div(
256+
rx.el.div(
257+
upload_section(),
258+
analysis_section(),
259+
class_name="max-w-4xl mx-auto px-4 space-y-6"
260+
),
261+
class_name="py-8 bg-gray-50 min-h-screen"
262+
),
263+
class_name="min-h-screen bg-gray-50"
264+
)
265+
266+
267+
app = rx.App()
268+
app.add_page(index)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
agno
2+
google-generativeai
3+
reflex
4+
duckduckgo-search
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import reflex as rx
2+
3+
4+
config = rx.Config(
5+
app_name="agent",
6+
)
40.1 KB
Loading

0 commit comments

Comments
 (0)