import gradio as gr
import requests
import os
import uuid
# --- Configuration ---
MODAL_BACKEND_URL = os.getenv("MODAL_BACKEND_URL", "").strip()
# --- Dynamic Theme CSS ---
CUSTOM_CSS = """
@media (prefers-color-scheme: dark) {
body {
background-color: #1a1a2e;
color: #E0E0E0;
}
.gradio-container {
background-color: #21213b;
color: #E0E0E0;
}
#app-header h1 { color: #8aa8ff; }
#app-header p { color: #aeb8d8; }
#role-selector {
border: 1px solid #3a3a5e;
background-color: #2b2b4d;
}
.gradio-label, .gradio-radio input[type='radio'] + label {
color: #E0E0E0 !important;
}
.gradio-radio input[type='radio']:checked + label {
color: #8aa8ff !important;
}
#chatbot {
background-color: #2b2b4d;
border: 1px solid #3a3a5e;
}
.message {
padding: 1rem 1.25rem;
border-radius: 12px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.message.user {
background: linear-gradient(to right, #4a4a8a, #5c5cb8) !important;
color: #FFFFFF;
}
.message.bot {
background: #3a3a5e !important;
color: #E0E0E0;
border: 1px solid #4a4a8a;
}
.message.bot h3 { color: #8aa8ff; border-bottom: 2px solid #5c5cb8; }
.message.bot ul { padding-left: 20px; list-style-type: disc; }
.message.bot li { margin-bottom: 0.5rem; line-height: 1.6; }
.message.bot a { color: #8aa8ff; text-decoration: none; font-weight: 500; }
.message.bot a:hover { text-decoration: underline; color: #768fff; }
.message.bot strong { color: #8aa8ff; }
.message.bot em {
color: #b0b0d0;
background-color: #4a4a8a;
padding: 2px 6px;
border-radius: 4px;
}
#send-button {
background-color: #8aa8ff !important;
color: white !important;
font-weight: 600;
}
#send-button:hover {
background-color: #768fff !important;
}
.gradio-textbox textarea {
background-color: #2b2b4d !important;
color: #E0E0E0 !important;
border: 1px solid #3a3a5e !important;
}
.gradio-textbox textarea::placeholder {
color: #A0A0A0 !important;
}
footer { display: none !important; }
}
"""
# --- Gradio UI Definition ---
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), css=CUSTOM_CSS, title="MediAgent AI") as demo:
conversation_state = gr.State(value={"history": []})
session_id = gr.State(lambda: str(uuid.uuid4()))
gr.Markdown("""
""")
with gr.Row():
gr.Markdown("""
💡 Example Prompts:
""")
with gr.Group(elem_id="role-selector"):
user_role = gr.Radio(
["patient", "doctor"],
label="👤 Select Your Role",
value="patient",
info="The AI will adapt its tools and communication style based on your role.",
elem_id="user-role-radio",
show_label=True
)
gr.Markdown("ℹ️")
chatbot = gr.Chatbot(
label="Conversation",
elem_id="chatbot",
bubble_full_width=False,
avatar_images=(None, "https://i.imgur.com/9k2p4t7.png"),
show_copy_button=True
)
with gr.Row(elem_id="input-row"):
user_textbox = gr.Textbox(
scale=4,
show_label=False,
placeholder="Type your health-related question here...",
container=False,
)
submit_btn = gr.Button("Send", variant="primary", scale=1, min_width=150, elem_id="send-button")
loading_spinner = gr.Markdown("⏳ Thinking...")
with gr.Row():
clear_btn = gr.ClearButton(
value="🔄 Start New Conversation",
components=[chatbot, user_textbox, conversation_state]
)
def handle_user_turn(user_input, chat_history, user_role_selection, current_state, sid):
import time
from gradio import processing
# Show spinner
gr.update(elem_id="loading-spinner", value="⏳ Thinking...")
if not user_input.strip():
gr.update(elem_id="loading-spinner", value="")
yield {chatbot: chat_history, conversation_state: current_state, user_textbox: user_input}
return
user_message_html = f"{user_input}
"
chat_history.append((user_message_html, None))
yield {chatbot: chat_history, user_textbox: ""}
if not MODAL_BACKEND_URL:
error_message = "### ❗ Configuration Error\n**Reason**: `MODAL_BACKEND_URL` environment variable is not set."
error_message_html = f"{error_message}
"
chat_history[-1] = (user_message_html, error_message_html)
gr.update(elem_id="loading-spinner", value="")
yield {chatbot: chat_history}
return
headers = {"Content-Type": "application/json"}
payload = {"user_input": user_input, "session_id": sid, "role": user_role_selection}
try:
response = requests.post(MODAL_BACKEND_URL, headers=headers, json=payload, timeout=300)
response.raise_for_status()
backend_response = response.json()
bot_response_text = backend_response.get("final_markdown", "Sorry, I received an empty response.")
except requests.exceptions.RequestException as e:
bot_response_text = f"### ❗ Network Error\n**Reason**: Could not connect to the backend.\n**Details**: {str(e)}"
bot_message_html = f"{bot_response_text}
"
chat_history[-1] = (user_message_html, bot_message_html)
gr.update(elem_id="loading-spinner", value="")
yield {chatbot: chat_history, conversation_state: current_state}
submit_btn.click(
fn=handle_user_turn,
inputs=[user_textbox, chatbot, user_role, conversation_state, session_id],
outputs=[chatbot, conversation_state, user_textbox],
show_progress="hidden"
)
user_textbox.submit(
fn=handle_user_turn,
inputs=[user_textbox, chatbot, user_role, conversation_state, session_id],
outputs=[chatbot, conversation_state, user_textbox],
show_progress="hidden"
)
@clear_btn.click
def clear_chat():
return {"history": []}
if __name__ == "__main__":
print("Gradio App Starting...")
if not MODAL_BACKEND_URL:
print("\nWARNING: The `MODAL_BACKEND_URL` environment variable is not set.")
print("The application will run, but will show a configuration error.\n")
demo.queue()
demo.launch()