1use async_trait::async_trait;
4use cortexai_core::{errors::ToolError, ExecutionContext, Tool, ToolSchema};
5use serde_json::json;
6
7pub struct WebSearchTool;
9
10impl Default for WebSearchTool {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl WebSearchTool {
17 pub fn new() -> Self {
18 Self
19 }
20}
21
22#[async_trait]
23impl Tool for WebSearchTool {
24 fn schema(&self) -> ToolSchema {
25 ToolSchema::new("web_search", "Search the web for information").with_parameters(json!({
26 "type": "object",
27 "properties": {
28 "query": {
29 "type": "string",
30 "description": "Search query"
31 },
32 "max_results": {
33 "type": "integer",
34 "description": "Maximum number of results",
35 "default": 5
36 }
37 },
38 "required": ["query"]
39 }))
40 }
41
42 async fn execute(
43 &self,
44 _context: &ExecutionContext,
45 arguments: serde_json::Value,
46 ) -> Result<serde_json::Value, ToolError> {
47 let query = arguments["query"]
48 .as_str()
49 .ok_or_else(|| ToolError::InvalidArguments("Missing 'query' field".to_string()))?;
50
51 Ok(json!({
53 "query": query,
54 "results": [
55 {
56 "title": "Example Result 1",
57 "url": "https://example.com/1",
58 "snippet": "This is a sample search result..."
59 }
60 ],
61 "note": "Web search not fully implemented - this is mock data"
62 }))
63 }
64}
65
66pub struct HttpRequestTool;
68
69impl Default for HttpRequestTool {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl HttpRequestTool {
76 pub fn new() -> Self {
77 Self
78 }
79}
80
81#[async_trait]
82impl Tool for HttpRequestTool {
83 fn schema(&self) -> ToolSchema {
84 ToolSchema::new("http_request", "Make an HTTP request to a URL")
85 .with_parameters(json!({
86 "type": "object",
87 "properties": {
88 "url": {
89 "type": "string",
90 "description": "URL to request"
91 },
92 "method": {
93 "type": "string",
94 "enum": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"],
95 "default": "GET"
96 },
97 "headers": {
98 "type": "object",
99 "description": "Request headers"
100 },
101 "body": {
102 "type": "string",
103 "description": "Request body (for POST/PUT)"
104 }
105 },
106 "required": ["url"]
107 }))
108 .with_dangerous(true)
109 }
110
111 async fn execute(
112 &self,
113 _context: &ExecutionContext,
114 arguments: serde_json::Value,
115 ) -> Result<serde_json::Value, ToolError> {
116 let url = arguments["url"]
117 .as_str()
118 .ok_or_else(|| ToolError::InvalidArguments("Missing 'url' field".to_string()))?;
119
120 let method = arguments["method"].as_str().unwrap_or("GET").to_uppercase();
121 let body_content = arguments["body"].as_str().unwrap_or("").to_string();
122
123 let client = reqwest::Client::new();
125 let mut request_builder = match method.as_str() {
126 "GET" => client.get(url),
127 "POST" => client.post(url).body(body_content),
128 "PUT" => client.put(url).body(body_content),
129 "DELETE" => client.delete(url),
130 "PATCH" => client.patch(url).body(body_content),
131 "HEAD" => client.head(url),
132 _ => {
133 return Err(ToolError::InvalidArguments(format!(
134 "Unsupported method: {}",
135 method
136 )))
137 }
138 };
139
140 if let Some(headers) = arguments["headers"].as_object() {
142 for (key, value) in headers {
143 if let Some(header_value) = value.as_str() {
144 request_builder = request_builder.header(key.as_str(), header_value);
145 }
146 }
147 }
148
149 let response = request_builder.send().await;
151
152 match response {
153 Ok(resp) => {
154 let status = resp.status().as_u16();
155 let response_headers: serde_json::Map<String, serde_json::Value> = resp
156 .headers()
157 .iter()
158 .filter_map(|(k, v)| v.to_str().ok().map(|val| (k.to_string(), json!(val))))
159 .collect();
160 let body = resp.text().await.unwrap_or_default();
161
162 Ok(json!({
163 "status": status,
164 "headers": response_headers,
165 "body": body,
166 "success": (200..300).contains(&status)
167 }))
168 }
169 Err(e) => Err(ToolError::ExecutionFailed(e.to_string())),
170 }
171 }
172}