Leonardo commited on
Commit
8d5671a
·
verified ·
1 Parent(s): a5a9118

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -122
app.py CHANGED
@@ -3,7 +3,7 @@ import re
3
  import shutil
4
  import datetime
5
  import mimetypes
6
- from typing import Optional, List, Dict, Tuple # More specific typing
7
 
8
  from dotenv import load_dotenv
9
  from huggingface_hub import login
@@ -35,7 +35,6 @@ from smolagents import (
35
  from smolagents.agent_types import AgentText, AgentImage, AgentAudio
36
  from smolagents.gradio_ui import pull_messages_from_step, handle_agent_output_types
37
 
38
-
39
  # ------------------------ Configuration and Setup ------------------------
40
  AUTHORIZED_IMPORTS = [
41
  "requests",
@@ -131,33 +130,30 @@ ALLOWED_EXTENSIONS = [
131
 
132
 
133
  def setup_environment():
134
- """
135
- Initialize environment variables and authenticate with Hugging Face Hub.
136
- """
137
  load_dotenv(override=True)
138
- hf_token = os.getenv("HF_TOKEN") # Get token once
139
  if hf_token:
140
  login(hf_token)
141
- print("HF_TOKEN (last 10 characters):", hf_token[-10:])
142
  else:
143
  print("HF_TOKEN not found in environment variables.")
144
 
145
 
146
  # ------------------------ Model and Tool Management ------------------------
147
  class ModelManager:
148
- """
149
- Manages model loading and initialization.
150
- """
151
 
152
  @staticmethod
153
- def load_model(chosen_inference: str, model_id: str, key_manager=None):
154
- """
155
- Load the specified model with appropriate configuration.
 
156
 
157
  Args:
158
- chosen_inference (str): The type of inference to use (e.g., "hf_api", "openai").
159
- model_id (str): The ID of the model to load.
160
- key_manager: (Optional) Key manager for API keys. Required for OpenAI.
161
 
162
  Returns:
163
  An instance of the specified model class.
@@ -170,24 +166,23 @@ class ModelManager:
170
  try:
171
  if chosen_inference == "hf_api":
172
  return HfApiModel(model_id=model_id)
173
- elif chosen_inference == "hf_api_provider":
174
  return HfApiModel(provider="together")
175
- elif chosen_inference == "litellm":
176
  return LiteLLMModel(model_id=model_id)
177
- elif chosen_inference == "openai":
178
  if not key_manager:
179
  raise ValueError("Key manager required for OpenAI model")
180
  return OpenAIServerModel(
181
  model_id=model_id, api_key=key_manager.get_key("openai_api_key")
182
  )
183
- elif chosen_inference == "transformers":
184
  return TransformersModel(
185
  model_id="HuggingFaceTB/SmolLM2-1.7B-Instruct",
186
  device_map="auto",
187
  max_new_tokens=1000,
188
  )
189
- else:
190
- raise ValueError(f"Invalid inference type: {chosen_inference}")
191
  except Exception as e:
192
  print(f"✗ Couldn't load model: {e}")
193
  raise
@@ -197,14 +192,13 @@ class ToolRegistry:
197
  """Manages tool initialization and organization."""
198
 
199
  @staticmethod
200
- def load_web_tools(model, browser, text_limit=20000) -> List[Tool]:
201
- """
202
- Initialize and return web-related tools.
203
 
204
  Args:
205
  model: The language model to use.
206
  browser: The web browser instance.
207
- text_limit (int): The maximum text length for the text inspector tool.
208
 
209
  Returns:
210
  A list of web-related tools.
@@ -222,8 +216,7 @@ class ToolRegistry:
222
 
223
  @staticmethod
224
  def load_document_tools() -> List[Tool]:
225
- """
226
- Initialize and return document processing tools.
227
 
228
  Returns:
229
  List of document tools.
@@ -232,8 +225,7 @@ class ToolRegistry:
232
 
233
  @staticmethod
234
  def load_image_generation_tools() -> Optional[Tool]:
235
- """
236
- Initialize and return image generation tools.
237
 
238
  Returns:
239
  The image generation tool or None if initialization fails.
@@ -251,8 +243,7 @@ class ToolRegistry:
251
 
252
  # ------------------------ Agent Creation and Execution ------------------------
253
  def create_agent() -> CodeAgent:
254
- """
255
- Creates a fresh agent instance with configured tools.
256
 
257
  Returns:
258
  CodeAgent: Configured agent ready for use.
@@ -309,7 +300,10 @@ def create_agent() -> CodeAgent:
309
 
310
 
311
  def stream_to_gradio(
312
- agent, task: str, reset_agent_memory: bool = False, additional_args=None
 
 
 
313
  ):
314
  """Runs an agent with the given task and streams messages as Gradio ChatMessages."""
315
  try:
@@ -344,9 +338,10 @@ def stream_to_gradio(
344
  role="assistant", content=f"Final answer: {str(final_answer)}"
345
  )
346
  except Exception as e:
 
347
  yield gr.ChatMessage(
348
  role="assistant",
349
- content=f"Error occurred during processing: {str(e)}\n\nPlease try again with a different query or check your inputs.",
350
  )
351
 
352
 
@@ -359,7 +354,7 @@ class GradioUI:
359
  self.file_upload_folder = file_upload_folder
360
  self.allowed_extensions = ALLOWED_EXTENSIONS # Use the constant
361
 
362
- if self.file_upload_folder is not None:
363
  os.makedirs(self.file_upload_folder, exist_ok=True)
364
 
365
  def interact_with_agent(
@@ -368,57 +363,61 @@ class GradioUI:
368
  messages: List[Dict],
369
  session_state: Dict,
370
  uploaded_files: List[str],
371
- ) -> List[Dict]: # Type hints
372
  """Main interaction handler with the agent."""
373
 
374
- # Get or create session-specific agent with cache persistence
375
  if "agent" not in session_state:
376
  try:
377
  session_state["agent"] = create_agent()
378
  session_state["creation_time"] = datetime.datetime.now()
379
  session_state["request_count"] = 0
380
  except Exception as e:
 
381
  messages.append(
382
  gr.ChatMessage(
383
  role="assistant",
384
- content=f"Error initializing agent: {str(e)}\n\nPlease refresh the page and try again.",
385
  )
386
  )
387
  yield messages
388
- return
389
 
390
  session_state["request_count"] += 1
391
 
392
- # Add user message
393
  messages.append(gr.ChatMessage(role="user", content=prompt))
394
  yield messages
395
 
396
- # Process files, linking them to the message
397
  file_message = ""
398
- if uploaded_files:
399
- file_info = {}
400
- for file_path in uploaded_files:
401
- ext = os.path.splitext(file_path)[1].lower()
402
- if ext in [".jpg", ".jpeg", ".png", ".gif", ".webp"]:
403
- category = "images"
404
- elif ext in [".mp3", ".wav", ".ogg"]:
405
- category = "audio"
406
- else:
407
- category = "documents"
408
-
409
- if category not in file_info:
410
- file_info[category] = []
411
- file_info[category].append(os.path.basename(file_path))
412
-
413
- # Format file information for the agent
414
- file_message = "\nYou have been provided with these files:\n"
415
- for category, files in file_info.items():
416
- file_message += f"- {category.capitalize()}: {', '.join(files)}\n"
417
-
418
- prompt_with_files = prompt + file_message
 
 
 
 
 
 
 
419
 
420
  try:
421
- # Check if agent should be reset (e.g., if too many requests)
422
  reset_needed = session_state["request_count"] > 15
423
 
424
  for msg in stream_to_gradio(
@@ -429,22 +428,21 @@ class GradioUI:
429
  messages.append(msg)
430
  yield messages
431
 
432
- # If we reset the agent memory, update the request count
433
  if reset_needed:
434
  session_state["request_count"] = 1
435
 
436
  except Exception as e:
 
437
  messages.append(
438
  gr.ChatMessage(
439
  role="assistant",
440
- content=f"Error processing your request: {str(e)}\n\nPlease try again with a different query.",
441
  )
442
  )
443
  yield messages
444
 
445
  def log_user_message(self, text_input: str) -> Tuple[str, gr.Textbox, gr.Button]:
446
  """Process user message log files."""
447
-
448
  return (
449
  text_input,
450
  gr.Textbox(value="", interactive=False, placeholder="Processing..."),
@@ -459,65 +457,55 @@ class GradioUI:
459
  uploaded_files = []
460
  error_message = None
461
 
462
- for file_path in files: # Iterate through the list of uploaded files
463
  try:
464
- # Check file extension
465
  file_extension = os.path.splitext(file_path)[1].lower()
466
  if file_extension not in self.allowed_extensions:
467
  error_message = (
468
  f"❌ File type '{file_extension}' is not allowed. "
469
- f"Supported types: {', '.join(ALLOWED_EXTENSIONS)}" # Use Constant
470
  )
 
 
 
 
471
 
472
- return error_message, [] # Return immediately on first error
473
- else:
474
- file_size_mb = os.path.getsize(file_path) / (
475
- 1024 * 1024
476
- ) # Size in MB
477
- max_file_size_mb = 50 # Define the limit
478
-
479
- if file_size_mb > max_file_size_mb:
480
- error_message = f" File size ({file_size_mb:.1f} MB) exceeds {max_file_size_mb} MB limit."
481
- return error_message, []
482
-
483
- sanitized_name = re.sub(
484
- r"[^\w\-.]", "", os.path.basename(file_path)
485
- ) # Sanitize
486
- dest_path = os.path.join(self.file_upload_folder, sanitized_name)
487
- shutil.copy(file_path, dest_path)
488
- uploaded_files.append(dest_path)
489
- print(f"Uploaded {file_path} to {dest_path}")
490
 
491
  except Exception as e:
492
  error_message = f"❌ Upload error: {str(e)}"
493
- return error_message, [] # Immediately return on error
494
 
495
  if error_message:
496
  return error_message, []
497
- else:
498
- return (
499
- f"✓ Files uploaded successfully: {', '.join([os.path.basename(f) for f in uploaded_files])}",
500
- uploaded_files,
501
- )
502
 
503
  def detect_device(self, request: gr.Request):
504
  """Detect whether the user is on mobile or desktop device."""
505
  if not request:
506
- return "Unknown device" # Handle case where request is none.
507
 
508
- # Method 1: Check sec-ch-ua-mobile header
509
  is_mobile_header = request.headers.get("sec-ch-ua-mobile")
510
  if is_mobile_header:
511
  return "Mobile" if "?1" in is_mobile_header else "Desktop"
512
 
513
- # Method 2: Check user-agent string
514
  user_agent = request.headers.get("user-agent", "").lower()
515
  mobile_keywords = ["android", "iphone", "ipad", "mobile", "phone"]
516
-
517
  if any(keyword in user_agent for keyword in mobile_keywords):
518
  return "Mobile"
519
 
520
- # Method 3: Check platform
521
  platform = request.headers.get("sec-ch-ua-platform", "").lower()
522
  if platform:
523
  if platform in ['"android"', '"ios"']:
@@ -525,25 +513,21 @@ class GradioUI:
525
  if platform in ['"windows"', '"macos"', '"linux"']:
526
  return "Desktop"
527
 
528
- # Default case if no clear indicators
529
  return "Desktop"
530
 
531
  def launch(self, **kwargs):
532
  """Launch the Gradio UI with responsive layout."""
533
  with gr.Blocks(theme="ocean", fill_height=True) as demo:
534
- # Different layouts for mobile and computer devices
535
  @gr.render()
536
  def layout(request: gr.Request):
537
  device = self.detect_device(request)
538
  print(f"device - {device}")
539
- # Render layout with sidebar
540
  if device == "Desktop":
541
  return self._create_desktop_layout()
542
  return self._create_mobile_layout()
543
 
544
- demo.queue(max_size=20).launch(
545
- debug=True, **kwargs
546
- ) # Add queue with reasonable size
547
 
548
  def _create_desktop_layout(self):
549
  """Create the desktop layout with sidebar and enhanced styling."""
@@ -568,11 +552,9 @@ class GradioUI:
568
  clear_btn = gr.Button("Clear", variant="secondary")
569
  launch_research_btn = gr.Button("Run", variant="primary")
570
 
571
- # File upload section with better labeling
572
- if self.file_upload_folder is not None:
573
  with gr.Group():
574
  gr.Markdown("📎 Upload Documents")
575
- # File input with multiple enabled with the allowed extensions
576
  file_upload = gr.File(
577
  label="Upload files for analysis",
578
  file_types=self.allowed_extensions,
@@ -598,7 +580,6 @@ class GradioUI:
598
  """
599
  )
600
 
601
- # Main chat area with improved styling
602
  session_state = gr.State({})
603
  stored_messages = gr.State([])
604
 
@@ -616,21 +597,19 @@ class GradioUI:
616
  height=700,
617
  )
618
 
619
- # Connect clear button
620
  clear_btn.click(
621
  lambda: ([], [], {"agent": session_state.get("agent")}, []),
622
  None,
623
  [chatbot, stored_messages, session_state, uploaded_files_state],
624
  )
625
 
626
- if self.file_upload_folder is not None:
627
  file_upload.change(
628
  self.upload_file,
629
  [file_upload],
630
  [upload_status, uploaded_files_state],
631
  )
632
 
633
- # Connect event handlers
634
  self._connect_event_handlers(
635
  text_input,
636
  launch_research_btn,
@@ -646,10 +625,8 @@ class GradioUI:
646
  """Create the mobile layout (simpler without sidebar)."""
647
  with gr.Blocks(fill_height=True) as simple_demo:
648
  gr.Markdown("""#OpenDeepResearch - free the AI agents!""")
649
- # Add session state to store session-specific data
650
  session_state = gr.State({})
651
  stored_messages = gr.State([])
652
- # File input with multiple enabled with the allowed extensions
653
  file_upload = gr.File(
654
  label="Upload files for analysis",
655
  file_types=self.allowed_extensions,
@@ -668,8 +645,7 @@ class GradioUI:
668
  scale=1,
669
  )
670
 
671
- # If an upload folder is provided, enable the upload feature
672
- if self.file_upload_folder is not None:
673
  upload_status = gr.Textbox(
674
  label="Upload Status", interactive=False, visible=False
675
  )
@@ -707,7 +683,6 @@ class GradioUI:
707
  uploaded_files_state,
708
  ):
709
  """Connect the event handlers for input elements."""
710
- # Connect text input submit event
711
  text_input.submit(
712
  self.log_user_message,
713
  [text_input],
@@ -728,7 +703,6 @@ class GradioUI:
728
  [text_input, launch_research_btn],
729
  )
730
 
731
- # Connect button click event
732
  launch_research_btn.click(
733
  self.log_user_message,
734
  [text_input],
@@ -753,13 +727,8 @@ class GradioUI:
753
  # ------------------------ Execution ------------------------
754
  def main():
755
  """Main entry point for the application."""
756
- # Initialize environment
757
  setup_environment()
758
-
759
- # Ensure downloads folder exists
760
  os.makedirs(f"./{BROWSER_CONFIG['downloads_folder']}", exist_ok=True)
761
-
762
- # Launch UI
763
  GradioUI(file_upload_folder="uploaded_files").launch()
764
 
765
 
 
3
  import shutil
4
  import datetime
5
  import mimetypes
6
+ from typing import Optional, List, Dict, Tuple
7
 
8
  from dotenv import load_dotenv
9
  from huggingface_hub import login
 
35
  from smolagents.agent_types import AgentText, AgentImage, AgentAudio
36
  from smolagents.gradio_ui import pull_messages_from_step, handle_agent_output_types
37
 
 
38
  # ------------------------ Configuration and Setup ------------------------
39
  AUTHORIZED_IMPORTS = [
40
  "requests",
 
130
 
131
 
132
  def setup_environment():
133
+ """Initialize environment variables and authenticate with Hugging Face Hub."""
 
 
134
  load_dotenv(override=True)
135
+ hf_token = os.getenv("HF_TOKEN")
136
  if hf_token:
137
  login(hf_token)
138
+ print(f"HF_TOKEN (last 10 characters): {hf_token[-10:]}")
139
  else:
140
  print("HF_TOKEN not found in environment variables.")
141
 
142
 
143
  # ------------------------ Model and Tool Management ------------------------
144
  class ModelManager:
145
+ """Manages model loading and initialization."""
 
 
146
 
147
  @staticmethod
148
+ def load_model(
149
+ chosen_inference: str, model_id: str, key_manager: Optional[object] = None
150
+ ):
151
+ """Load the specified model with appropriate configuration.
152
 
153
  Args:
154
+ chosen_inference: The type of inference to use (e.g., "hf_api", "openai").
155
+ model_id: The ID of the model to load.
156
+ key_manager: Key manager for API keys (required for OpenAI).
157
 
158
  Returns:
159
  An instance of the specified model class.
 
166
  try:
167
  if chosen_inference == "hf_api":
168
  return HfApiModel(model_id=model_id)
169
+ if chosen_inference == "hf_api_provider":
170
  return HfApiModel(provider="together")
171
+ if chosen_inference == "litellm":
172
  return LiteLLMModel(model_id=model_id)
173
+ if chosen_inference == "openai":
174
  if not key_manager:
175
  raise ValueError("Key manager required for OpenAI model")
176
  return OpenAIServerModel(
177
  model_id=model_id, api_key=key_manager.get_key("openai_api_key")
178
  )
179
+ if chosen_inference == "transformers":
180
  return TransformersModel(
181
  model_id="HuggingFaceTB/SmolLM2-1.7B-Instruct",
182
  device_map="auto",
183
  max_new_tokens=1000,
184
  )
185
+ raise ValueError(f"Invalid inference type: {chosen_inference}")
 
186
  except Exception as e:
187
  print(f"✗ Couldn't load model: {e}")
188
  raise
 
192
  """Manages tool initialization and organization."""
193
 
194
  @staticmethod
195
+ def load_web_tools(model, browser, text_limit: int = 20000) -> List[Tool]:
196
+ """Initialize and return web-related tools.
 
197
 
198
  Args:
199
  model: The language model to use.
200
  browser: The web browser instance.
201
+ text_limit: The maximum text length for the text inspector tool.
202
 
203
  Returns:
204
  A list of web-related tools.
 
216
 
217
  @staticmethod
218
  def load_document_tools() -> List[Tool]:
219
+ """Initialize and return document processing tools.
 
220
 
221
  Returns:
222
  List of document tools.
 
225
 
226
  @staticmethod
227
  def load_image_generation_tools() -> Optional[Tool]:
228
+ """Initialize and return image generation tools.
 
229
 
230
  Returns:
231
  The image generation tool or None if initialization fails.
 
243
 
244
  # ------------------------ Agent Creation and Execution ------------------------
245
  def create_agent() -> CodeAgent:
246
+ """Creates a fresh agent instance with configured tools.
 
247
 
248
  Returns:
249
  CodeAgent: Configured agent ready for use.
 
300
 
301
 
302
  def stream_to_gradio(
303
+ agent,
304
+ task: str,
305
+ reset_agent_memory: bool = False,
306
+ additional_args: Optional[dict] = None,
307
  ):
308
  """Runs an agent with the given task and streams messages as Gradio ChatMessages."""
309
  try:
 
338
  role="assistant", content=f"Final answer: {str(final_answer)}"
339
  )
340
  except Exception as e:
341
+ error_message = f"Error occurred during processing: {str(e)}\n\nPlease try again with a different query or check your inputs."
342
  yield gr.ChatMessage(
343
  role="assistant",
344
+ content=error_message,
345
  )
346
 
347
 
 
354
  self.file_upload_folder = file_upload_folder
355
  self.allowed_extensions = ALLOWED_EXTENSIONS # Use the constant
356
 
357
+ if self.file_upload_folder:
358
  os.makedirs(self.file_upload_folder, exist_ok=True)
359
 
360
  def interact_with_agent(
 
363
  messages: List[Dict],
364
  session_state: Dict,
365
  uploaded_files: List[str],
366
+ ) -> List[Dict]:
367
  """Main interaction handler with the agent."""
368
 
 
369
  if "agent" not in session_state:
370
  try:
371
  session_state["agent"] = create_agent()
372
  session_state["creation_time"] = datetime.datetime.now()
373
  session_state["request_count"] = 0
374
  except Exception as e:
375
+ error_message = f"Error initializing agent: {str(e)}\n\nPlease refresh the page and try again."
376
  messages.append(
377
  gr.ChatMessage(
378
  role="assistant",
379
+ content=error_message,
380
  )
381
  )
382
  yield messages
383
+ return # Exit if can't create agent
384
 
385
  session_state["request_count"] += 1
386
 
 
387
  messages.append(gr.ChatMessage(role="user", content=prompt))
388
  yield messages
389
 
 
390
  file_message = ""
391
+ try:
392
+ if uploaded_files:
393
+ file_info = {}
394
+ for file_path in uploaded_files:
395
+ ext = os.path.splitext(file_path)[1].lower()
396
+ if ext in [".jpg", ".jpeg", ".png", ".gif", ".webp"]:
397
+ category = "images"
398
+ elif ext in [".mp3", ".wav", ".ogg"]:
399
+ category = "audio"
400
+ else:
401
+ category = "documents"
402
+
403
+ if category not in file_info:
404
+ file_info[category] = []
405
+ file_info[category].append(os.path.basename(file_path))
406
+
407
+ file_message = "\nYou have been provided with these files:\n"
408
+ for category, files in file_info.items():
409
+ file_message += f"- {category.capitalize()}: {', '.join(files)}\n"
410
+
411
+ prompt_with_files = prompt + file_message
412
+ else:
413
+ prompt_with_files = prompt
414
+ except Exception as e:
415
+ prompt_with_files = prompt
416
+ print(
417
+ f"WARNING: Error processing files: {e}. Continuing without file info."
418
+ )
419
 
420
  try:
 
421
  reset_needed = session_state["request_count"] > 15
422
 
423
  for msg in stream_to_gradio(
 
428
  messages.append(msg)
429
  yield messages
430
 
 
431
  if reset_needed:
432
  session_state["request_count"] = 1
433
 
434
  except Exception as e:
435
+ error_message = f"Error processing your request: {str(e)}\n\nPlease try again with a different query."
436
  messages.append(
437
  gr.ChatMessage(
438
  role="assistant",
439
+ content=error_message,
440
  )
441
  )
442
  yield messages
443
 
444
  def log_user_message(self, text_input: str) -> Tuple[str, gr.Textbox, gr.Button]:
445
  """Process user message log files."""
 
446
  return (
447
  text_input,
448
  gr.Textbox(value="", interactive=False, placeholder="Processing..."),
 
457
  uploaded_files = []
458
  error_message = None
459
 
460
+ for file_path in files:
461
  try:
 
462
  file_extension = os.path.splitext(file_path)[1].lower()
463
  if file_extension not in self.allowed_extensions:
464
  error_message = (
465
  f"❌ File type '{file_extension}' is not allowed. "
466
+ f"Supported types: {', '.join(ALLOWED_EXTENSIONS)}"
467
  )
468
+ return error_message, []
469
+
470
+ file_size_mb = os.path.getsize(file_path) / (1024 * 1024)
471
+ max_file_size_mb = 50
472
 
473
+ if file_size_mb > max_file_size_mb:
474
+ error_message = f"❌ File size ({file_size_mb:.1f} MB) exceeds {max_file_size_mb} MB limit."
475
+ return error_message, []
476
+
477
+ sanitized_name = re.sub(r"[^\w\-.]", "", os.path.basename(file_path))
478
+ dest_path = os.path.join(self.file_upload_folder, sanitized_name)
479
+ shutil.copy(file_path, dest_path)
480
+ uploaded_files.append(dest_path)
481
+ print(f"Uploaded {file_path} to {dest_path}")
 
 
 
 
 
 
 
 
 
482
 
483
  except Exception as e:
484
  error_message = f"❌ Upload error: {str(e)}"
485
+ return error_message, []
486
 
487
  if error_message:
488
  return error_message, []
489
+
490
+ return (
491
+ f"✓ Files uploaded successfully: {', '.join([os.path.basename(f) for f in uploaded_files])}",
492
+ uploaded_files,
493
+ )
494
 
495
  def detect_device(self, request: gr.Request):
496
  """Detect whether the user is on mobile or desktop device."""
497
  if not request:
498
+ return "Unknown device"
499
 
 
500
  is_mobile_header = request.headers.get("sec-ch-ua-mobile")
501
  if is_mobile_header:
502
  return "Mobile" if "?1" in is_mobile_header else "Desktop"
503
 
 
504
  user_agent = request.headers.get("user-agent", "").lower()
505
  mobile_keywords = ["android", "iphone", "ipad", "mobile", "phone"]
 
506
  if any(keyword in user_agent for keyword in mobile_keywords):
507
  return "Mobile"
508
 
 
509
  platform = request.headers.get("sec-ch-ua-platform", "").lower()
510
  if platform:
511
  if platform in ['"android"', '"ios"']:
 
513
  if platform in ['"windows"', '"macos"', '"linux"']:
514
  return "Desktop"
515
 
 
516
  return "Desktop"
517
 
518
  def launch(self, **kwargs):
519
  """Launch the Gradio UI with responsive layout."""
520
  with gr.Blocks(theme="ocean", fill_height=True) as demo:
521
+
522
  @gr.render()
523
  def layout(request: gr.Request):
524
  device = self.detect_device(request)
525
  print(f"device - {device}")
 
526
  if device == "Desktop":
527
  return self._create_desktop_layout()
528
  return self._create_mobile_layout()
529
 
530
+ demo.queue(max_size=20).launch(debug=True, **kwargs)
 
 
531
 
532
  def _create_desktop_layout(self):
533
  """Create the desktop layout with sidebar and enhanced styling."""
 
552
  clear_btn = gr.Button("Clear", variant="secondary")
553
  launch_research_btn = gr.Button("Run", variant="primary")
554
 
555
+ if self.file_upload_folder:
 
556
  with gr.Group():
557
  gr.Markdown("📎 Upload Documents")
 
558
  file_upload = gr.File(
559
  label="Upload files for analysis",
560
  file_types=self.allowed_extensions,
 
580
  """
581
  )
582
 
 
583
  session_state = gr.State({})
584
  stored_messages = gr.State([])
585
 
 
597
  height=700,
598
  )
599
 
 
600
  clear_btn.click(
601
  lambda: ([], [], {"agent": session_state.get("agent")}, []),
602
  None,
603
  [chatbot, stored_messages, session_state, uploaded_files_state],
604
  )
605
 
606
+ if self.file_upload_folder:
607
  file_upload.change(
608
  self.upload_file,
609
  [file_upload],
610
  [upload_status, uploaded_files_state],
611
  )
612
 
 
613
  self._connect_event_handlers(
614
  text_input,
615
  launch_research_btn,
 
625
  """Create the mobile layout (simpler without sidebar)."""
626
  with gr.Blocks(fill_height=True) as simple_demo:
627
  gr.Markdown("""#OpenDeepResearch - free the AI agents!""")
 
628
  session_state = gr.State({})
629
  stored_messages = gr.State([])
 
630
  file_upload = gr.File(
631
  label="Upload files for analysis",
632
  file_types=self.allowed_extensions,
 
645
  scale=1,
646
  )
647
 
648
+ if self.file_upload_folder:
 
649
  upload_status = gr.Textbox(
650
  label="Upload Status", interactive=False, visible=False
651
  )
 
683
  uploaded_files_state,
684
  ):
685
  """Connect the event handlers for input elements."""
 
686
  text_input.submit(
687
  self.log_user_message,
688
  [text_input],
 
703
  [text_input, launch_research_btn],
704
  )
705
 
 
706
  launch_research_btn.click(
707
  self.log_user_message,
708
  [text_input],
 
727
  # ------------------------ Execution ------------------------
728
  def main():
729
  """Main entry point for the application."""
 
730
  setup_environment()
 
 
731
  os.makedirs(f"./{BROWSER_CONFIG['downloads_folder']}", exist_ok=True)
 
 
732
  GradioUI(file_upload_folder="uploaded_files").launch()
733
 
734