adk_browser/
toolset.rs

1//! Browser toolset that provides all browser tools as a collection.
2
3use crate::session::BrowserSession;
4use crate::tools::*;
5use adk_core::{ReadonlyContext, Result, Tool, Toolset};
6use async_trait::async_trait;
7use std::sync::Arc;
8
9/// A toolset that provides all browser automation tools.
10///
11/// Use this to add all browser tools to an agent at once, or use
12/// individual tools for more control.
13pub struct BrowserToolset {
14    browser: Arc<BrowserSession>,
15    /// Include navigation tools (navigate, back, forward, refresh)
16    include_navigation: bool,
17    /// Include interaction tools (click, type, select)
18    include_interaction: bool,
19    /// Include extraction tools (extract text, attributes, links, page info)
20    include_extraction: bool,
21    /// Include wait tools
22    include_wait: bool,
23    /// Include screenshot tool
24    include_screenshot: bool,
25    /// Include JavaScript evaluation tools
26    include_js: bool,
27    /// Include cookie management tools
28    include_cookies: bool,
29    /// Include window/tab management tools
30    include_windows: bool,
31    /// Include frame/iframe management tools
32    include_frames: bool,
33    /// Include advanced action tools (drag-drop, focus, file upload, etc.)
34    include_actions: bool,
35}
36
37impl BrowserToolset {
38    /// Create a new toolset with all tools enabled.
39    pub fn new(browser: Arc<BrowserSession>) -> Self {
40        Self {
41            browser,
42            include_navigation: true,
43            include_interaction: true,
44            include_extraction: true,
45            include_wait: true,
46            include_screenshot: true,
47            include_js: true,
48            include_cookies: true,
49            include_windows: true,
50            include_frames: true,
51            include_actions: true,
52        }
53    }
54
55    /// Enable or disable navigation tools.
56    pub fn with_navigation(mut self, enabled: bool) -> Self {
57        self.include_navigation = enabled;
58        self
59    }
60
61    /// Enable or disable interaction tools.
62    pub fn with_interaction(mut self, enabled: bool) -> Self {
63        self.include_interaction = enabled;
64        self
65    }
66
67    /// Enable or disable extraction tools.
68    pub fn with_extraction(mut self, enabled: bool) -> Self {
69        self.include_extraction = enabled;
70        self
71    }
72
73    /// Enable or disable wait tools.
74    pub fn with_wait(mut self, enabled: bool) -> Self {
75        self.include_wait = enabled;
76        self
77    }
78
79    /// Enable or disable screenshot tool.
80    pub fn with_screenshot(mut self, enabled: bool) -> Self {
81        self.include_screenshot = enabled;
82        self
83    }
84
85    /// Enable or disable JavaScript tools.
86    pub fn with_js(mut self, enabled: bool) -> Self {
87        self.include_js = enabled;
88        self
89    }
90
91    /// Enable or disable cookie management tools.
92    pub fn with_cookies(mut self, enabled: bool) -> Self {
93        self.include_cookies = enabled;
94        self
95    }
96
97    /// Enable or disable window/tab management tools.
98    pub fn with_windows(mut self, enabled: bool) -> Self {
99        self.include_windows = enabled;
100        self
101    }
102
103    /// Enable or disable frame/iframe management tools.
104    pub fn with_frames(mut self, enabled: bool) -> Self {
105        self.include_frames = enabled;
106        self
107    }
108
109    /// Enable or disable advanced action tools.
110    pub fn with_actions(mut self, enabled: bool) -> Self {
111        self.include_actions = enabled;
112        self
113    }
114
115    /// Get all tools as a vector (synchronous version).
116    pub fn all_tools(&self) -> Vec<Arc<dyn Tool>> {
117        let mut tools: Vec<Arc<dyn Tool>> = Vec::new();
118
119        if self.include_navigation {
120            tools.push(Arc::new(NavigateTool::new(self.browser.clone())));
121            tools.push(Arc::new(BackTool::new(self.browser.clone())));
122            tools.push(Arc::new(ForwardTool::new(self.browser.clone())));
123            tools.push(Arc::new(RefreshTool::new(self.browser.clone())));
124        }
125
126        if self.include_interaction {
127            tools.push(Arc::new(ClickTool::new(self.browser.clone())));
128            tools.push(Arc::new(DoubleClickTool::new(self.browser.clone())));
129            tools.push(Arc::new(TypeTool::new(self.browser.clone())));
130            tools.push(Arc::new(ClearTool::new(self.browser.clone())));
131            tools.push(Arc::new(SelectTool::new(self.browser.clone())));
132        }
133
134        if self.include_extraction {
135            tools.push(Arc::new(ExtractTextTool::new(self.browser.clone())));
136            tools.push(Arc::new(ExtractAttributeTool::new(self.browser.clone())));
137            tools.push(Arc::new(ExtractLinksTool::new(self.browser.clone())));
138            tools.push(Arc::new(PageInfoTool::new(self.browser.clone())));
139            tools.push(Arc::new(PageSourceTool::new(self.browser.clone())));
140        }
141
142        if self.include_wait {
143            tools.push(Arc::new(WaitForElementTool::new(self.browser.clone())));
144            tools.push(Arc::new(WaitTool::new()));
145            tools.push(Arc::new(WaitForPageLoadTool::new(self.browser.clone())));
146            tools.push(Arc::new(WaitForTextTool::new(self.browser.clone())));
147        }
148
149        if self.include_screenshot {
150            tools.push(Arc::new(ScreenshotTool::new(self.browser.clone())));
151        }
152
153        if self.include_js {
154            tools.push(Arc::new(EvaluateJsTool::new(self.browser.clone())));
155            tools.push(Arc::new(ScrollTool::new(self.browser.clone())));
156            tools.push(Arc::new(HoverTool::new(self.browser.clone())));
157            tools.push(Arc::new(AlertTool::new(self.browser.clone())));
158        }
159
160        if self.include_cookies {
161            tools.push(Arc::new(GetCookiesTool::new(self.browser.clone())));
162            tools.push(Arc::new(GetCookieTool::new(self.browser.clone())));
163            tools.push(Arc::new(AddCookieTool::new(self.browser.clone())));
164            tools.push(Arc::new(DeleteCookieTool::new(self.browser.clone())));
165            tools.push(Arc::new(DeleteAllCookiesTool::new(self.browser.clone())));
166        }
167
168        if self.include_windows {
169            tools.push(Arc::new(ListWindowsTool::new(self.browser.clone())));
170            tools.push(Arc::new(NewTabTool::new(self.browser.clone())));
171            tools.push(Arc::new(NewWindowTool::new(self.browser.clone())));
172            tools.push(Arc::new(SwitchWindowTool::new(self.browser.clone())));
173            tools.push(Arc::new(CloseWindowTool::new(self.browser.clone())));
174            tools.push(Arc::new(MaximizeWindowTool::new(self.browser.clone())));
175            tools.push(Arc::new(MinimizeWindowTool::new(self.browser.clone())));
176            tools.push(Arc::new(SetWindowSizeTool::new(self.browser.clone())));
177        }
178
179        if self.include_frames {
180            tools.push(Arc::new(SwitchToFrameTool::new(self.browser.clone())));
181            tools.push(Arc::new(SwitchToParentFrameTool::new(self.browser.clone())));
182            tools.push(Arc::new(SwitchToDefaultContentTool::new(self.browser.clone())));
183        }
184
185        if self.include_actions {
186            tools.push(Arc::new(DragAndDropTool::new(self.browser.clone())));
187            tools.push(Arc::new(RightClickTool::new(self.browser.clone())));
188            tools.push(Arc::new(FocusTool::new(self.browser.clone())));
189            tools.push(Arc::new(ElementStateTool::new(self.browser.clone())));
190            tools.push(Arc::new(PressKeyTool::new(self.browser.clone())));
191            tools.push(Arc::new(FileUploadTool::new(self.browser.clone())));
192            tools.push(Arc::new(PrintToPdfTool::new(self.browser.clone())));
193        }
194
195        tools
196    }
197}
198
199#[async_trait]
200impl Toolset for BrowserToolset {
201    fn name(&self) -> &str {
202        "browser"
203    }
204
205    async fn tools(&self, _ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
206        Ok(self.all_tools())
207    }
208}
209
210/// Helper function to create a minimal browser toolset with only essential tools.
211pub fn minimal_browser_tools(browser: Arc<BrowserSession>) -> Vec<Arc<dyn Tool>> {
212    vec![
213        Arc::new(NavigateTool::new(browser.clone())),
214        Arc::new(ClickTool::new(browser.clone())),
215        Arc::new(TypeTool::new(browser.clone())),
216        Arc::new(ExtractTextTool::new(browser.clone())),
217        Arc::new(WaitForElementTool::new(browser.clone())),
218        Arc::new(ScreenshotTool::new(browser)),
219    ]
220}
221
222/// Helper function to create a read-only browser toolset (no interaction).
223pub fn readonly_browser_tools(browser: Arc<BrowserSession>) -> Vec<Arc<dyn Tool>> {
224    vec![
225        Arc::new(NavigateTool::new(browser.clone())),
226        Arc::new(ExtractTextTool::new(browser.clone())),
227        Arc::new(ExtractAttributeTool::new(browser.clone())),
228        Arc::new(ExtractLinksTool::new(browser.clone())),
229        Arc::new(PageInfoTool::new(browser.clone())),
230        Arc::new(ScreenshotTool::new(browser.clone())),
231        Arc::new(ScrollTool::new(browser)),
232    ]
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238    use crate::config::BrowserConfig;
239
240    #[test]
241    fn test_toolset_all_tools() {
242        let browser = Arc::new(BrowserSession::new(BrowserConfig::default()));
243        let toolset = BrowserToolset::new(browser);
244        let tools = toolset.all_tools();
245
246        // Should have 46 tools total
247        assert!(tools.len() > 40);
248
249        // Check some tool names exist
250        let tool_names: Vec<&str> = tools.iter().map(|t| t.name()).collect();
251        assert!(tool_names.contains(&"browser_navigate"));
252        assert!(tool_names.contains(&"browser_click"));
253        assert!(tool_names.contains(&"browser_type"));
254        assert!(tool_names.contains(&"browser_screenshot"));
255        // New tools
256        assert!(tool_names.contains(&"browser_get_cookies"));
257        assert!(tool_names.contains(&"browser_new_tab"));
258        assert!(tool_names.contains(&"browser_switch_to_frame"));
259        assert!(tool_names.contains(&"browser_drag_and_drop"));
260    }
261
262    #[test]
263    fn test_toolset_selective() {
264        let browser = Arc::new(BrowserSession::new(BrowserConfig::default()));
265        let toolset = BrowserToolset::new(browser)
266            .with_navigation(true)
267            .with_interaction(false)
268            .with_extraction(false)
269            .with_wait(false)
270            .with_screenshot(false)
271            .with_js(false)
272            .with_cookies(false)
273            .with_windows(false)
274            .with_frames(false)
275            .with_actions(false);
276
277        let tools = toolset.all_tools();
278
279        // Should only have navigation tools
280        assert_eq!(tools.len(), 4);
281    }
282
283    #[test]
284    fn test_minimal_tools() {
285        let browser = Arc::new(BrowserSession::new(BrowserConfig::default()));
286        let tools = minimal_browser_tools(browser);
287
288        assert_eq!(tools.len(), 6);
289    }
290}