watsonx_rs/orchestrate/
tool.rs

1//! Tool management operations
2
3use crate::error::{Error, Result};
4use super::types::{Tool, ToolExecutionRequest, ToolExecutionResult, ToolUpdateRequest, ToolTestRequest, ToolTestResult, ToolExecutionHistory, ToolVersion};
5use super::OrchestrateClient;
6
7impl OrchestrateClient {
8    /// List all tools
9    pub async fn list_tools(&self) -> Result<Vec<Tool>> {
10        let api_key = self.access_token.as_ref().ok_or_else(|| {
11            Error::Authentication("Not authenticated. Set access token (API key) first.".to_string())
12        })?;
13
14        let base_url = self.config.get_base_url();
15        let url = format!("{}/tools", base_url);
16
17        let response = self
18            .client
19            .get(&url)
20            .header("Authorization", format!("Bearer {}", api_key))
21            .header("Content-Type", "application/json")
22            .send()
23            .await
24            .map_err(|e| Error::Network(e.to_string()))?;
25
26        if !response.status().is_success() {
27            let status = response.status();
28            let error_text = response
29                .text()
30                .await
31                .unwrap_or_else(|_| "Unknown error".to_string());
32            return Err(Error::Api(format!(
33                "Failed to list tools: {} - {}",
34                status, error_text
35            )));
36        }
37
38        let text = response
39            .text()
40            .await
41            .map_err(|e| Error::Serialization(e.to_string()))?;
42
43        if let Ok(tools) = serde_json::from_str::<Vec<Tool>>(&text) {
44            return Ok(tools);
45        }
46
47        if let Ok(obj) = serde_json::from_str::<serde_json::Value>(&text) {
48            if let Some(tools_array) = obj.get("tools").and_then(|t| t.as_array()) {
49                let tools: Result<Vec<Tool>> = tools_array
50                    .iter()
51                    .map(|tool| {
52                        serde_json::from_value::<Tool>(tool.clone())
53                            .map_err(|e| Error::Serialization(e.to_string()))
54                    })
55                    .collect();
56                return tools;
57            }
58        }
59
60        Ok(Vec::new())
61    }
62
63    /// Get a specific tool by ID
64    pub async fn get_tool(&self, tool_id: &str) -> Result<Tool> {
65        let api_key = self.access_token.as_ref().ok_or_else(|| {
66            Error::Authentication("Not authenticated. Set access token (API key) first.".to_string())
67        })?;
68
69        let base_url = self.config.get_base_url();
70        let url = format!("{}/tools/{}", base_url, tool_id);
71
72        let response = self
73            .client
74            .get(&url)
75            .header("Authorization", format!("Bearer {}", api_key))
76            .header("Content-Type", "application/json")
77            .send()
78            .await
79            .map_err(|e| Error::Network(e.to_string()))?;
80
81        if !response.status().is_success() {
82            let status = response.status();
83            let error_text = response
84                .text()
85                .await
86                .unwrap_or_else(|_| "Unknown error".to_string());
87            return Err(Error::Api(format!(
88                "Failed to get tool {}: {} - {}",
89                tool_id, status, error_text
90            )));
91        }
92
93        let tool: Tool = response
94            .json()
95            .await
96            .map_err(|e| Error::Serialization(e.to_string()))?;
97
98        Ok(tool)
99    }
100
101    /// Execute a tool directly
102    pub async fn execute_tool(&self, request: ToolExecutionRequest) -> Result<ToolExecutionResult> {
103        let api_key = self.access_token.as_ref().ok_or_else(|| {
104            Error::Authentication("Not authenticated. Set access token (API key) first.".to_string())
105        })?;
106
107        let base_url = self.config.get_base_url();
108        let url = format!("{}/tools/{}/execute", base_url, request.tool_id);
109
110        let response = self
111            .client
112            .post(&url)
113            .header("Authorization", format!("Bearer {}", api_key))
114            .header("Content-Type", "application/json")
115            .json(&request)
116            .send()
117            .await
118            .map_err(|e| Error::Network(e.to_string()))?;
119
120        if !response.status().is_success() {
121            let status = response.status();
122            let error_text = response
123                .text()
124                .await
125                .unwrap_or_else(|_| "Unknown error".to_string());
126            return Err(Error::Api(format!(
127                "Failed to execute tool: {} - {}",
128                status, error_text
129            )));
130        }
131
132        let result: ToolExecutionResult = response
133            .json()
134            .await
135            .map_err(|e| Error::Serialization(e.to_string()))?;
136
137        Ok(result)
138    }
139
140    /// Update a tool
141    pub async fn update_tool(&self, tool_id: &str, request: ToolUpdateRequest) -> Result<Tool> {
142        let token = self.access_token.as_ref().ok_or_else(|| {
143            Error::Authentication("Not authenticated. Set access token (Bearer token) first.".to_string())
144        })?;
145
146        let base_url = self.config.get_base_url();
147        let url = format!("{}/orchestrate/tools/{}", base_url, tool_id);
148
149        let response = self
150            .client
151            .patch(&url)
152            .header("Authorization", format!("Bearer {}", token))
153            .header("Content-Type", "application/json")
154            .header("X-Instance-ID", &self.config.instance_id)
155            .json(&request)
156            .send()
157            .await
158            .map_err(|e| Error::Network(e.to_string()))?;
159
160        if !response.status().is_success() {
161            let status = response.status();
162            let error_text = response
163                .text()
164                .await
165                .unwrap_or_else(|_| "Unknown error".to_string());
166            return Err(Error::Api(format!(
167                "Failed to update tool: {} - {}",
168                status, error_text
169            )));
170        }
171
172        let tool: Tool = response
173            .json()
174            .await
175            .map_err(|e| Error::Serialization(e.to_string()))?;
176
177        Ok(tool)
178    }
179
180    /// Delete a tool
181    pub async fn delete_tool(&self, tool_id: &str) -> Result<()> {
182        let token = self.access_token.as_ref().ok_or_else(|| {
183            Error::Authentication("Not authenticated. Set access token (Bearer token) first.".to_string())
184        })?;
185
186        let base_url = self.config.get_base_url();
187        let url = format!("{}/orchestrate/tools/{}", base_url, tool_id);
188
189        let response = self
190            .client
191            .delete(&url)
192            .header("Authorization", format!("Bearer {}", token))
193            .header("X-Instance-ID", &self.config.instance_id)
194            .send()
195            .await
196            .map_err(|e| Error::Network(e.to_string()))?;
197
198        if !response.status().is_success() {
199            let status = response.status();
200            let error_text = response
201                .text()
202                .await
203                .unwrap_or_else(|_| "Unknown error".to_string());
204            return Err(Error::Api(format!(
205                "Failed to delete tool: {} - {}",
206                status, error_text
207            )));
208        }
209
210        Ok(())
211    }
212
213    /// Test a tool with sample input
214    pub async fn test_tool(&self, request: ToolTestRequest) -> Result<ToolTestResult> {
215        let token = self.access_token.as_ref().ok_or_else(|| {
216            Error::Authentication("Not authenticated. Set access token (Bearer token) first.".to_string())
217        })?;
218
219        let base_url = self.config.get_base_url();
220        let url = format!("{}/orchestrate/tools/{}/test", base_url, request.tool_id);
221
222        let response = self
223            .client
224            .post(&url)
225            .header("Authorization", format!("Bearer {}", token))
226            .header("Content-Type", "application/json")
227            .header("X-Instance-ID", &self.config.instance_id)
228            .json(&request)
229            .send()
230            .await
231            .map_err(|e| Error::Network(e.to_string()))?;
232
233        if !response.status().is_success() {
234            let status = response.status();
235            let error_text = response
236                .text()
237                .await
238                .unwrap_or_else(|_| "Unknown error".to_string());
239            return Err(Error::Api(format!(
240                "Failed to test tool: {} - {}",
241                status, error_text
242            )));
243        }
244
245        let result: ToolTestResult = response
246            .json()
247            .await
248            .map_err(|e| Error::Serialization(e.to_string()))?;
249
250        Ok(result)
251    }
252
253    /// Get tool execution history
254    pub async fn get_tool_execution_history(&self, tool_id: &str, limit: Option<u32>) -> Result<Vec<ToolExecutionHistory>> {
255        let token = self.access_token.as_ref().ok_or_else(|| {
256            Error::Authentication("Not authenticated. Set access token (Bearer token) first.".to_string())
257        })?;
258
259        let base_url = self.config.get_base_url();
260        let mut url = format!("{}/orchestrate/tools/{}/execution-history", base_url, tool_id);
261        
262        if let Some(l) = limit {
263            url.push_str(&format!("?limit={}", l));
264        }
265
266        let response = self
267            .client
268            .get(&url)
269            .header("Authorization", format!("Bearer {}", token))
270            .header("Content-Type", "application/json")
271            .header("X-Instance-ID", &self.config.instance_id)
272            .send()
273            .await
274            .map_err(|e| Error::Network(e.to_string()))?;
275
276        if !response.status().is_success() {
277            let status = response.status();
278            let error_text = response
279                .text()
280                .await
281                .unwrap_or_else(|_| "Unknown error".to_string());
282            return Err(Error::Api(format!(
283                "Failed to get tool execution history: {} - {}",
284                status, error_text
285            )));
286        }
287
288        let text = response
289            .text()
290            .await
291            .map_err(|e| Error::Network(e.to_string()))?;
292
293        if let Ok(history) = serde_json::from_str::<Vec<ToolExecutionHistory>>(&text) {
294            return Ok(history);
295        }
296
297        if let Ok(obj) = serde_json::from_str::<serde_json::Value>(&text) {
298            if let Some(history_array) = obj.get("history").and_then(|h| h.as_array()) {
299                let history: Result<Vec<ToolExecutionHistory>> = history_array
300                    .iter()
301                    .map(|item| {
302                        serde_json::from_value::<ToolExecutionHistory>(item.clone())
303                            .map_err(|e| Error::Serialization(e.to_string()))
304                    })
305                    .collect();
306                return history;
307            }
308        }
309
310        Ok(Vec::new())
311    }
312
313    /// Get tool versions
314    pub async fn get_tool_versions(&self, tool_id: &str) -> Result<Vec<ToolVersion>> {
315        let token = self.access_token.as_ref().ok_or_else(|| {
316            Error::Authentication("Not authenticated. Set access token (Bearer token) first.".to_string())
317        })?;
318
319        let base_url = self.config.get_base_url();
320        let url = format!("{}/orchestrate/tools/{}/versions", base_url, tool_id);
321
322        let response = self
323            .client
324            .get(&url)
325            .header("Authorization", format!("Bearer {}", token))
326            .header("Content-Type", "application/json")
327            .header("X-Instance-ID", &self.config.instance_id)
328            .send()
329            .await
330            .map_err(|e| Error::Network(e.to_string()))?;
331
332        if !response.status().is_success() {
333            let status = response.status();
334            let error_text = response
335                .text()
336                .await
337                .unwrap_or_else(|_| "Unknown error".to_string());
338            return Err(Error::Api(format!(
339                "Failed to get tool versions: {} - {}",
340                status, error_text
341            )));
342        }
343
344        let text = response
345            .text()
346            .await
347            .map_err(|e| Error::Network(e.to_string()))?;
348
349        if let Ok(versions) = serde_json::from_str::<Vec<ToolVersion>>(&text) {
350            return Ok(versions);
351        }
352
353        if let Ok(obj) = serde_json::from_str::<serde_json::Value>(&text) {
354            if let Some(versions_array) = obj.get("versions").and_then(|v| v.as_array()) {
355                let versions: Result<Vec<ToolVersion>> = versions_array
356                    .iter()
357                    .map(|item| {
358                        serde_json::from_value::<ToolVersion>(item.clone())
359                            .map_err(|e| Error::Serialization(e.to_string()))
360                    })
361                    .collect();
362                return versions;
363            }
364        }
365
366        Ok(Vec::new())
367    }
368}