agtrace_providers/gemini/
tools.rs1use agtrace_types::{ExecuteArgs, FileEditArgs, FileReadArgs, FileWriteArgs, SearchArgs};
6use serde::{Deserialize, Serialize};
7use serde_json::json;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct GeminiReadFileArgs {
21 pub file_path: String,
22}
23
24impl GeminiReadFileArgs {
25 pub fn to_file_read_args(&self) -> FileReadArgs {
27 FileReadArgs {
28 file_path: Some(self.file_path.clone()),
29 path: None,
30 pattern: None,
31 extra: json!({}),
32 }
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct GeminiWriteFileArgs {
49 pub content: String,
50 pub file_path: String,
51}
52
53impl GeminiWriteFileArgs {
54 pub fn to_file_write_args(&self) -> FileWriteArgs {
56 FileWriteArgs {
57 file_path: self.file_path.clone(),
58 content: self.content.clone(),
59 }
60 }
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct GeminiReplaceArgs {
79 pub file_path: String,
80 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub instruction: Option<String>,
83 pub old_string: String,
84 pub new_string: String,
85}
86
87impl GeminiReplaceArgs {
88 pub fn to_file_edit_args(&self) -> FileEditArgs {
96 FileEditArgs {
98 file_path: self.file_path.clone(),
99 old_string: self.old_string.clone(),
100 new_string: self.new_string.clone(),
101 replace_all: false, }
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct GeminiRunShellCommandArgs {
119 pub command: String,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub description: Option<String>,
122}
123
124impl GeminiRunShellCommandArgs {
125 pub fn to_execute_args(&self) -> ExecuteArgs {
127 ExecuteArgs {
128 command: Some(self.command.clone()),
129 description: self.description.clone(),
130 timeout: None,
131 extra: json!({}),
132 }
133 }
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct GeminiGoogleWebSearchArgs {
146 pub query: String,
147}
148
149impl GeminiGoogleWebSearchArgs {
150 pub fn to_search_args(&self) -> SearchArgs {
152 SearchArgs {
153 pattern: None,
154 query: Some(self.query.clone()),
155 input: None,
156 path: None,
157 extra: json!({}),
158 }
159 }
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct GeminiWriteTodosArgs {
184 pub todos: Vec<GeminiTodoItem>,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct GeminiTodoItem {
189 pub description: String,
191 pub status: String,
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn test_read_file_args_conversion() {
201 let args = GeminiReadFileArgs {
202 file_path: "src/main.rs".to_string(),
203 };
204
205 let domain_args = args.to_file_read_args();
206 assert_eq!(domain_args.file_path, Some("src/main.rs".to_string()));
207 assert_eq!(domain_args.path, None);
208 assert_eq!(domain_args.pattern, None);
209 assert_eq!(domain_args.extra, json!({}));
210 }
211
212 #[test]
213 fn test_write_file_args_conversion() {
214 let args = GeminiWriteFileArgs {
215 content: "hello world".to_string(),
216 file_path: "test.txt".to_string(),
217 };
218
219 let domain_args = args.to_file_write_args();
220 assert_eq!(domain_args.file_path, "test.txt");
221 assert_eq!(domain_args.content, "hello world");
222 }
223
224 #[test]
225 fn test_replace_args_with_instruction() {
226 let args = GeminiReplaceArgs {
227 file_path: "src/lib.rs".to_string(),
228 instruction: Some("Update import statement".to_string()),
229 old_string: "old code".to_string(),
230 new_string: "new code".to_string(),
231 };
232
233 let domain_args = args.to_file_edit_args();
234 assert_eq!(domain_args.file_path, "src/lib.rs");
235 assert_eq!(domain_args.old_string, "old code");
236 assert_eq!(domain_args.new_string, "new code");
237 assert_eq!(domain_args.replace_all, false);
238 }
240
241 #[test]
242 fn test_replace_args_without_instruction() {
243 let args = GeminiReplaceArgs {
244 file_path: "src/lib.rs".to_string(),
245 instruction: None,
246 old_string: "old".to_string(),
247 new_string: "new".to_string(),
248 };
249
250 let domain_args = args.to_file_edit_args();
251 assert_eq!(domain_args.file_path, "src/lib.rs");
252 assert_eq!(domain_args.old_string, "old");
253 assert_eq!(domain_args.new_string, "new");
254 assert_eq!(domain_args.replace_all, false);
255 }
256
257 #[test]
258 fn test_run_shell_command_args_conversion() {
259 let args = GeminiRunShellCommandArgs {
260 command: "ls -la".to_string(),
261 description: Some("List files".to_string()),
262 };
263
264 let domain_args = args.to_execute_args();
265 assert_eq!(domain_args.command, Some("ls -la".to_string()));
266 assert_eq!(domain_args.description, Some("List files".to_string()));
267 assert_eq!(domain_args.timeout, None);
268 assert_eq!(domain_args.extra, json!({}));
269 }
270
271 #[test]
272 fn test_run_shell_command_args_without_description() {
273 let args = GeminiRunShellCommandArgs {
274 command: "pwd".to_string(),
275 description: None,
276 };
277
278 let domain_args = args.to_execute_args();
279 assert_eq!(domain_args.command, Some("pwd".to_string()));
280 assert_eq!(domain_args.description, None);
281 }
282
283 #[test]
284 fn test_google_web_search_args_conversion() {
285 let args = GeminiGoogleWebSearchArgs {
286 query: "rust async".to_string(),
287 };
288
289 let domain_args = args.to_search_args();
290 assert_eq!(domain_args.query, Some("rust async".to_string()));
291 assert_eq!(domain_args.extra, json!({}));
292 }
293
294 #[test]
295 fn test_write_todos_args_parsing() {
296 let json_value = json!({
297 "todos": [
298 {
299 "description": "Create cli directory",
300 "status": "pending"
301 },
302 {
303 "description": "Move import logic",
304 "status": "in_progress"
305 }
306 ]
307 });
308
309 let args: GeminiWriteTodosArgs = serde_json::from_value(json_value).unwrap();
310 assert_eq!(args.todos.len(), 2);
311 assert_eq!(args.todos[0].description, "Create cli directory");
312 assert_eq!(args.todos[0].status, "pending");
313 assert_eq!(args.todos[1].description, "Move import logic");
314 assert_eq!(args.todos[1].status, "in_progress");
315 }
316}