vtcode_core/tools/registry/
dual_output.rs1use anyhow::Result;
4use serde_json::Value;
5use tracing::{debug, warn};
6use vtcode_commons::serde_helpers::json_to_string_pretty;
7
8use crate::config::constants::tools;
9use crate::tools::summarizers::{
10 Summarizer,
11 execution::BashSummarizer,
12 file_ops::{EditSummarizer, ReadSummarizer},
13 search::{GrepSummarizer, ListSummarizer},
14};
15use crate::tools::tool_intent;
16
17use super::{SplitToolResult, ToolRegistry};
18
19impl ToolRegistry {
20 pub async fn execute_tool_dual(&self, name: &str, args: Value) -> Result<SplitToolResult> {
39 let result = self.execute_tool_ref(name, &args).await?;
41
42 let ui_content = if result.is_string() {
44 result.as_str().unwrap_or("").to_string()
45 } else {
46 json_to_string_pretty(&result)
47 };
48
49 let tool_name = if let Some(registration) = self.inventory.registration_for(name) {
52 registration.name().to_string()
53 } else {
54 name.to_string() };
56
57 match tool_name.as_str() {
59 tools::UNIFIED_SEARCH => {
60 match tool_intent::unified_search_action(&args).unwrap_or("grep") {
61 "grep" => {
62 let summarizer = GrepSummarizer::default();
63 match summarizer.summarize(&ui_content, None) {
64 Ok(llm_content) => {
65 debug!(
66 tool = tools::UNIFIED_SEARCH,
67 action = "grep",
68 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
69 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
70 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
71 "Applied grep summarization"
72 );
73 Ok(SplitToolResult::new(
74 tool_name.as_str(),
75 llm_content,
76 ui_content,
77 ))
78 }
79 Err(e) => {
80 warn!(
81 tool = tools::UNIFIED_SEARCH,
82 action = "grep",
83 error = %e,
84 "Failed to summarize grep output, using simple result"
85 );
86 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
87 }
88 }
89 }
90 "list" => {
91 let summarizer = ListSummarizer::default();
92 match summarizer.summarize(&ui_content, None) {
93 Ok(llm_content) => {
94 debug!(
95 tool = tools::UNIFIED_SEARCH,
96 action = "list",
97 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
98 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
99 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
100 "Applied list summarization"
101 );
102 Ok(SplitToolResult::new(
103 tool_name.as_str(),
104 llm_content,
105 ui_content,
106 ))
107 }
108 Err(e) => {
109 warn!(
110 tool = tools::UNIFIED_SEARCH,
111 action = "list",
112 error = %e,
113 "Failed to summarize list output, using simple result"
114 );
115 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
116 }
117 }
118 }
119 _ => Ok(SplitToolResult::simple(tool_name.as_str(), ui_content)),
120 }
121 }
122 tools::UNIFIED_FILE => {
123 match tool_intent::unified_file_action(&args).unwrap_or("read") {
124 "read" => {
125 let mut metadata = args.clone();
126 if let Value::Object(map) = &mut metadata
127 && !map.contains_key("file_path")
128 {
129 let inferred_path = args
130 .get("path")
131 .or_else(|| args.get("file_path"))
132 .or_else(|| args.get("filepath"))
133 .or_else(|| args.get("target_path"))
134 .and_then(Value::as_str)
135 .map(str::to_string);
136 if let Some(path) = inferred_path {
137 map.insert("file_path".to_string(), Value::String(path));
138 }
139 }
140
141 let summarizer = ReadSummarizer::default();
142 match summarizer.summarize(&ui_content, Some(&metadata)) {
143 Ok(llm_content) => {
144 debug!(
145 tool = tools::UNIFIED_FILE,
146 action = "read",
147 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
148 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
149 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
150 "Applied unified_file read summarization"
151 );
152 Ok(SplitToolResult::new(
153 tool_name.as_str(),
154 llm_content,
155 ui_content,
156 ))
157 }
158 Err(e) => {
159 warn!(
160 tool = tools::UNIFIED_FILE,
161 action = "read",
162 error = %e,
163 "Failed to summarize unified_file read output, using simple result"
164 );
165 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
166 }
167 }
168 }
169 "write" | "edit" | "patch" | "move" | "copy" | "delete" => {
170 let summarizer = EditSummarizer::default();
171 match summarizer.summarize(&ui_content, None) {
172 Ok(llm_content) => {
173 debug!(
174 tool = tools::UNIFIED_FILE,
175 action = "mutate",
176 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
177 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
178 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
179 "Applied unified_file mutation summarization"
180 );
181 Ok(SplitToolResult::new(
182 tool_name.as_str(),
183 llm_content,
184 ui_content,
185 ))
186 }
187 Err(e) => {
188 warn!(
189 tool = tools::UNIFIED_FILE,
190 action = "mutate",
191 error = %e,
192 "Failed to summarize unified_file mutation output, using simple result"
193 );
194 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
195 }
196 }
197 }
198 _ => Ok(SplitToolResult::simple(tool_name.as_str(), ui_content)),
199 }
200 }
201 tools::UNIFIED_EXEC => match tool_intent::unified_exec_action(&args).unwrap_or("run") {
202 "run" | "code" => {
203 let summarizer = BashSummarizer::default();
204 let metadata = args.as_object().map(|_| args.clone());
205 match summarizer.summarize(&ui_content, metadata.as_ref()) {
206 Ok(llm_content) => {
207 debug!(
208 tool = tools::UNIFIED_EXEC,
209 action = "run",
210 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
211 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
212 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
213 "Applied unified_exec summarization"
214 );
215 Ok(SplitToolResult::new(
216 tool_name.as_str(),
217 llm_content,
218 ui_content,
219 ))
220 }
221 Err(e) => {
222 warn!(
223 tool = tools::UNIFIED_EXEC,
224 action = "run",
225 error = %e,
226 "Failed to summarize unified_exec output, using simple result"
227 );
228 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
229 }
230 }
231 }
232 _ => Ok(SplitToolResult::simple(tool_name.as_str(), ui_content)),
233 },
234 tools::READ_FILE => {
235 let summarizer = ReadSummarizer::default();
237 let metadata = args.as_object().map(|_| args.clone());
239 match summarizer.summarize(&ui_content, metadata.as_ref()) {
240 Ok(llm_content) => {
241 debug!(
242 tool = tools::READ_FILE,
243 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
244 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
245 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
246 "Applied read file summarization"
247 );
248 Ok(SplitToolResult::new(
249 tool_name.as_str(),
250 llm_content,
251 ui_content,
252 ))
253 }
254 Err(e) => {
255 warn!(
256 tool = tools::READ_FILE,
257 error = %e,
258 "Failed to summarize read output, using simple result"
259 );
260 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
261 }
262 }
263 }
264 tools::RUN_PTY_CMD => {
265 let summarizer = BashSummarizer::default();
267 let metadata = args.as_object().map(|_| args.clone());
269 match summarizer.summarize(&ui_content, metadata.as_ref()) {
270 Ok(llm_content) => {
271 debug!(
272 tool = tools::RUN_PTY_CMD,
273 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
274 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
275 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
276 "Applied bash summarization"
277 );
278 Ok(SplitToolResult::new(
279 tool_name.as_str(),
280 llm_content,
281 ui_content,
282 ))
283 }
284 Err(e) => {
285 warn!(
286 tool = tools::RUN_PTY_CMD,
287 error = %e,
288 "Failed to summarize bash output, using simple result"
289 );
290 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
291 }
292 }
293 }
294 tools::WRITE_FILE | tools::EDIT_FILE | tools::APPLY_PATCH => {
295 let summarizer = EditSummarizer::default();
297 match summarizer.summarize(&ui_content, None) {
298 Ok(llm_content) => {
299 debug!(
300 tool = tool_name.as_str(),
301 ui_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).1,
302 llm_tokens = %summarizer.estimate_savings(&ui_content, &llm_content).0,
303 savings_pct = %summarizer.estimate_savings(&ui_content, &llm_content).2,
304 "Applied edit summarization"
305 );
306 Ok(SplitToolResult::new(
307 tool_name.as_str(),
308 llm_content,
309 ui_content,
310 ))
311 }
312 Err(e) => {
313 warn!(
314 tool = tool_name.as_str(),
315 error = %e,
316 "Failed to summarize edit output, using simple result"
317 );
318 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
319 }
320 }
321 }
322 _ => {
323 Ok(SplitToolResult::simple(tool_name.as_str(), ui_content))
325 }
326 }
327 }
328}