Spaces:
Running
Running
UI update
Browse files
app.py
CHANGED
@@ -35,14 +35,7 @@ def handle_conversation_turn(user_input: str, user_image: Image.Image, history:
|
|
35 |
|
36 |
try:
|
37 |
system_prompt = (
|
38 |
-
"You are an expert, empathetic AI medical assistant conducting a virtual consultation. "
|
39 |
-
"Your primary goal is to ask clarifying questions to understand the user's symptoms thoroughly. "
|
40 |
-
"Do NOT provide a diagnosis or a list of possibilities right away. Ask only one or two focused questions per turn. "
|
41 |
-
"If the user provides an image, your first step is to analyze it from an expert perspective. Briefly describe the key findings from the image. "
|
42 |
-
"Then, use this analysis to ask relevant follow-up questions about the user's symptoms or medical history to better understand the context. "
|
43 |
-
"For example, after seeing a rash, you might say, 'I see a reddish rash with well-defined borders on the forearm. To help me understand more, could you tell me when you first noticed this? Is it itchy, painful, or does it have any other sensation?'"
|
44 |
-
"After several turns of asking questions, when you feel you have gathered enough information, you must FIRST state that you are ready to provide a summary. "
|
45 |
-
"THEN, in the SAME response, provide a list of possible conditions, your reasoning, and a clear, actionable next-steps plan."
|
46 |
)
|
47 |
|
48 |
generation_args = {"max_new_tokens": 1024, "do_sample": True, "temperature": 0.7}
|
@@ -80,13 +73,14 @@ def handle_conversation_turn(user_input: str, user_image: Image.Image, history:
|
|
80 |
print(f"An exception occurred during conversation turn: {type(e).__name__}: {e}")
|
81 |
yield history, history
|
82 |
|
83 |
-
# --- UI MODIFICATION: Professional CSS for a
|
84 |
css = """
|
85 |
/* Main App Styling */
|
86 |
-
body, .gradio-container { background-color: #f9fafb
|
87 |
-
#chat-container { flex-grow: 1; overflow-y: auto; padding-bottom:
|
88 |
/* Chat Bubble Styling */
|
89 |
-
.message-bubble {
|
|
|
90 |
/* Sticky Footer Input Bar */
|
91 |
#footer-container {
|
92 |
position: fixed !important; bottom: 0; left: 0; width: 100%;
|
@@ -96,82 +90,68 @@ body, .gradio-container { background-color: #f9fafb; font-family: 'Inter', sans-
|
|
96 |
}
|
97 |
/* Text Input Box Styling */
|
98 |
#user-textbox textarea {
|
99 |
-
background-color: #
|
100 |
border: 1px solid #d1d5db !important;
|
101 |
border-radius: 10px !important;
|
102 |
-
color: #111827 !important; /* Darker text
|
103 |
}
|
104 |
-
/* Icon Button Styling */
|
105 |
.icon-btn {
|
106 |
-
min-width:
|
107 |
-
height:
|
108 |
-
border:
|
109 |
}
|
|
|
|
|
|
|
110 |
"""
|
111 |
|
112 |
with gr.Blocks(theme=gr.themes.Base(), title="AI Doctor Consultation", css=css) as demo:
|
113 |
conversation_history = gr.State([])
|
114 |
|
115 |
with gr.Column(elem_id="chat-container"):
|
116 |
-
chatbot_display = gr.Chatbot(label="Consultation", show_copy_button=True, bubble_full_width=False)
|
117 |
|
118 |
with gr.Column(elem_id="footer-container"):
|
119 |
with gr.Row():
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
# The new icon-based upload button
|
124 |
-
upload_button = gr.UploadButton("π·", file_types=["image"], scale=1, elem_classes="icon-btn")
|
125 |
-
|
126 |
user_textbox = gr.Textbox(
|
127 |
elem_id="user-textbox",
|
128 |
placeholder="Type your message, or upload an image...",
|
129 |
show_label=False, scale=5, container=False
|
130 |
)
|
131 |
-
|
132 |
-
|
133 |
-
clear_button = gr.Button("ποΈ", scale=1, elem_classes="icon-btn")
|
134 |
|
135 |
-
# --- Event Handlers for the new UI ---
|
136 |
-
|
137 |
-
# This handler makes the image preview appear when a user uploads a file.
|
138 |
def show_image_preview(image_file):
|
139 |
-
# When a file is uploaded, make the preview Image component visible and set its value.
|
140 |
return gr.Image(value=image_file, visible=True)
|
141 |
|
142 |
upload_button.upload(fn=show_image_preview, inputs=upload_button, outputs=image_preview)
|
143 |
|
144 |
-
# Main function to handle the conversation flow
|
145 |
def submit_message_and_stream(user_input: str, user_image: Image.Image, history: list):
|
146 |
if not user_input.strip() and user_image is None:
|
147 |
return history, history, None
|
148 |
-
|
149 |
-
# 1. Instantly add the user's message (and image) to the chat UI
|
150 |
history.append((user_input, None))
|
151 |
yield history, history, None
|
152 |
-
|
153 |
-
# 2. Start the generator to get the AI's response stream
|
154 |
for updated_history, new_state in handle_conversation_turn(user_input, user_image, history):
|
155 |
-
yield updated_history, new_state
|
156 |
|
157 |
-
# Function to clear the textbox and image preview after submission
|
158 |
def clear_inputs():
|
159 |
return "", None
|
160 |
|
161 |
-
# Wire up the send and submit events
|
162 |
send_button.click(
|
163 |
fn=submit_message_and_stream,
|
164 |
inputs=[user_textbox, image_preview, conversation_history],
|
165 |
-
outputs=[chatbot_display, conversation_history
|
166 |
).then(fn=clear_inputs, outputs=[user_textbox, image_preview])
|
167 |
|
168 |
user_textbox.submit(
|
169 |
fn=submit_message_and_stream,
|
170 |
inputs=[user_textbox, image_preview, conversation_history],
|
171 |
-
outputs=[chatbot_display, conversation_history
|
172 |
).then(fn=clear_inputs, outputs=[user_textbox, image_preview])
|
173 |
|
174 |
-
# Wire up the clear button
|
175 |
clear_button.click(
|
176 |
lambda: ([], [], None, ""),
|
177 |
outputs=[chatbot_display, conversation_history, image_preview, user_textbox]
|
|
|
35 |
|
36 |
try:
|
37 |
system_prompt = (
|
38 |
+
"You are an expert, empathetic AI medical assistant conducting a virtual consultation. Your primary goal is to ask clarifying questions to understand the user's symptoms thoroughly. Do NOT provide a diagnosis or a list of possibilities right away. Ask only one or two focused questions per turn. If the user provides an image, your first step is to analyze it from an expert perspective. Briefly describe the key findings from the image. Then, use this analysis to ask relevant follow-up questions about the user's symptoms or medical history to better understand the context. For example, after seeing a rash, you might say, 'I see a reddish rash with well-defined borders on the forearm. To help me understand more, could you tell me when you first noticed this? Is it itchy, painful, or does it have any other sensation?' After several turns of asking questions, when you feel you have gathered enough information, you must FIRST state that you are ready to provide a summary. THEN, in the SAME response, provide a list of possible conditions, your reasoning, and a clear, actionable next-steps plan."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
)
|
40 |
|
41 |
generation_args = {"max_new_tokens": 1024, "do_sample": True, "temperature": 0.7}
|
|
|
73 |
print(f"An exception occurred during conversation turn: {type(e).__name__}: {e}")
|
74 |
yield history, history
|
75 |
|
76 |
+
# --- UI MODIFICATION: Professional CSS for a polished chat interface ---
|
77 |
css = """
|
78 |
/* Main App Styling */
|
79 |
+
body, .gradio-container { background-color: #f9fafb !important; }
|
80 |
+
#chat-container { flex-grow: 1; overflow-y: auto; padding-bottom: 200px !important; }
|
81 |
/* Chat Bubble Styling */
|
82 |
+
.user .message-bubble { background-color: #dbeafe !important; color: #1f2937 !important; }
|
83 |
+
.bot .message-bubble { background-color: #f3f4f6 !important; color: #1f2937 !important; }
|
84 |
/* Sticky Footer Input Bar */
|
85 |
#footer-container {
|
86 |
position: fixed !important; bottom: 0; left: 0; width: 100%;
|
|
|
90 |
}
|
91 |
/* Text Input Box Styling */
|
92 |
#user-textbox textarea {
|
93 |
+
background-color: #ffffff !important;
|
94 |
border: 1px solid #d1d5db !important;
|
95 |
border-radius: 10px !important;
|
96 |
+
color: #111827 !important; /* Darker input text */
|
97 |
}
|
98 |
+
/* Icon Button General Styling */
|
99 |
.icon-btn {
|
100 |
+
min-width: 50px !important; max-width: 50px !important;
|
101 |
+
height: 50px !important; font-size: 1.5rem !important;
|
102 |
+
border: none !important; border-radius: 10px !important;
|
103 |
}
|
104 |
+
/* Specific Icon Button Colors */
|
105 |
+
#restart-btn { background-color: #fee2e2 !important; color: #ef4444 !important; }
|
106 |
+
#upload-btn, #send-btn { background-color: #3b82f6 !important; color: white !important; }
|
107 |
"""
|
108 |
|
109 |
with gr.Blocks(theme=gr.themes.Base(), title="AI Doctor Consultation", css=css) as demo:
|
110 |
conversation_history = gr.State([])
|
111 |
|
112 |
with gr.Column(elem_id="chat-container"):
|
113 |
+
chatbot_display = gr.Chatbot(label="Consultation", show_copy_button=True, bubble_full_width=False, avatar_images=("./images/user.png", "./images/bot.png"))
|
114 |
|
115 |
with gr.Column(elem_id="footer-container"):
|
116 |
with gr.Row():
|
117 |
+
image_preview = gr.Image(type="pil", height=60, width=60, visible=False, show_label=False, container=False, scale=1)
|
118 |
+
upload_button = gr.UploadButton("π·", file_types=["image"], elem_id="upload-btn", elem_classes="icon-btn", scale=1)
|
|
|
|
|
|
|
|
|
119 |
user_textbox = gr.Textbox(
|
120 |
elem_id="user-textbox",
|
121 |
placeholder="Type your message, or upload an image...",
|
122 |
show_label=False, scale=5, container=False
|
123 |
)
|
124 |
+
send_button = gr.Button("β€", elem_id="send-btn", elem_classes="icon-btn", scale=1)
|
125 |
+
clear_button = gr.Button("π", elem_id="restart-btn", elem_classes="icon-btn", scale=1)
|
|
|
126 |
|
|
|
|
|
|
|
127 |
def show_image_preview(image_file):
|
|
|
128 |
return gr.Image(value=image_file, visible=True)
|
129 |
|
130 |
upload_button.upload(fn=show_image_preview, inputs=upload_button, outputs=image_preview)
|
131 |
|
|
|
132 |
def submit_message_and_stream(user_input: str, user_image: Image.Image, history: list):
|
133 |
if not user_input.strip() and user_image is None:
|
134 |
return history, history, None
|
|
|
|
|
135 |
history.append((user_input, None))
|
136 |
yield history, history, None
|
|
|
|
|
137 |
for updated_history, new_state in handle_conversation_turn(user_input, user_image, history):
|
138 |
+
yield updated_history, new_state
|
139 |
|
|
|
140 |
def clear_inputs():
|
141 |
return "", None
|
142 |
|
|
|
143 |
send_button.click(
|
144 |
fn=submit_message_and_stream,
|
145 |
inputs=[user_textbox, image_preview, conversation_history],
|
146 |
+
outputs=[chatbot_display, conversation_history],
|
147 |
).then(fn=clear_inputs, outputs=[user_textbox, image_preview])
|
148 |
|
149 |
user_textbox.submit(
|
150 |
fn=submit_message_and_stream,
|
151 |
inputs=[user_textbox, image_preview, conversation_history],
|
152 |
+
outputs=[chatbot_display, conversation_history],
|
153 |
).then(fn=clear_inputs, outputs=[user_textbox, image_preview])
|
154 |
|
|
|
155 |
clear_button.click(
|
156 |
lambda: ([], [], None, ""),
|
157 |
outputs=[chatbot_display, conversation_history, image_preview, user_textbox]
|