huytofu92 commited on
Commit
1a04a88
·
1 Parent(s): e9af7ec

Thread safe browsing

Browse files
Files changed (3) hide show
  1. app.py +38 -29
  2. browser.py +67 -8
  3. mini_agents.py +55 -14
app.py CHANGED
@@ -3,7 +3,7 @@ import gradio as gr
3
  import requests
4
  import pandas as pd
5
  import datasets
6
- from mini_agents import master_agent
7
  from utils import get_full_file_path
8
  from smolagents.memory import ActionStep, PlanningStep, TaskStep, SystemPromptStep, FinalAnswerStep
9
  from typing import Optional
@@ -43,43 +43,52 @@ columns = [
43
 
44
  class BasicAgent:
45
  def __init__(self):
46
- self.agent = master_agent
47
  print("Master Agent initialized.")
48
- def __call__(self, question: str, task_id: str, df_agent_steps: pd.DataFrame) -> str:
 
49
  print(f"Agent received question (first 50 chars): {question[:50]}...")
50
- fixed_answer = self.agent.run(question)
51
- all_steps = self.agent.memory.get_full_steps()
52
- for step in all_steps:
53
- if isinstance(step, ActionStep):
54
- step_class = "ActionStep"
55
- elif isinstance(step, PlanningStep):
56
- step_class = "PlanningStep"
57
- elif isinstance(step, TaskStep):
58
- step_class = "TaskStep"
59
- elif isinstance(step, SystemPromptStep):
60
- step_class = "SystemPromptStep"
61
- elif isinstance(step, FinalAnswerStep):
62
- step_class = "FinalAnswerStep"
63
- else:
64
- step_class = "UnknownStep"
 
 
 
 
65
 
66
- step_dict = step.dict()
67
- df_agent_steps.loc[len(df_agent_steps)] = None
68
- df_agent_steps.at[len(df_agent_steps), 'task_id'] = task_id
69
- df_agent_steps.at[len(df_agent_steps), 'step_class'] = step_class
70
- for key, value in step_dict.items():
71
- df_agent_steps.at[len(df_agent_steps), key] = value
72
 
73
- print(f"Agent returning fixed answer: {fixed_answer}")
74
- return fixed_answer, df_agent_steps
 
 
 
 
75
 
76
  def check_required_env_vars() -> tuple[bool, Optional[str]]:
77
  """Check if required environment variables are set"""
78
  missing_vars = []
79
 
80
  # Check HF_TOKEN
81
- if not os.getenv("HF_TOKEN"):
82
- missing_vars.append("HF_TOKEN")
83
 
84
  # Check SPACE_ID (only warn, not required)
85
  if not os.getenv("SPACE_ID"):
@@ -117,7 +126,7 @@ def save_dataset_to_hub(df: pd.DataFrame, dataset_name: str) -> tuple[bool, str]
117
  dataset.push_to_hub(
118
  dataset_name,
119
  private=True,
120
- token=os.getenv("HF_TOKEN")
121
  )
122
 
123
  return True, f"Successfully saved {len(df)} steps to {dataset_name}"
 
3
  import requests
4
  import pandas as pd
5
  import datasets
6
+ from mini_agents import MasterAgentWrapper
7
  from utils import get_full_file_path
8
  from smolagents.memory import ActionStep, PlanningStep, TaskStep, SystemPromptStep, FinalAnswerStep
9
  from typing import Optional
 
43
 
44
  class BasicAgent:
45
  def __init__(self):
46
+ self.agent = MasterAgentWrapper() # This is now the MasterAgentWrapper instance
47
  print("Master Agent initialized.")
48
+
49
+ def __call__(self, question: str, task_id: str, df_agent_steps: pd.DataFrame) -> tuple[str, pd.DataFrame]:
50
  print(f"Agent received question (first 50 chars): {question[:50]}...")
51
+ try:
52
+ # Use the wrapper's run method which handles browser tools safely
53
+ fixed_answer = self.agent.run(question)
54
+
55
+ # Log steps
56
+ all_steps = self.agent.master_agent.memory.get_full_steps()
57
+ for step in all_steps:
58
+ if isinstance(step, ActionStep):
59
+ step_class = "ActionStep"
60
+ elif isinstance(step, PlanningStep):
61
+ step_class = "PlanningStep"
62
+ elif isinstance(step, TaskStep):
63
+ step_class = "TaskStep"
64
+ elif isinstance(step, SystemPromptStep):
65
+ step_class = "SystemPromptStep"
66
+ elif isinstance(step, FinalAnswerStep):
67
+ step_class = "FinalAnswerStep"
68
+ else:
69
+ step_class = "UnknownStep"
70
 
71
+ step_dict = step.dict()
72
+ df_agent_steps.loc[len(df_agent_steps)] = None
73
+ df_agent_steps.at[len(df_agent_steps), 'task_id'] = task_id
74
+ df_agent_steps.at[len(df_agent_steps), 'step_class'] = step_class
75
+ for key, value in step_dict.items():
76
+ df_agent_steps.at[len(df_agent_steps), key] = value
77
 
78
+ print(f"Agent returning fixed answer: {fixed_answer}")
79
+ return fixed_answer, df_agent_steps
80
+
81
+ except Exception as e:
82
+ print(f"Error in agent execution: {e}")
83
+ raise
84
 
85
  def check_required_env_vars() -> tuple[bool, Optional[str]]:
86
  """Check if required environment variables are set"""
87
  missing_vars = []
88
 
89
  # Check HF_TOKEN
90
+ if not os.getenv("HUGGINGFACE_API_KEY"):
91
+ missing_vars.append("HUGGINGFACE_API_KEY")
92
 
93
  # Check SPACE_ID (only warn, not required)
94
  if not os.getenv("SPACE_ID"):
 
126
  dataset.push_to_hub(
127
  dataset_name,
128
  private=True,
129
+ token=os.getenv("HUGGINGFACE_API_KEY")
130
  )
131
 
132
  return True, f"Successfully saved {len(df)} steps to {dataset_name}"
browser.py CHANGED
@@ -1,16 +1,75 @@
1
  import subprocess
2
-
3
- subprocess.run(["bash", "scripts.sh"])
4
-
5
  from smolagents.tools import Tool
6
  from langchain_community.tools.playwright.utils import (
7
  create_async_playwright_browser,
8
  create_sync_playwright_browser
9
-
10
  )
11
  from langchain_community.agent_toolkits import PlayWrightBrowserToolkit
12
 
13
- async_browser = create_async_playwright_browser()
14
- sync_browser = create_sync_playwright_browser()
15
- browser_toolkit = PlayWrightBrowserToolkit.from_browser(async_browser=async_browser, sync_browser=sync_browser)
16
- browser_tools = [Tool.from_langchain(tool) for tool in browser_toolkit.get_tools()]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import subprocess
2
+ from contextlib import contextmanager
3
+ from typing import List, Optional
4
+ from threading import Lock
5
  from smolagents.tools import Tool
6
  from langchain_community.tools.playwright.utils import (
7
  create_async_playwright_browser,
8
  create_sync_playwright_browser
 
9
  )
10
  from langchain_community.agent_toolkits import PlayWrightBrowserToolkit
11
 
12
+ class BrowserManager:
13
+ _instance = None
14
+ _lock = Lock()
15
+ _browser_tools: Optional[List[Tool]] = None
16
+
17
+ def __new__(cls):
18
+ if cls._instance is None:
19
+ with cls._lock:
20
+ if cls._instance is None:
21
+ cls._instance = super(BrowserManager, cls).__new__(cls)
22
+ return cls._instance
23
+
24
+ def __init__(self):
25
+ if not hasattr(self, 'initialized'):
26
+ # Run setup script
27
+ subprocess.run(["bash", "scripts.sh"])
28
+ self.initialized = True
29
+
30
+ @contextmanager
31
+ def get_browser_tools(self):
32
+ """Get browser tools in a context that ensures proper cleanup"""
33
+ try:
34
+ if self._browser_tools is None:
35
+ with self._lock:
36
+ if self._browser_tools is None:
37
+ # Create browsers in the current context
38
+ async_browser = create_async_playwright_browser()
39
+ sync_browser = create_sync_playwright_browser()
40
+
41
+ # Create toolkit and tools
42
+ browser_toolkit = PlayWrightBrowserToolkit.from_browser(
43
+ async_browser=async_browser,
44
+ sync_browser=sync_browser
45
+ )
46
+ self._browser_tools = [
47
+ Tool.from_langchain(tool)
48
+ for tool in browser_toolkit.get_tools()
49
+ ]
50
+
51
+ yield self._browser_tools
52
+
53
+ except Exception as e:
54
+ print(f"Error in browser context: {e}")
55
+ # Reset tools on error
56
+ self._browser_tools = None
57
+ raise
58
+ finally:
59
+ # Cleanup if needed
60
+ if self._browser_tools:
61
+ for tool in self._browser_tools:
62
+ if hasattr(tool, 'browser'):
63
+ try:
64
+ tool.browser.close()
65
+ except:
66
+ pass
67
+ self._browser_tools = None
68
+
69
+ # Create a singleton instance
70
+ browser_manager = BrowserManager()
71
+
72
+ # For backward compatibility, but prefer using browser_manager.get_browser_tools()
73
+ def get_browser_tools():
74
+ """Get browser tools (use with context manager)"""
75
+ return browser_manager.get_browser_tools()
mini_agents.py CHANGED
@@ -5,10 +5,12 @@ from tools import to_dataframe, to_json, get_dataframe_data, get_dataframe_colum
5
  from vlm_tools import image_processing, object_detection_tool, ocr_scan_tool, extract_images_from_video, get_image_from_file_path, get_video_from_file_path
6
  from audio_tools import transcribe_audio_tool, get_audio_from_file_path, noise_reduction, audio_segmentation, speaker_diarization
7
  from community_tools import community_tools, get_youtube_transcript_from_url
8
- from browser import browser_tools
9
  import os
10
  import logging
11
  import yaml
 
 
12
 
13
  logging.basicConfig(level=logging.DEBUG)
14
 
@@ -141,19 +143,58 @@ master_model = InferenceClientModel(
141
  token=os.getenv("HUGGINGFACE_API_KEY")
142
  )
143
 
144
- master_agent = CodeAgent(
145
- model=master_model,
146
- managed_agents=[audio_agent, vlm_agent, arithmetic_agent, pandas_agent],
147
- tools=[sort_list, get_youtube_transcript_from_url, read_python_file_from_path, *community_tools, *browser_tools, tavily_search_tool],
148
- add_base_tools=True,
149
- max_steps=20,
150
- additional_authorized_imports=AUTHORIZED_IMPORTS,
151
- verbosity_level=logging.INFO,
152
- planning_interval=4,
153
- prompt_templates=PROMPT_TEMPLATE["master_agent"],
154
- name="master_agent",
155
- description="This agent is responsible for managing audio, vlm, arithmetic and pandas agents."
156
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
  #TESTING 5
159
 
 
5
  from vlm_tools import image_processing, object_detection_tool, ocr_scan_tool, extract_images_from_video, get_image_from_file_path, get_video_from_file_path
6
  from audio_tools import transcribe_audio_tool, get_audio_from_file_path, noise_reduction, audio_segmentation, speaker_diarization
7
  from community_tools import community_tools, get_youtube_transcript_from_url
8
+ from browser import browser_manager
9
  import os
10
  import logging
11
  import yaml
12
+ from typing import List
13
+ from smolagents.tools import Tool
14
 
15
  logging.basicConfig(level=logging.DEBUG)
16
 
 
143
  token=os.getenv("HUGGINGFACE_API_KEY")
144
  )
145
 
146
+ class MasterAgentWrapper:
147
+ """Wrapper class to manage master agent with thread-safe browser tools"""
148
+ def __init__(self):
149
+ self.base_tools = [
150
+ sort_list,
151
+ get_youtube_transcript_from_url,
152
+ read_python_file_from_path,
153
+ *community_tools,
154
+ tavily_search_tool
155
+ ]
156
+
157
+ self.master_agent = CodeAgent(
158
+ model=master_model,
159
+ managed_agents=[audio_agent, vlm_agent, arithmetic_agent, pandas_agent],
160
+ tools=self.base_tools, # Initialize without browser tools
161
+ add_base_tools=True,
162
+ max_steps=20,
163
+ additional_authorized_imports=AUTHORIZED_IMPORTS,
164
+ verbosity_level=logging.INFO,
165
+ planning_interval=4,
166
+ prompt_templates=PROMPT_TEMPLATE["master_agent"],
167
+ name="master_agent",
168
+ description="This agent is responsible for managing audio, vlm, arithmetic and pandas agents."
169
+ )
170
+
171
+ def run(self, question: str) -> str:
172
+ """Run the agent with thread-safe browser tools"""
173
+ try:
174
+ # Get browser tools in the correct context
175
+ with browser_manager.get_browser_tools() as browser_tools:
176
+ # Temporarily add browser tools for this run
177
+ original_tools = self.master_agent.tools
178
+ self.master_agent.tools = original_tools + browser_tools
179
+
180
+ try:
181
+ # Run the agent
182
+ result = self.master_agent.run(question)
183
+ return result
184
+ finally:
185
+ # Restore original tools
186
+ self.master_agent.tools = original_tools
187
+
188
+ except Exception as e:
189
+ logging.error(f"Error in master agent run: {e}")
190
+ raise
191
+
192
+ # Create the wrapped master agent
193
+ master_agent = MasterAgentWrapper()
194
+
195
+ # For backward compatibility
196
+ def run_master_agent(question: str) -> str:
197
+ return master_agent.run(question)
198
 
199
  #TESTING 5
200