Spaces:
Sleeping
Sleeping
multimodal ai chatbot
Browse files- .gitignore +2 -0
- app.py +83 -0
- app_1.py +48 -0
- modules/__init__.py +0 -0
- modules/__pycache__/__init__.cpython-312.pyc +0 -0
- modules/__pycache__/ai_response.cpython-312.pyc +0 -0
- modules/__pycache__/audio_processing.cpython-312.pyc +0 -0
- modules/__pycache__/email_reminder.cpython-312.pyc +0 -0
- modules/__pycache__/gemini_response.cpython-312.pyc +0 -0
- modules/__pycache__/image_processing.cpython-312.pyc +0 -0
- modules/__pycache__/reminder_scheduler.cpython-312.pyc +0 -0
- modules/__pycache__/text_processing.cpython-312.pyc +0 -0
- modules/ai_response.py +36 -0
- modules/audio_processing.py +16 -0
- modules/email_reminder.py +38 -0
- modules/gemini_response.py +47 -0
- modules/image_processing.py +5 -0
- modules/reminder_scheduler.py +33 -0
- modules/text_processing.py +7 -0
- prompts/system_prompt.txt +25 -0
- requirements.txt +6 -0
- test.py +3 -0
.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
multiaichat
|
2 |
+
.env
|
app.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from datetime import datetime
|
3 |
+
from modules.audio_processing import transcribe_audio
|
4 |
+
from modules.image_processing import encode_image_to_base64
|
5 |
+
from modules.text_processing import get_user_query
|
6 |
+
from modules.gemini_response import generate_medical_response
|
7 |
+
from modules.reminder_scheduler import add_reminder
|
8 |
+
|
9 |
+
# Chatbot logic
|
10 |
+
def chatbot_fn(user_message, history, audio_file=None, image_file=None, tone="Friendly and Simple"):
|
11 |
+
transcribed_text = None
|
12 |
+
if audio_file:
|
13 |
+
transcribed_text = transcribe_audio(audio_file)
|
14 |
+
|
15 |
+
query = get_user_query(user_message, transcribed_text)
|
16 |
+
image_base64 = encode_image_to_base64(image_file) if image_file else None
|
17 |
+
|
18 |
+
tone_instruction = f"(Respond in a '{tone}' tone)\n"
|
19 |
+
full_query = tone_instruction + query
|
20 |
+
|
21 |
+
messages = history or []
|
22 |
+
messages.append({"role": "user", "content": full_query})
|
23 |
+
|
24 |
+
response = generate_medical_response(full_query, image_base64=image_base64, history=messages)
|
25 |
+
|
26 |
+
return response
|
27 |
+
|
28 |
+
chatbot_ui = gr.ChatInterface(
|
29 |
+
fn=chatbot_fn,
|
30 |
+
additional_inputs=[
|
31 |
+
gr.Audio(sources=["microphone"], type="filepath", label="🎤 Speak (optional)"),
|
32 |
+
gr.Image(type="filepath", label="🖼️ Upload Medical Image (optional)"),
|
33 |
+
gr.Dropdown(["Friendly and Simple", "Detailed and Clinical", "Explain Like I'm 5"],
|
34 |
+
label="🗣️ Response Tone", value="Friendly and Simple")
|
35 |
+
],
|
36 |
+
title="🩺 Medical AI Chatbot",
|
37 |
+
description="Ask medical questions using text, voice, or images. The AI will respond like a helpful doctor.",
|
38 |
+
theme="soft",
|
39 |
+
type="messages"
|
40 |
+
)
|
41 |
+
|
42 |
+
# Reminder logic
|
43 |
+
def submit_reminder(subject, description, time_str, email, confirm):
|
44 |
+
if not confirm:
|
45 |
+
return "❌ Please check the confirmation box before submitting."
|
46 |
+
if not subject or not time_str or not email:
|
47 |
+
return "❌ Please fill in Subject, Time, and Email."
|
48 |
+
|
49 |
+
try:
|
50 |
+
parsed_time = datetime.strptime(time_str.strip(), "%I:%M %p")
|
51 |
+
#print(f"⏰ Parsed time input: {parsed_time}")
|
52 |
+
reminder_time = datetime.combine(datetime.today(), parsed_time.time())
|
53 |
+
except ValueError:
|
54 |
+
return "❌ Use HH:MM AM/PM format (e.g. 09:00 PM)"
|
55 |
+
|
56 |
+
full_description = f"{subject} — {description}" if description else subject
|
57 |
+
add_reminder(email=email, med=full_description, time_obj=reminder_time)
|
58 |
+
|
59 |
+
return f"✅ Reminder set for '{subject}' at {reminder_time.strftime('%I:%M %p')} to {email}."
|
60 |
+
|
61 |
+
with gr.Blocks() as reminder_ui:
|
62 |
+
gr.Markdown("### ⏰ Set Medication Reminder")
|
63 |
+
|
64 |
+
with gr.Row():
|
65 |
+
subject_input = gr.Textbox(label="🧪 Subject", placeholder="Insulin Injection")
|
66 |
+
time_input = gr.Textbox(label="🕒 Time (e.g. 09:00 PM)", placeholder="HH:MM AM/PM")
|
67 |
+
|
68 |
+
with gr.Row():
|
69 |
+
email_input = gr.Textbox(label="📧 Email", placeholder="[email protected]")
|
70 |
+
confirm_checkbox = gr.Checkbox(label="✅ Confirm to receive email reminder")
|
71 |
+
|
72 |
+
description_input = gr.Textbox(label="📝 Description (optional)", placeholder="Take after dinner")
|
73 |
+
|
74 |
+
send_btn = gr.Button("📩 Set Reminder")
|
75 |
+
output_text = gr.Textbox(label="Status")
|
76 |
+
|
77 |
+
send_btn.click(
|
78 |
+
fn=submit_reminder,
|
79 |
+
inputs=[subject_input, description_input, time_input, email_input, confirm_checkbox],
|
80 |
+
outputs=output_text
|
81 |
+
)
|
82 |
+
|
83 |
+
gr.TabbedInterface([chatbot_ui, reminder_ui], ["💬 Medical Chatbot", "⏰ Medication Reminder"]).launch(debug=True)
|
app_1.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from modules.audio_processing import transcribe_audio
|
3 |
+
from modules.image_processing import encode_image_to_base64
|
4 |
+
from modules.text_processing import get_user_query
|
5 |
+
#from modules.ai_response import generate_medical_response
|
6 |
+
from modules.gemini_response import generate_medical_response
|
7 |
+
|
8 |
+
def process_inputs(text_input, audio_file, image_file, history):
|
9 |
+
transcribed_text = None
|
10 |
+
if audio_file:
|
11 |
+
transcribed_text = transcribe_audio(audio_file)
|
12 |
+
|
13 |
+
query = get_user_query(text_input, transcribed_text)
|
14 |
+
image_base64 = encode_image_to_base64(image_file) if image_file else None
|
15 |
+
|
16 |
+
if history is None:
|
17 |
+
history = []
|
18 |
+
|
19 |
+
# Add user message to history
|
20 |
+
history.append({"role": "user", "content": query})
|
21 |
+
|
22 |
+
# Get doctor response
|
23 |
+
doctor_response = generate_medical_response(query, image_base64=image_base64, history=history)
|
24 |
+
|
25 |
+
# Add doctor response to history
|
26 |
+
history.append({"role": "assistant", "content": doctor_response})
|
27 |
+
|
28 |
+
return transcribed_text or "", doctor_response, history
|
29 |
+
|
30 |
+
iface = gr.Interface(
|
31 |
+
fn=process_inputs,
|
32 |
+
inputs=[
|
33 |
+
gr.Textbox(label="Type your medical question (optional)"),
|
34 |
+
gr.Audio(sources=["microphone"], type="filepath", label="Speak your question (optional)"),
|
35 |
+
gr.Image(type="filepath", label="Upload a related medical image (optional)"),
|
36 |
+
gr.State([]) # <-- Chat history
|
37 |
+
],
|
38 |
+
outputs=[
|
39 |
+
gr.Textbox(label="Transcribed Audio"),
|
40 |
+
gr.Textbox(label="Doctor's Detailed Response"),
|
41 |
+
gr.State() # <-- Return updated history
|
42 |
+
],
|
43 |
+
title="Multimodal Medical AI Chatbot",
|
44 |
+
description="Ask about diseases or chronic illnesses using text, audio, or images."
|
45 |
+
)
|
46 |
+
|
47 |
+
if __name__ == "__main__":
|
48 |
+
iface.launch(debug=True)
|
modules/__init__.py
ADDED
File without changes
|
modules/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (159 Bytes). View file
|
|
modules/__pycache__/ai_response.cpython-312.pyc
ADDED
Binary file (1.82 kB). View file
|
|
modules/__pycache__/audio_processing.cpython-312.pyc
ADDED
Binary file (986 Bytes). View file
|
|
modules/__pycache__/email_reminder.cpython-312.pyc
ADDED
Binary file (1.74 kB). View file
|
|
modules/__pycache__/gemini_response.cpython-312.pyc
ADDED
Binary file (2.69 kB). View file
|
|
modules/__pycache__/image_processing.cpython-312.pyc
ADDED
Binary file (616 Bytes). View file
|
|
modules/__pycache__/reminder_scheduler.cpython-312.pyc
ADDED
Binary file (1.94 kB). View file
|
|
modules/__pycache__/text_processing.cpython-312.pyc
ADDED
Binary file (378 Bytes). View file
|
|
modules/ai_response.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
from groq import Groq
|
4 |
+
|
5 |
+
load_dotenv()
|
6 |
+
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
7 |
+
|
8 |
+
def generate_medical_response(query, image_base64=None, history=None, model="meta-llama/llama-4-scout-17b-16e-instruct"):
|
9 |
+
client = Groq(api_key=GROQ_API_KEY)
|
10 |
+
|
11 |
+
prompt_path = os.path.join(os.path.dirname(__file__), "../prompts/system_prompt.txt")
|
12 |
+
with open(prompt_path, "r") as f:
|
13 |
+
system_prompt = f.read().strip()
|
14 |
+
|
15 |
+
messages = [{"role": "system", "content": system_prompt}]
|
16 |
+
|
17 |
+
# Add past conversation turns (if any)
|
18 |
+
if history:
|
19 |
+
messages.extend(history[-5:]) # Use last 5 exchanges max
|
20 |
+
|
21 |
+
# Append current query + image
|
22 |
+
user_content = [{"type": "text", "text": query}]
|
23 |
+
if image_base64:
|
24 |
+
user_content.append({
|
25 |
+
"type": "image_url",
|
26 |
+
"image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}
|
27 |
+
})
|
28 |
+
|
29 |
+
messages.append({"role": "user", "content": user_content})
|
30 |
+
|
31 |
+
response = client.chat.completions.create(
|
32 |
+
model=model,
|
33 |
+
messages=messages
|
34 |
+
)
|
35 |
+
|
36 |
+
return response.choices[0].message.content
|
modules/audio_processing.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from groq import Groq
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
|
5 |
+
load_dotenv()
|
6 |
+
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
7 |
+
|
8 |
+
def transcribe_audio(audio_filepath, stt_model='whisper-large-v3'):
|
9 |
+
client = Groq(api_key=GROQ_API_KEY)
|
10 |
+
with open(audio_filepath, "rb") as audio_file:
|
11 |
+
transcription = client.audio.transcriptions.create(
|
12 |
+
model=stt_model,
|
13 |
+
file=audio_file,
|
14 |
+
language="en"
|
15 |
+
)
|
16 |
+
return transcription.text
|
modules/email_reminder.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import smtplib
|
2 |
+
from email.mime.text import MIMEText
|
3 |
+
from email.mime.multipart import MIMEMultipart
|
4 |
+
|
5 |
+
import os
|
6 |
+
from dotenv import load_dotenv
|
7 |
+
load_dotenv()
|
8 |
+
|
9 |
+
EMAIL = os.environ["EMAIL"]
|
10 |
+
PASSWORD = os.environ["PASSWORD"]
|
11 |
+
|
12 |
+
def send_reminder_email(to_email, med_name, med_time):
|
13 |
+
try:
|
14 |
+
msg = MIMEMultipart()
|
15 |
+
msg['From'] = EMAIL
|
16 |
+
msg['To'] = to_email
|
17 |
+
msg['Subject'] = f"⏰ Medication Reminder: {med_name}"
|
18 |
+
|
19 |
+
body = f"""Hello,
|
20 |
+
|
21 |
+
This is your reminder to take:
|
22 |
+
|
23 |
+
🧪 {med_name}
|
24 |
+
🕒 At: {med_time}
|
25 |
+
|
26 |
+
Take care!
|
27 |
+
- Your Medical AI Chatbot
|
28 |
+
"""
|
29 |
+
msg.attach(MIMEText(body, 'plain'))
|
30 |
+
|
31 |
+
server = smtplib.SMTP("smtp.gmail.com", 587)
|
32 |
+
server.starttls()
|
33 |
+
server.login(EMAIL, PASSWORD)
|
34 |
+
server.sendmail(EMAIL, to_email, msg.as_string())
|
35 |
+
server.quit()
|
36 |
+
print(f"✅ Email sent to {to_email}")
|
37 |
+
except Exception as e:
|
38 |
+
print(f"❌ Failed to send email to {to_email}: {e}")
|
modules/gemini_response.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import base64
|
3 |
+
import io
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
import google.generativeai as genai
|
6 |
+
from PIL import Image
|
7 |
+
|
8 |
+
load_dotenv()
|
9 |
+
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
10 |
+
|
11 |
+
genai.configure(api_key=GEMINI_API_KEY)
|
12 |
+
|
13 |
+
def load_system_prompt():
|
14 |
+
prompt_path = os.path.join(os.path.dirname(__file__), "../prompts/system_prompt.txt")
|
15 |
+
with open(prompt_path, "r", encoding="utf-8") as f:
|
16 |
+
return f.read().strip()
|
17 |
+
|
18 |
+
def generate_medical_response(query, image_base64=None, history=None, model_name="models/gemini-1.5-pro"):
|
19 |
+
model = genai.GenerativeModel(model_name)
|
20 |
+
system_prompt = load_system_prompt()
|
21 |
+
|
22 |
+
# Build chat context from history
|
23 |
+
messages = []
|
24 |
+
if history:
|
25 |
+
for msg in history[-5:]:
|
26 |
+
role = msg["role"].capitalize()
|
27 |
+
content = msg["content"]
|
28 |
+
if isinstance(content, list): # handle multimodal input
|
29 |
+
text = next((part["text"] for part in content if "text" in part), "")
|
30 |
+
else:
|
31 |
+
text = content
|
32 |
+
messages.append(f"{role}: {text}")
|
33 |
+
|
34 |
+
# Add current user input
|
35 |
+
messages.append(f"User: {query}")
|
36 |
+
full_prompt = f"{system_prompt}\n\n" + "\n".join(messages)
|
37 |
+
|
38 |
+
# If image is included, decode it and use vision model
|
39 |
+
if image_base64:
|
40 |
+
image_data = base64.b64decode(image_base64)
|
41 |
+
image = Image.open(io.BytesIO(image_data))
|
42 |
+
|
43 |
+
response = model.generate_content([full_prompt, image])
|
44 |
+
else:
|
45 |
+
response = model.generate_content(full_prompt)
|
46 |
+
|
47 |
+
return response.text.strip()
|
modules/image_processing.py
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import base64
|
2 |
+
|
3 |
+
def encode_image_to_base64(image_path):
|
4 |
+
with open(image_path, "rb") as image_file:
|
5 |
+
return base64.b64encode(image_file.read()).decode('utf-8')
|
modules/reminder_scheduler.py
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from apscheduler.schedulers.background import BackgroundScheduler
|
2 |
+
from datetime import datetime, timedelta
|
3 |
+
from modules.email_reminder import send_reminder_email
|
4 |
+
|
5 |
+
# In-memory list to store reminders
|
6 |
+
reminders = []
|
7 |
+
|
8 |
+
# Background scheduler setup
|
9 |
+
scheduler = BackgroundScheduler()
|
10 |
+
|
11 |
+
def check_reminders():
|
12 |
+
now = datetime.now()
|
13 |
+
|
14 |
+
for r in reminders[:]:
|
15 |
+
reminder_time = r["time"]
|
16 |
+
reminder_trigger_time = reminder_time - timedelta(minutes=15)
|
17 |
+
|
18 |
+
time_diff_seconds = (now - reminder_trigger_time).total_seconds()
|
19 |
+
print(f"⏱️ Now: {now}, Trigger At: {reminder_trigger_time}, Diff: {time_diff_seconds} seconds")
|
20 |
+
|
21 |
+
if abs(time_diff_seconds) <= 60:
|
22 |
+
print(f"📬 Sending email to {r['email']} at {now.strftime('%I:%M:%S %p')}")
|
23 |
+
send_reminder_email(r["email"], r["med"], reminder_time.strftime("%I:%M %p"))
|
24 |
+
reminders.remove(r)
|
25 |
+
|
26 |
+
# Schedule the check to run every minute
|
27 |
+
scheduler.add_job(check_reminders, 'interval', minutes=1)
|
28 |
+
scheduler.start()
|
29 |
+
|
30 |
+
# Function to add a reminder
|
31 |
+
def add_reminder(email, med, time_obj):
|
32 |
+
print(f"📝 Reminder added: {med} at {time_obj.strftime('%I:%M %p')} for {email}")
|
33 |
+
reminders.append({"email": email, "med": med, "time": time_obj})
|
modules/text_processing.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def get_user_query(text_input, transcribed_audio):
|
2 |
+
if text_input:
|
3 |
+
return text_input
|
4 |
+
elif transcribed_audio:
|
5 |
+
return transcribed_audio
|
6 |
+
else:
|
7 |
+
return "Can you help me understand what's wrong?"
|
prompts/system_prompt.txt
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
You are a compassionate and knowledgeable medical doctor.
|
2 |
+
|
3 |
+
Your job is to understand the patient's concern, whether it comes from typed text, voice transcription, or a medical image, and provide clear, caring, and medically accurate advice.
|
4 |
+
|
5 |
+
🩺 Use the following style when responding:
|
6 |
+
- Speak directly to the patient, as if you’re in a warm one-on-one conversation
|
7 |
+
- Adapt your tone:
|
8 |
+
- For children or parents: gentle and reassuring
|
9 |
+
- For elderly users: slow, clear, and respectful
|
10 |
+
- For adults: professional yet empathetic
|
11 |
+
- Avoid jargon or explain it simply if used
|
12 |
+
- Be thorough but not overwhelming
|
13 |
+
- Mention possible causes, when to see a doctor, and home care (if safe)
|
14 |
+
|
15 |
+
📸 If an image is provided:
|
16 |
+
- Observe and describe any visible medical signs
|
17 |
+
- Connect it to the patient's question
|
18 |
+
- Provide a reasonable explanation based on the image
|
19 |
+
|
20 |
+
❗ Do not:
|
21 |
+
- Mention you are an AI or language model
|
22 |
+
- Use bullet points or numbered lists
|
23 |
+
- Give a diagnosis — instead, give advice and next steps
|
24 |
+
|
25 |
+
👩⚕️ Speak like a caring doctor who wants the patient to feel understood and supported.
|
requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
groq
|
2 |
+
python-dotenv
|
3 |
+
gradio
|
4 |
+
google-generativeai
|
5 |
+
pillow
|
6 |
+
apscheduler
|
test.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
from modules.email_reminder import send_reminder_email
|
2 |
+
|
3 |
+
send_reminder_email("[email protected]", "Test Reminder", "02:30 PM")
|