mohammedelfeky-ai commited on
Commit
9b8d24a
·
verified ·
1 Parent(s): 689850e

Update Gradio_UI.py

Browse files
Files changed (1) hide show
  1. Gradio_UI.py +73 -110
Gradio_UI.py CHANGED
@@ -13,10 +13,10 @@
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
  # See the License for the specific language governing permissions and
15
  # limitations under the License.
16
- import mimetypes
17
  import os
18
  import re
19
- import shutil
20
  from typing import Optional
21
  import tempfile
22
  from PIL import Image as PILImage
@@ -123,14 +123,16 @@ def stream_to_gradio(
123
  final_answer_content = all_step_logs[-1]
124
 
125
  actual_content_for_handling = final_answer_content
126
- if hasattr(final_answer_content, 'final_answer') and not isinstance(final_answer_content, (str, PILImage.Image)):
 
127
  actual_content_for_handling = final_answer_content.final_answer
128
  print(f"DEBUG Gradio: Extracted actual_content_for_handling from FinalAnswerStep: {type(actual_content_for_handling)}")
129
 
 
130
  if isinstance(actual_content_for_handling, PILImage.Image):
131
  print("DEBUG Gradio (stream_to_gradio): Actual content IS a raw PIL Image.")
132
  try:
133
- with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp_file:
134
  actual_content_for_handling.save(tmp_file, format="PNG")
135
  image_path_for_gradio = tmp_file.name
136
  print(f"DEBUG Gradio: Saved PIL image to temp path: {image_path_for_gradio}")
@@ -141,6 +143,7 @@ def stream_to_gradio(
141
  yield {"role": "assistant", "content": f"**Final Answer (Error displaying image):** {e}"}
142
  return
143
 
 
144
  final_answer_processed = handle_agent_output_types(actual_content_for_handling)
145
  print(f"DEBUG Gradio: final_answer_processed type after handle_agent_output_types: {type(final_answer_processed)}")
146
 
@@ -165,18 +168,20 @@ def stream_to_gradio(
165
  print(f"DEBUG Gradio: {err_msg}")
166
  yield {"role": "assistant", "content": f"**Final Answer ({err_msg})**"}
167
  else:
 
168
  yield {"role": "assistant", "content": f"**Final Answer:**\n{str(final_answer_processed)}"}
169
 
170
 
171
  class GradioUI:
172
- def __init__(self, agent: MultiStepAgent, file_upload_folder: str | None = None):
173
  if not _is_package_available("gradio"):
174
  raise ModuleNotFoundError("Install 'gradio': `pip install 'smolagents[gradio]'`")
175
  self.agent = agent
176
- self.file_upload_folder = file_upload_folder
177
- if self.file_upload_folder is not None:
178
- if not os.path.exists(self.file_upload_folder):
179
- os.makedirs(self.file_upload_folder, exist_ok=True)
 
180
  self._latest_file_path_for_download = None
181
 
182
  def _check_for_created_file(self):
@@ -186,112 +191,69 @@ class GradioUI:
186
  for log_entry in reversed(self.agent.interaction_logs):
187
  if isinstance(log_entry, ActionStep):
188
  observations = getattr(log_entry, 'observations', None)
 
189
 
190
- if observations and isinstance(observations, str):
191
- # Log the full observations for the relevant step to help debug the regex
192
- # We are looking for the output of python_interpreter which contains create_document's print
193
- tool_calls = getattr(log_entry, 'tool_calls', [])
194
- is_python_interpreter_step = any(tc.name == "python_interpreter" for tc in tool_calls)
195
-
196
- if is_python_interpreter_step: # Only parse observations from python_interpreter
197
- print(f"DEBUG Gradio UI (_check_for_file): Python Interpreter Observations: '''{observations}'''")
198
-
199
- # Regex to find paths specifically printed by our create_document tool
200
- # This pattern should match:
201
- # "Document created (docx): /tmp/random/generated_document.docx"
202
- # "Document created (txt): /tmp/random/generated_document.txt"
203
- # "Document converted to PDF: /tmp/random/generated_document.pdf"
204
- # It allows for optional "Execution logs:" prefix and surrounding newlines/whitespace.
205
- match = re.search(
206
- r"(?:Document created \((?:docx|pdf|txt)\):|Document converted to PDF:)\s*(/tmp/[a-zA-Z0-9_]+/generated_document\.(?:docx|pdf|txt))",
207
- observations,
208
- re.MULTILINE # Important if observations string has multiple lines
209
- )
210
-
211
- if match:
212
- extracted_path = match.group(1)
213
- normalized_path = os.path.normpath(extracted_path)
214
- if os.path.exists(normalized_path):
215
- self._latest_file_path_for_download = normalized_path
216
- print(f"DEBUG Gradio UI: File path for download SET: {self._latest_file_path_for_download}")
217
- return True # File found and path set
218
- else:
219
- print(f"DEBUG Gradio UI: Path from create_document output ('{normalized_path}') does not exist.")
220
- # else: # Optional: log if the specific pattern isn't found in this observation
221
- # print(f"DEBUG Gradio UI: 'create_document' output pattern not found in this observation block.")
222
- print("DEBUG Gradio UI: No valid generated file path (from create_document) found in agent logs for download.")
223
  return False
224
 
225
  def interact_with_agent(self, prompt_text: str, current_chat_history: list):
226
  print(f"DEBUG Gradio: interact_with_agent called with prompt: '{prompt_text}'")
227
- # print(f"DEBUG Gradio: Current chat history (input type {type(current_chat_history)}): {current_chat_history}")
228
 
229
  updated_chat_history = current_chat_history + [{"role": "user", "content": prompt_text}]
230
 
 
231
  yield updated_chat_history, gr.update(visible=False), gr.update(value=None, visible=False)
232
 
233
  agent_responses_for_history = []
234
  for msg_dict in stream_to_gradio(self.agent, task=prompt_text, reset_agent_memory=False):
235
  agent_responses_for_history.append(msg_dict)
 
236
  yield updated_chat_history + agent_responses_for_history, gr.update(visible=False), gr.update(value=None, visible=False)
237
 
238
- file_found = self._check_for_created_file() # This call is crucial
239
-
240
- final_chat_display = updated_chat_history + agent_responses_for_history
241
- print(f"DEBUG Gradio: Final chat history for display: {len(final_chat_display)} messages. File found for download: {file_found}")
242
- yield final_chat_display, gr.update(visible=file_found), gr.update(value=None, visible=False) # Update download button visibility
243
-
244
- def upload_file(self, file, file_uploads_log_state):
245
- if file is None:
246
- return gr.update(value="No file uploaded.", visible=True), file_uploads_log_state
247
-
248
- if not self.file_upload_folder or not os.path.exists(self.file_upload_folder):
249
- os.makedirs(self.file_upload_folder, exist_ok=True)
250
-
251
- allowed_file_types = [
252
- "application/pdf",
253
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
254
- "text/plain", "image/jpeg", "image/png",
255
- ]
256
 
257
- original_name = file.orig_name if hasattr(file, 'orig_name') and file.orig_name else os.path.basename(file.name)
 
258
 
259
- mime_type, _ = mimetypes.guess_type(file.name)
260
- if mime_type is None:
261
- mime_type, _ = mimetypes.guess_type(original_name)
262
 
263
- if mime_type not in allowed_file_types:
264
- return gr.update(value=f"File type '{mime_type or 'unknown'}' for '{original_name}' is disallowed.", visible=True), file_uploads_log_state
265
 
266
- sanitized_name = re.sub(r"[^\w\-.]", "_", original_name)
267
- base_name, current_ext = os.path.splitext(sanitized_name)
268
-
269
- common_mime_to_ext = {
270
- "application/pdf": ".pdf",
271
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
272
- "text/plain": ".txt", "image/jpeg": ".jpg", "image/png": ".png"
273
- }
274
- expected_ext = common_mime_to_ext.get(mime_type)
275
-
276
- if expected_ext and current_ext.lower() != expected_ext.lower():
277
- sanitized_name = base_name + expected_ext
278
-
279
- destination_path = os.path.join(self.file_upload_folder, sanitized_name)
280
-
281
- try:
282
- shutil.copy(file.name, destination_path)
283
- print(f"DEBUG Gradio: File '{original_name}' copied to '{destination_path}'")
284
- updated_log = file_uploads_log_state + [destination_path]
285
- return gr.update(value=f"Uploaded: {original_name}", visible=True), updated_log
286
- except Exception as e:
287
- print(f"DEBUG Gradio: Error copying uploaded file: {e}")
288
- return gr.update(value=f"Error uploading {original_name}: {e}", visible=True), file_uploads_log_state
289
-
290
- def log_user_message(self, text_input_value: str, current_file_uploads: list):
291
  full_prompt = text_input_value
292
- if current_file_uploads:
293
- files_str = ", ".join([os.path.basename(f) for f in current_file_uploads])
294
- full_prompt += f"\n\n[Uploaded files for context: {files_str}]"
295
  print(f"DEBUG Gradio: Prepared prompt for agent: {full_prompt[:300]}...")
296
  return full_prompt, ""
297
 
@@ -307,15 +269,14 @@ class GradioUI:
307
 
308
  def launch(self, **kwargs):
309
  with gr.Blocks(fill_height=True, theme=gr.themes.Soft(primary_hue=gr.themes.colors.blue)) as demo:
310
- file_uploads_log_state = gr.State([])
311
  prepared_prompt_for_agent = gr.State("")
312
 
313
  gr.Markdown("## Smol Talk with your Agent")
314
 
315
  with gr.Row(equal_height=False):
316
- with gr.Column(scale=3):
317
  chatbot_display = gr.Chatbot(
318
- label="Agent Interaction",
319
  type="messages",
320
  avatar_images=(None, "https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo-round.png"),
321
  height=700,
@@ -325,28 +286,30 @@ class GradioUI:
325
  )
326
  text_message_input = gr.Textbox(
327
  lines=1,
328
- label="Your Message to the Agent",
329
  placeholder="Type your message and press Enter, or Shift+Enter for new line...",
330
  show_label=False
331
  )
332
 
333
- with gr.Column(scale=1):
334
- with gr.Accordion("File Upload", open=False):
335
- file_uploader = gr.File(label="Upload a supporting file (PDF, DOCX, TXT, JPG, PNG)")
336
- upload_status_text = gr.Textbox(label="Upload Status", interactive=False, lines=1)
337
- file_uploader.upload(
338
- self.upload_file,
339
- [file_uploader, file_uploads_log_state],
340
- [upload_status_text, file_uploads_log_state],
341
- )
 
 
342
 
343
- with gr.Accordion("Generated File", open=True):
344
  download_action_button = gr.Button("Download Generated File", visible=False)
345
  file_download_display_component = gr.File(label="Downloadable Document", visible=False, interactive=False)
346
 
 
347
  text_message_input.submit(
348
  self.log_user_message,
349
- [text_message_input, file_uploads_log_state],
350
  [prepared_prompt_for_agent, text_message_input]
351
  ).then(
352
  self.interact_with_agent,
 
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
  # See the License for the specific language governing permissions and
15
  # limitations under the License.
16
+ # import mimetypes # No longer needed if file upload is removed
17
  import os
18
  import re
19
+ # import shutil # No longer needed if file upload is removed
20
  from typing import Optional
21
  import tempfile
22
  from PIL import Image as PILImage
 
123
  final_answer_content = all_step_logs[-1]
124
 
125
  actual_content_for_handling = final_answer_content
126
+ # Check if final_answer_content is a wrapper like FinalAnswerStep and extract the core content
127
+ if hasattr(final_answer_content, 'final_answer') and not isinstance(final_answer_content, (str, PILImage.Image, tuple)): # Added tuple to avoid unwrapping already formatted image content
128
  actual_content_for_handling = final_answer_content.final_answer
129
  print(f"DEBUG Gradio: Extracted actual_content_for_handling from FinalAnswerStep: {type(actual_content_for_handling)}")
130
 
131
+ # Priority 1: Handle raw PIL Image object for direct display
132
  if isinstance(actual_content_for_handling, PILImage.Image):
133
  print("DEBUG Gradio (stream_to_gradio): Actual content IS a raw PIL Image.")
134
  try:
135
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp_file: # delete=False is important
136
  actual_content_for_handling.save(tmp_file, format="PNG")
137
  image_path_for_gradio = tmp_file.name
138
  print(f"DEBUG Gradio: Saved PIL image to temp path: {image_path_for_gradio}")
 
143
  yield {"role": "assistant", "content": f"**Final Answer (Error displaying image):** {e}"}
144
  return
145
 
146
+ # Priority 2: Use smolagents' type handling if not a raw PIL image
147
  final_answer_processed = handle_agent_output_types(actual_content_for_handling)
148
  print(f"DEBUG Gradio: final_answer_processed type after handle_agent_output_types: {type(final_answer_processed)}")
149
 
 
168
  print(f"DEBUG Gradio: {err_msg}")
169
  yield {"role": "assistant", "content": f"**Final Answer ({err_msg})**"}
170
  else:
171
+ # This will display the string representation of the object if not specifically handled
172
  yield {"role": "assistant", "content": f"**Final Answer:**\n{str(final_answer_processed)}"}
173
 
174
 
175
  class GradioUI:
176
+ def __init__(self, agent: MultiStepAgent, file_upload_folder: str | None = None): # file_upload_folder kept for potential future use
177
  if not _is_package_available("gradio"):
178
  raise ModuleNotFoundError("Install 'gradio': `pip install 'smolagents[gradio]'`")
179
  self.agent = agent
180
+ # self.file_upload_folder = file_upload_folder # Commented out as per request to simplify
181
+ # if self.file_upload_folder is not None:
182
+ # if not os.path.exists(self.file_upload_folder):
183
+ # os.makedirs(self.file_upload_folder, exist_ok=True)
184
+ self.file_upload_folder = None # Explicitly disable for now
185
  self._latest_file_path_for_download = None
186
 
187
  def _check_for_created_file(self):
 
191
  for log_entry in reversed(self.agent.interaction_logs):
192
  if isinstance(log_entry, ActionStep):
193
  observations = getattr(log_entry, 'observations', None)
194
+ tool_calls = getattr(log_entry, 'tool_calls', []) # Get tool calls if any
195
 
196
+ # Check if this step involved python_interpreter
197
+ is_python_interpreter_step = any(tc.name == "python_interpreter" for tc in tool_calls)
198
+
199
+ if is_python_interpreter_step and observations and isinstance(observations, str):
200
+ print(f"DEBUG Gradio UI (_check_for_file): Python Interpreter Observations: '''{observations[:500]}...'''") # Log snippet
201
+
202
+ # Regex to find paths specifically printed by our create_document tool
203
+ # This pattern expects: "Document created (docx): /tmp/random/generated_document.docx"
204
+ match = re.search(
205
+ # Capture group 1 is the prefix, group 2 is the path
206
+ r"(Document created \((?:docx|pdf|txt)\):|Document converted to PDF:)\s*(/tmp/[a-zA-Z0-9_]+/generated_document\.(?:docx|pdf|txt))",
207
+ observations,
208
+ re.MULTILINE
209
+ )
210
+
211
+ if match:
212
+ # extracted_prefix = match.group(1) # e.g., "Document created (docx):"
213
+ extracted_path = match.group(2) # The actual path
214
+ print(f"DEBUG Gradio UI: Regex matched. Extracted path: '{extracted_path}'")
215
+ normalized_path = os.path.normpath(extracted_path)
216
+ if os.path.exists(normalized_path):
217
+ self._latest_file_path_for_download = normalized_path
218
+ print(f"DEBUG Gradio UI: File path for download SET: {self._latest_file_path_for_download}")
219
+ return True
220
+ else:
221
+ print(f"DEBUG Gradio UI: Path from create_document output ('{normalized_path}') does not exist.")
222
+ # else:
223
+ # print(f"DEBUG Gradio UI: 'create_document' output pattern not found in this observation block.")
224
+ print("DEBUG Gradio UI: No valid generated file path (from create_document) found for download button.")
 
 
 
 
225
  return False
226
 
227
  def interact_with_agent(self, prompt_text: str, current_chat_history: list):
228
  print(f"DEBUG Gradio: interact_with_agent called with prompt: '{prompt_text}'")
 
229
 
230
  updated_chat_history = current_chat_history + [{"role": "user", "content": prompt_text}]
231
 
232
+ # Initial yield: show user message, hide download components until agent run is complete
233
  yield updated_chat_history, gr.update(visible=False), gr.update(value=None, visible=False)
234
 
235
  agent_responses_for_history = []
236
  for msg_dict in stream_to_gradio(self.agent, task=prompt_text, reset_agent_memory=False):
237
  agent_responses_for_history.append(msg_dict)
238
+ # Yield progressively to update chat, keep download components hidden during streaming
239
  yield updated_chat_history + agent_responses_for_history, gr.update(visible=False), gr.update(value=None, visible=False)
240
 
241
+ # After all agent messages are processed and added to history
242
+ final_chat_display_content = updated_chat_history + agent_responses_for_history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
+ # Now check for created files to decide visibility of download button
245
+ file_found_for_download = self._check_for_created_file()
246
 
247
+ print(f"DEBUG Gradio: Final chat history for display: {len(final_chat_display_content)} messages. File found for download button: {file_found_for_download}")
248
+ # Final yield: update chat, set visibility of download button, keep file display component hidden (it's shown on button click)
249
+ yield final_chat_display_content, gr.update(visible=file_found_for_download), gr.update(value=None, visible=False)
250
 
 
 
251
 
252
+ def log_user_message(self, text_input_value: str): # Removed current_file_uploads as upload is disabled
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  full_prompt = text_input_value
254
+ # if current_file_uploads: # This part is now disabled
255
+ # files_str = ", ".join([os.path.basename(f) for f in current_file_uploads])
256
+ # full_prompt += f"\n\n[Uploaded files for context: {files_str}]"
257
  print(f"DEBUG Gradio: Prepared prompt for agent: {full_prompt[:300]}...")
258
  return full_prompt, ""
259
 
 
269
 
270
  def launch(self, **kwargs):
271
  with gr.Blocks(fill_height=True, theme=gr.themes.Soft(primary_hue=gr.themes.colors.blue)) as demo:
272
+ # file_uploads_log_state = gr.State([]) # No longer needed as upload is disabled
273
  prepared_prompt_for_agent = gr.State("")
274
 
275
  gr.Markdown("## Smol Talk with your Agent")
276
 
277
  with gr.Row(equal_height=False):
278
+ with gr.Column(scale=3): # Main chat column
279
  chatbot_display = gr.Chatbot(
 
280
  type="messages",
281
  avatar_images=(None, "https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo-round.png"),
282
  height=700,
 
286
  )
287
  text_message_input = gr.Textbox(
288
  lines=1,
 
289
  placeholder="Type your message and press Enter, or Shift+Enter for new line...",
290
  show_label=False
291
  )
292
 
293
+ with gr.Column(scale=1): # Sidebar column
294
+ # --- File Upload Section Removed/Commented Out ---
295
+ # if self.file_upload_folder is not None:
296
+ # with gr.Accordion("File Upload", open=False):
297
+ # file_uploader = gr.File(label="Upload a supporting file (PDF, DOCX, TXT, JPG, PNG)")
298
+ # upload_status_text = gr.Textbox(label="Upload Status", interactive=False, lines=1)
299
+ # file_uploader.upload(
300
+ # self.upload_file,
301
+ # [file_uploader, file_uploads_log_state],
302
+ # [upload_status_text, file_uploads_log_state],
303
+ # )
304
 
305
+ with gr.Accordion("Generated File", open=True): # Keep this section
306
  download_action_button = gr.Button("Download Generated File", visible=False)
307
  file_download_display_component = gr.File(label="Downloadable Document", visible=False, interactive=False)
308
 
309
+ # Event Handling Chain for Text Submission
310
  text_message_input.submit(
311
  self.log_user_message,
312
+ [text_message_input], # Removed file_uploads_log_state from inputs
313
  [prepared_prompt_for_agent, text_message_input]
314
  ).then(
315
  self.interact_with_agent,