deepseek_rust_cli/tools/
web_tools.rs1use std::{collections::HashMap, path::Path};
2
3use anyhow::Result;
4use async_trait::async_trait;
5use serde_json::Value;
6
7use crate::{agent::types::UndoAction, tools, tools::base::Tool};
8
9pub struct RunPythonTool;
10#[async_trait]
11impl Tool for RunPythonTool {
12 fn name(&self) -> &str {
13 "run_python_code"
14 }
15 async fn execute(
16 &self,
17 args: &HashMap<String, Value>,
18 _undo: &mut Vec<UndoAction>,
19 _cwd: Option<&Path>,
20 ) -> Result<String> {
21 let code = args
22 .get("code")
23 .and_then(|v| v.as_str())
24 .ok_or_else(|| anyhow::anyhow!("Missing 'code'"))?;
25 tools::code_ops::run_python_code(code).await
26 }
27}
28
29pub struct FetchUrlTool;
30#[async_trait]
31impl Tool for FetchUrlTool {
32 fn name(&self) -> &str {
33 "fetch_url"
34 }
35 async fn execute(
36 &self,
37 args: &HashMap<String, Value>,
38 _undo: &mut Vec<UndoAction>,
39 _cwd: Option<&Path>,
40 ) -> Result<String> {
41 let url = args
42 .get("url")
43 .and_then(|v| v.as_str())
44 .ok_or_else(|| anyhow::anyhow!("Missing 'url'"))?;
45 tools::web_ops::fetch_url(url).await
46 }
47}
48
49pub struct GetEnvVarTool;
50#[async_trait]
51impl Tool for GetEnvVarTool {
52 fn name(&self) -> &str {
53 "get_env_var"
54 }
55 async fn execute(
56 &self,
57 args: &HashMap<String, Value>,
58 _undo: &mut Vec<UndoAction>,
59 _cwd: Option<&Path>,
60 ) -> Result<String> {
61 let name = args
62 .get("name")
63 .and_then(|v| v.as_str())
64 .ok_or_else(|| anyhow::anyhow!("Missing 'name'"))?;
65 Ok(tools::web_ops::get_env_var(name))
66 }
67}
68
69pub struct ScreenshotWebappTool;
70#[async_trait]
71impl Tool for ScreenshotWebappTool {
72 fn name(&self) -> &str {
73 "screenshot_webapp"
74 }
75 async fn execute(
76 &self,
77 args: &HashMap<String, Value>,
78 _undo: &mut Vec<UndoAction>,
79 _cwd: Option<&Path>,
80 ) -> Result<String> {
81 let url = args
82 .get("url")
83 .and_then(|v| v.as_str())
84 .ok_or_else(|| anyhow::anyhow!("Missing 'url'"))?;
85 let output_path = args
86 .get("output_path")
87 .and_then(|v| v.as_str())
88 .ok_or_else(|| anyhow::anyhow!("Missing 'output_path'"))?;
89
90 let p = crate::tools::base::validate_path(output_path)?;
91 if let Some(parent) = p.parent() {
92 tokio::fs::create_dir_all(parent).await?;
93 }
94 let p_str = p.to_string_lossy().to_string();
95
96 let mut cmd = tokio::process::Command::new("msedge");
97 cmd.arg("--headless")
98 .arg("--disable-gpu")
99 .arg(format!("--screenshot={}", p_str))
100 .arg(url);
101
102 let res = cmd.output().await;
103 match res {
104 Ok(output) if output.status.success() => Ok(format!("Screenshot saved to {}", p_str)),
105 _ => {
106 let mut cmd2 = tokio::process::Command::new("chrome");
107 cmd2.arg("--headless")
108 .arg("--disable-gpu")
109 .arg(format!("--screenshot={}", p_str))
110 .arg(url);
111 match cmd2.output().await {
112 Ok(output) if output.status.success() => {
113 Ok(format!("Screenshot saved to {}", p_str))
114 }
115 Ok(output) => Err(anyhow::anyhow!(
116 "Browser exited with error: {}",
117 String::from_utf8_lossy(&output.stderr)
118 )),
119 Err(e) => Err(anyhow::anyhow!(
120 "Failed to start msedge or chrome. Make sure at least one is installed \
121 and in your PATH. Error: {}",
122 e
123 )),
124 }
125 }
126 }
127 }
128}
129
130pub struct WebSearchDuckDuckGoTool;
131#[async_trait]
132impl Tool for WebSearchDuckDuckGoTool {
133 fn name(&self) -> &str {
134 "web_search_duckduckgo"
135 }
136 async fn execute(
137 &self,
138 args: &HashMap<String, Value>,
139 _undo: &mut Vec<UndoAction>,
140 _cwd: Option<&Path>,
141 ) -> Result<String> {
142 let query = args
143 .get("query")
144 .and_then(|v| v.as_str())
145 .ok_or_else(|| anyhow::anyhow!("Missing 'query'"))?;
146 tools::web_ops::web_search_duckduckgo(query).await
147 }
148}