From b395c52fd6fed8a916880882367d452bf8c08c44 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 11:21:50 +0200 Subject: [PATCH 01/90] move ast_definition description from yaml to trait impl --- .../engine/src/tools/tool_ast_definition.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 9 --------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_ast_definition.rs b/refact-agent/engine/src/tools/tool_ast_definition.rs index af512562f..76a6001b8 100644 --- a/refact-agent/engine/src/tools/tool_ast_definition.rs +++ b/refact-agent/engine/src/tools/tool_ast_definition.rs @@ -7,7 +7,7 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::ast::ast_structs::AstDB; use crate::ast::ast_db::fetch_counters; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; pub struct ToolAstDefinition; @@ -105,6 +105,23 @@ impl Tool for ToolAstDefinition { } } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "search_symbol_definition".to_string(), + agentic: false, + experimental: false, + description: "Find definition of a symbol in the project using AST".to_string(), + parameters: vec![ + ToolParam { + name: "symbols".to_string(), + description: "Comma-separated list of symbols to search for (functions, methods, classes, type aliases). No spaces allowed in symbol names.".to_string(), + param_type: "string".to_string(), + }, + ], + parameters_required: vec!["symbols".to_string()], + } + } + fn tool_depends_on(&self) -> Vec { vec!["ast".to_string()] } diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 45e476e33..204d234d2 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -195,15 +195,6 @@ tools: - "queries" - "scope" - - name: "search_symbol_definition" - description: "Find definition of a symbol in the project using AST" - parameters: - - name: "symbols" - type: "string" - description: "Comma-separated list of symbols to search for (functions, methods, classes, type aliases). No spaces allowed in symbol names." - parameters_required: - - "symbols" - - name: "search_symbol_usages" description: "Find usages of a symbol within a project using AST" parameters: From 944238780fead23865774ab9180c0023169722e3 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 11:23:37 +0200 Subject: [PATCH 02/90] move ast_references description from yaml to trait impl --- .../engine/src/tools/tool_ast_reference.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 9 --------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_ast_reference.rs b/refact-agent/engine/src/tools/tool_ast_reference.rs index 5a9de4304..c9c0b06a9 100644 --- a/refact-agent/engine/src/tools/tool_ast_reference.rs +++ b/refact-agent/engine/src/tools/tool_ast_reference.rs @@ -6,7 +6,7 @@ use serde_json::Value; use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; use crate::tools::tool_ast_definition::there_are_definitions_with_similar_names_though; @@ -129,6 +129,23 @@ impl Tool for ToolAstReference { } } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "search_symbol_usages".to_string(), + agentic: false, + experimental: false, + description: "Find usages of a symbol within a project using AST".to_string(), + parameters: vec![ + ToolParam { + name: "symbols".to_string(), + description: "Comma-separated list of symbols to search for (functions, methods, classes, type aliases). No spaces allowed in symbol names.".to_string(), + param_type: "string".to_string(), + } + ], + parameters_required: vec!["symbols".to_string()], + } + } + fn tool_depends_on(&self) -> Vec { vec!["ast".to_string()] } diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 204d234d2..1ee8edb54 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -195,15 +195,6 @@ tools: - "queries" - "scope" - - name: "search_symbol_usages" - description: "Find usages of a symbol within a project using AST" - parameters: - - name: "symbols" - type: "string" - description: "Comma-separated list of symbols to search for (functions, methods, classes, type aliases). No spaces allowed in symbol names." - parameters_required: - - "symbols" - - name: "tree" description: "Get a files tree with symbols for the project. Use it to get familiar with the project, file names and symbols" parameters: From 7991e5dfb34db514e13b0f4f060c394f032a7ad6 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:05:27 +0200 Subject: [PATCH 03/90] force tools to implement tool_description --- refact-agent/engine/src/tools/tools_description.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 1ee8edb54..ecdf97101 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -41,6 +41,8 @@ pub trait Tool: Send + Sync { args: &HashMap ) -> Result<(bool, Vec), String>; + fn tool_description(&self) -> ToolDesc; + async fn match_against_confirm_deny( &self, ccx: Arc>, @@ -109,10 +111,6 @@ pub trait Tool: Send + Sync { fn tool_name(&self) -> String { return "".to_string(); } - - fn tool_description(&self) -> ToolDesc { - unimplemented!(); - } } pub async fn tools_merged_and_filtered( From b78d30d4b59a9f0005104452798ee70b72cf120d Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 11:28:27 +0200 Subject: [PATCH 04/90] move tools description yaml to the bottom of the file --- .../engine/src/tools/tools_description.rs | 275 +++++++++--------- 1 file changed, 137 insertions(+), 138 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index ecdf97101..f7c895fe1 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -178,6 +178,143 @@ pub async fn tools_merged_and_filtered( Ok(filtered_tools) } +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct ToolDesc { + pub name: String, + #[serde(default)] + pub agentic: bool, + #[serde(default)] + pub experimental: bool, + pub description: String, + pub parameters: Vec, + pub parameters_required: Vec, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct ToolParam { + #[serde(deserialize_with = "validate_snake_case")] + pub name: String, + #[serde(rename = "type", default = "default_param_type")] + pub param_type: String, + pub description: String, +} + +fn validate_snake_case<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + if !s.chars().next().map_or(false, |c| c.is_ascii_lowercase()) + || !s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_') + || s.contains("__") + || s.ends_with('_') + { + return Err(serde::de::Error::custom( + format!("name {:?} must be in snake_case format: lowercase letters, numbers and single underscores, must start with letter", s) + )); + } + Ok(s) +} + +fn default_param_type() -> String { + "string".to_string() +} + +/// TODO: Think a better way to know if we can send array type to the model +/// +/// For now, anthropic models support it, gpt models don't, for other, we'll need to test +pub fn model_supports_array_param_type(model_id: &str) -> bool { + model_id.contains("claude") +} + +pub fn make_openai_tool_value( + name: String, + agentic: bool, + description: String, + parameters_required: Vec, + parameters: Vec, +) -> Value { + let params_properties = parameters.iter().map(|param| { + ( + param.name.clone(), + json!({ + "type": param.param_type, + "description": param.description + }) + ) + }).collect::>(); + + let function_json = json!({ + "type": "function", + "function": { + "name": name, + "agentic": agentic, // this field is not OpenAI's + "description": description, + "parameters": { + "type": "object", + "properties": params_properties, + "required": parameters_required + } + } + }); + function_json +} + +impl ToolDesc { + pub fn into_openai_style(self) -> Value { + make_openai_tool_value( + self.name, + self.agentic, + self.description, + self.parameters_required, + self.parameters, + ) + } + + pub fn is_supported_by(&self, model: &str) -> bool { + if !model_supports_array_param_type(model) { + for param in &self.parameters { + if param.param_type == "array" { + tracing::warn!("Tool {} has array parameter, but model {} does not support it", self.name, model); + return false; + } + } + } + true + } +} + +#[derive(Deserialize)] +pub struct ToolDictDeserialize { + pub tools: Vec, +} + +pub async fn tool_description_list_from_yaml( + tools: IndexMap>, + turned_on: Option<&Vec>, + allow_experimental: bool, +) -> Result, String> { + let tool_desc_deser: ToolDictDeserialize = serde_yaml::from_str(BUILT_IN_TOOLS) + .map_err(|e|format!("Failed to parse BUILT_IN_TOOLS: {}", e))?; + + let mut tool_desc_vec = vec![]; + tool_desc_vec.extend(tool_desc_deser.tools.iter().cloned()); + + for (tool_name, tool) in tools { + if !tool_desc_vec.iter().any(|desc| desc.name == tool_name) { + tool_desc_vec.push(tool.tool_description()); + } + } + + Ok(tool_desc_vec.iter() + .filter(|x| { + turned_on.map_or(true, |turned_on_vec| turned_on_vec.contains(&x.name)) && + (allow_experimental || !x.experimental) + }) + .cloned() + .collect::>()) +} + const BUILT_IN_TOOLS: &str = r####" tools: - name: "search_semantic" @@ -455,141 +592,3 @@ const NOT_READY_TOOLS: &str = r####" description: "Path to the specific file to diff (optional)." parameters_required: "####; - - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct ToolDesc { - pub name: String, - #[serde(default)] - pub agentic: bool, - #[serde(default)] - pub experimental: bool, - pub description: String, - pub parameters: Vec, - pub parameters_required: Vec, -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct ToolParam { - #[serde(deserialize_with = "validate_snake_case")] - pub name: String, - #[serde(rename = "type", default = "default_param_type")] - pub param_type: String, - pub description: String, -} - -fn validate_snake_case<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - if !s.chars().next().map_or(false, |c| c.is_ascii_lowercase()) - || !s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_') - || s.contains("__") - || s.ends_with('_') - { - return Err(serde::de::Error::custom( - format!("name {:?} must be in snake_case format: lowercase letters, numbers and single underscores, must start with letter", s) - )); - } - Ok(s) -} - -fn default_param_type() -> String { - "string".to_string() -} - -/// TODO: Think a better way to know if we can send array type to the model -/// -/// For now, anthropic models support it, gpt models don't, for other, we'll need to test -pub fn model_supports_array_param_type(model_id: &str) -> bool { - model_id.contains("claude") -} - -pub fn make_openai_tool_value( - name: String, - agentic: bool, - description: String, - parameters_required: Vec, - parameters: Vec, -) -> Value { - let params_properties = parameters.iter().map(|param| { - ( - param.name.clone(), - json!({ - "type": param.param_type, - "description": param.description - }) - ) - }).collect::>(); - - let function_json = json!({ - "type": "function", - "function": { - "name": name, - "agentic": agentic, // this field is not OpenAI's - "description": description, - "parameters": { - "type": "object", - "properties": params_properties, - "required": parameters_required - } - } - }); - function_json -} - -impl ToolDesc { - pub fn into_openai_style(self) -> Value { - make_openai_tool_value( - self.name, - self.agentic, - self.description, - self.parameters_required, - self.parameters, - ) - } - - pub fn is_supported_by(&self, model: &str) -> bool { - if !model_supports_array_param_type(model) { - for param in &self.parameters { - if param.param_type == "array" { - tracing::warn!("Tool {} has array parameter, but model {} does not support it", self.name, model); - return false; - } - } - } - true - } -} - -#[derive(Deserialize)] -pub struct ToolDictDeserialize { - pub tools: Vec, -} - -pub async fn tool_description_list_from_yaml( - tools: IndexMap>, - turned_on: Option<&Vec>, - allow_experimental: bool, -) -> Result, String> { - let tool_desc_deser: ToolDictDeserialize = serde_yaml::from_str(BUILT_IN_TOOLS) - .map_err(|e|format!("Failed to parse BUILT_IN_TOOLS: {}", e))?; - - let mut tool_desc_vec = vec![]; - tool_desc_vec.extend(tool_desc_deser.tools.iter().cloned()); - - for (tool_name, tool) in tools { - if !tool_desc_vec.iter().any(|desc| desc.name == tool_name) { - tool_desc_vec.push(tool.tool_description()); - } - } - - Ok(tool_desc_vec.iter() - .filter(|x| { - turned_on.map_or(true, |turned_on_vec| turned_on_vec.contains(&x.name)) && - (allow_experimental || !x.experimental) - }) - .cloned() - .collect::>()) -} From f1b709ab60e17eb9707e442f772d1c49f917845f Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:07:45 +0200 Subject: [PATCH 05/90] move tree description from yaml to trait impl --- refact-agent/engine/src/tools/tool_tree.rs | 24 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 11 --------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_tree.rs b/refact-agent/engine/src/tools/tool_tree.rs index 35209c1d9..6c1bc51dc 100644 --- a/refact-agent/engine/src/tools/tool_tree.rs +++ b/refact-agent/engine/src/tools/tool_tree.rs @@ -8,7 +8,7 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_file::return_one_candidate_or_a_good_error; use crate::at_commands::at_tree::{construct_tree_out_of_flat_list_of_paths, print_files_tree_with_budget}; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::files_correction::{correct_to_nearest_dir_path, correct_to_nearest_filename, get_project_dirs, paths_from_anywhere}; use crate::files_in_workspace::ls_files; @@ -24,6 +24,28 @@ fn preformat_path(path: &String) -> String { impl Tool for ToolTree { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "tree".to_string(), + agentic: false, + experimental: false, + description: "Get a files tree with symbols for the project. Use it to get familiar with the project, file names and symbols".to_string(), + parameters: vec![ + ToolParam { + name: "path".to_string(), + description: "An absolute path to get files tree for. Do not pass it if you need a full project tree.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "use_ast".to_string(), + description: "If true, for each file an array of AST symbols will appear as well as its filename".to_string(), + param_type: "boolean".to_string(), + }, + ], + parameters_required: vec![], + } + } + async fn tool_execute( &mut self, ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index f7c895fe1..295078da9 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -330,17 +330,6 @@ tools: - "queries" - "scope" - - name: "tree" - description: "Get a files tree with symbols for the project. Use it to get familiar with the project, file names and symbols" - parameters: - - name: "path" - type: "string" - description: "An absolute path to get files tree for. Do not pass it if you need a full project tree." - - name: "use_ast" - type: "boolean" - description: "If true, for each file an array of AST symbols will appear as well as its filename" - parameters_required: [] - - name: "web" description: "Fetch a web page and convert to readable plain text." parameters: From 9823a5f7d4ff40efca1c08173baf29628a7d9f90 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:09:22 +0200 Subject: [PATCH 06/90] move web description from yaml to trait impl --- refact-agent/engine/src/tools/tool_web.rs | 19 ++++++++++++++- .../engine/src/tools/tools_description.rs | 23 +++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_web.rs b/refact-agent/engine/src/tools/tool_web.rs index e8b969af5..5eaade40c 100644 --- a/refact-agent/engine/src/tools/tool_web.rs +++ b/refact-agent/engine/src/tools/tool_web.rs @@ -6,7 +6,7 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_web::execute_at_web; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; @@ -15,6 +15,23 @@ pub struct ToolWeb; #[async_trait] impl Tool for ToolWeb { fn as_any(&self) -> &dyn std::any::Any { self } + + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "web".to_string(), + agentic: false, + experimental: false, + description: "Fetch a web page and convert to readable plain text.".to_string(), + parameters: vec![ + ToolParam { + name: "url".to_string(), + description: "URL of the web page to fetch.".to_string(), + param_type: "string".to_string(), + }, + ], + parameters_required: vec!["url".to_string()], + } + } async fn tool_execute( &mut self, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 295078da9..9f2ac22a1 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -41,7 +41,7 @@ pub trait Tool: Send + Sync { args: &HashMap ) -> Result<(bool, Vec), String>; - fn tool_description(&self) -> ToolDesc; + // fn tool_description(&self) -> ToolDesc; async fn match_against_confirm_deny( &self, @@ -111,6 +111,10 @@ pub trait Tool: Send + Sync { fn tool_name(&self) -> String { return "".to_string(); } + + fn tool_description(&self) -> ToolDesc { + unimplemented!(); + } } pub async fn tools_merged_and_filtered( @@ -330,14 +334,19 @@ tools: - "queries" - "scope" - - name: "web" - description: "Fetch a web page and convert to readable plain text." + - name: "tree" + description: "Get a files tree with symbols for the project. Use it to get familiar with the project, file names and symbols" parameters: - - name: "url" + - name: "path" type: "string" - description: "URL of the web page to fetch." - parameters_required: - - "url" + description: "An absolute path to get files tree for. Do not pass it if you need a full project tree." + - name: "use_ast" + type: "boolean" + description: "If true, for each file an array of AST symbols will appear as well as its filename" + parameters_required: [] + + + - name: "cat" description: "Like cat in console, but better: it can read multiple files and images. Prefer to open full files." From 4de2d2d1aa9aac4e8c901e9177ffa1e355a5d285 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 11:30:58 +0200 Subject: [PATCH 07/90] move cat description from yaml to trait impl --- refact-agent/engine/src/tools/tool_cat.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 9 +-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_cat.rs b/refact-agent/engine/src/tools/tool_cat.rs index fd856c425..082398235 100644 --- a/refact-agent/engine/src/tools/tool_cat.rs +++ b/refact-agent/engine/src/tools/tool_cat.rs @@ -9,7 +9,7 @@ use async_trait::async_trait; use resvg::{tiny_skia, usvg}; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_file::{file_repair_candidates, return_one_candidate_or_a_good_error}; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; use crate::files_correction::{correct_to_nearest_dir_path, get_project_dirs}; use crate::files_in_workspace::{get_file_text_from_memory_or_disk, ls_files}; @@ -98,6 +98,23 @@ fn parse_cat_args(args: &HashMap) -> Result<(Vec, HashMap #[async_trait] impl Tool for ToolCat { fn as_any(&self) -> &dyn std::any::Any { self } + + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "cat".to_string(), + agentic: false, + experimental: false, + description: "Like cat in console, but better: it can read multiple files and images. Prefer to open full files.".to_string(), + parameters: vec![ + ToolParam { + name: "paths".to_string(), + description: "Comma separated file names or directories: dir1/file1.ext,dir3/dir4.".to_string(), + param_type: "string".to_string(), + }, + ], + parameters_required: vec!["paths".to_string()], + } + } async fn tool_execute( &mut self, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 9f2ac22a1..d05e01035 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -348,14 +348,7 @@ tools: - - name: "cat" - description: "Like cat in console, but better: it can read multiple files and images. Prefer to open full files." - parameters: - - name: "paths" - type: "string" - description: "Comma separated file names or directories: dir1/file1.ext,dir3/dir4." - parameters_required: - - "paths" + - name: "rm" description: "Deletes a file or directory. Use recursive=true for directories. Set dry_run=true to preview without deletion." From 3393d48e144aed75cc896eff447638835cb1597f Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:14:10 +0200 Subject: [PATCH 08/90] remove rm description from yaml as it's already in trait impl --- .../engine/src/tools/tools_description.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index d05e01035..e171e1a6d 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -350,23 +350,7 @@ tools: - - name: "rm" - description: "Deletes a file or directory. Use recursive=true for directories. Set dry_run=true to preview without deletion." - parameters: - - name: "path" - type: "string" - description: "Absolute or relative path of the file or directory to delete." - - name: "recursive" - type: "boolean" - description: "If true and target is a directory, delete recursively. Defaults to false." - - name: "dry_run" - type: "boolean" - description: "If true, only report what would be done without deleting." - - name: "max_depth" - type: "number" - description: "(Optional) Maximum depth (currently unused)." - parameters_required: - - "path" + - name: "mv" description: "Moves or renames files and directories. If a simple rename fails due to a cross-device error and the source is a file, it falls back to copying and deleting. Use overwrite=true to replace an existing target." From d7cd4b9c1d64161bdd537aabb076e1ab2e7e8c8b Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:15:20 +0200 Subject: [PATCH 09/90] remove mv description from yaml as it's already in trait impl --- .../engine/src/tools/tools_description.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index e171e1a6d..6f49fef64 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -352,21 +352,7 @@ tools: - - name: "mv" - description: "Moves or renames files and directories. If a simple rename fails due to a cross-device error and the source is a file, it falls back to copying and deleting. Use overwrite=true to replace an existing target." - parameters: - - name: "source" - type: "string" - description: "Path of the file or directory to move." - - name: "destination" - type: "string" - description: "Target path where the file or directory should be placed." - - name: "overwrite" - type: "boolean" - description: "If true and target exists, replace it. Defaults to false." - parameters_required: - - "source" - - "destination" + - name: "create_textdoc" agentic: false From 80e6e3b5a3e82d1d70a0df5ef0480558e3435984 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:18:04 +0200 Subject: [PATCH 10/90] move create_textdoc description from yaml to trait impl --- .../tools/file_edit/tool_create_textdoc.rs | 24 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 14 +---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs b/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs index efa40edbd..6c4724fce 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs @@ -5,7 +5,7 @@ use crate::privacy::{check_file_privacy, load_privacy_if_needed, FilePrivacyLeve use crate::tools::file_edit::auxiliary::{ await_ast_indexing, convert_edit_to_diffchunks, sync_documents_ast, write_file, }; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; use async_trait::async_trait; use serde_json::{json, Value}; use std::collections::HashMap; @@ -181,4 +181,26 @@ impl Tool for ToolCreateTextDoc { deny: vec![], }) } + + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "create_textdoc".to_string(), + agentic: false, + experimental: false, + description: "Creates a new text document or code or completely replaces the content of an existing document. Avoid trailing spaces and tabs.".to_string(), + parameters: vec![ + ToolParam { + name: "path".to_string(), + description: "Absolute path to new file.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "content".to_string(), + description: "The initial text or code.".to_string(), + param_type: "string".to_string(), + } + ], + parameters_required: vec!["path".to_string(), "content".to_string()], + } + } } diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 6f49fef64..0227e22f4 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -354,19 +354,7 @@ tools: - - name: "create_textdoc" - agentic: false - description: "Creates a new text document or code or completely replaces the content of an existing document. Avoid trailing spaces and tabs." - parameters: - - name: "path" - type: "string" - description: "Absolute path to new file." - - name: "content" - type: "string" - description: "The initial text or code." - parameters_required: - - "path" - - "content" + - name: "update_textdoc" agentic: false From a31eecb02792e3bf089babdd64ce82f8a9d7a507 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:20:30 +0200 Subject: [PATCH 11/90] move update_textdoc description from yaml to trait impl --- .../tools/file_edit/tool_update_textdoc.rs | 34 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 22 +----------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs index b8745d636..b59332866 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs @@ -3,7 +3,7 @@ use crate::call_validation::{ChatContent, ChatMessage, ContextEnum, DiffChunk}; use crate::integrations::integr_abstract::IntegrationConfirmation; use crate::privacy::{check_file_privacy, load_privacy_if_needed, FilePrivacyLevel, PrivacySettings}; use crate::tools::file_edit::auxiliary::{await_ast_indexing, convert_edit_to_diffchunks, str_replace, sync_documents_ast}; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; use async_trait::async_trait; use serde_json::{json, Value}; use std::collections::HashMap; @@ -174,4 +174,36 @@ impl Tool for ToolUpdateTextDoc { deny: vec![], }) } + + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "update_textdoc".to_string(), + agentic: false, + experimental: false, + description: "Updates an existing document by replacing specific text, use this if file already exists. Optimized for large files or small changes where simple string replacement is sufficient. Avoid trailing spaces and tabs.".to_string(), + parameters: vec![ + ToolParam { + name: "path".to_string(), + description: "Absolute path to the file to change.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "old_str".to_string(), + description: "The exact text that needs to be updated. Use update_textdoc_regex if you need pattern matching.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "replacement".to_string(), + description: "The new text that will replace the old text.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "multiple".to_string(), + description: "If true, applies the replacement to all occurrences; if false, only the first occurrence is replaced.".to_string(), + param_type: "boolean".to_string(), + } + ], + parameters_required: vec!["path".to_string(), "old_str".to_string(), "replacement".to_string(), "multiple".to_string()], + } + } } diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 0227e22f4..f852a5ee9 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -356,27 +356,7 @@ tools: - - name: "update_textdoc" - agentic: false - description: "Updates an existing document by replacing specific text, use this if file already exists. Optimized for large files or small changes where simple string replacement is sufficient. Avoid trailing spaces and tabs." - parameters: - - name: "path" - type: "string" - description: "Absolute path to the file to change." - - name: "old_str" - type: "string" - description: "The exact text that needs to be updated. Use update_textdoc_regex if you need pattern matching." - - name: "replacement" - type: "string" - description: "The new text that will replace the old text." - - name: "multiple" - type: "boolean" - description: "If true, applies the replacement to all occurrences; if false, only the first occurrence is replaced." - parameters_required: - - "path" - - "old_str" - - "replacement" - - "multiple" + - name: "update_textdoc_regex" agentic: false From 9a56bc328ad5e45f6ad6842ae5fabeed3d11461c Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:22:30 +0200 Subject: [PATCH 12/90] move update_textdoc_regex description from yaml to trait impl --- .../file_edit/tool_update_textdoc_regex.rs | 34 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 22 +----------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs index 0094a04ba..324ac9edf 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs @@ -3,7 +3,7 @@ use crate::call_validation::{ChatContent, ChatMessage, ContextEnum, DiffChunk}; use crate::integrations::integr_abstract::IntegrationConfirmation; use crate::privacy::{check_file_privacy, load_privacy_if_needed, FilePrivacyLevel, PrivacySettings}; use crate::tools::file_edit::auxiliary::{await_ast_indexing, convert_edit_to_diffchunks, str_replace_regex, sync_documents_ast}; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; use async_trait::async_trait; use serde_json::{json, Value}; use std::collections::HashMap; @@ -182,4 +182,36 @@ impl Tool for ToolUpdateTextDocRegex { deny: vec![], }) } + + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "update_textdoc_regex".to_string(), + agentic: false, + experimental: false, + description: "Updates an existing document using regex pattern matching. Ideal when changes can be expressed as a regular expression or when you need to match variable text patterns. Avoid trailing spaces and tabs.".to_string(), + parameters: vec![ + ToolParam { + name: "path".to_string(), + description: "Absolute path to the file to change.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "pattern".to_string(), + description: "A regex pattern to match the text that needs to be updated. Prefer simpler regexes for better performance.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "replacement".to_string(), + description: "The new text that will replace the matched pattern.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "multiple".to_string(), + description: "If true, applies the replacement to all occurrences; if false, only the first occurrence is replaced.".to_string(), + param_type: "boolean".to_string(), + } + ], + parameters_required: vec!["path".to_string(), "pattern".to_string(), "replacement".to_string(), "multiple".to_string()], + } + } } diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index f852a5ee9..d4a688c45 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -358,27 +358,7 @@ tools: - - name: "update_textdoc_regex" - agentic: false - description: "Updates an existing document using regex pattern matching. Ideal when changes can be expressed as a regular expression or when you need to match variable text patterns. Avoid trailing spaces and tabs." - parameters: - - name: "path" - type: "string" - description: "Absolute path to the file to change." - - name: "pattern" - type: "string" - description: "A regex pattern to match the text that needs to be updated. Prefer simpler regexes for better performance." - - name: "replacement" - type: "string" - description: "The new text that will replace the matched pattern." - - name: "multiple" - type: "boolean" - description: "If true, applies the replacement to all occurrences; if false, only the first occurrence is replaced." - parameters_required: - - "path" - - "pattern" - - "replacement" - - "multiple" + # -- agentic tools below -- - name: "locate" From 8184da30e2087c4e2ef6b81c86e156c63c28d46e Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:23:30 +0200 Subject: [PATCH 13/90] move docker description from yaml to trait impl --- .../src/integrations/docker/integr_docker.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 11 +---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/refact-agent/engine/src/integrations/docker/integr_docker.rs b/refact-agent/engine/src/integrations/docker/integr_docker.rs index 0a15497f4..df12f1783 100644 --- a/refact-agent/engine/src/integrations/docker/integr_docker.rs +++ b/refact-agent/engine/src/integrations/docker/integr_docker.rs @@ -11,7 +11,7 @@ use crate::call_validation::{ChatContent, ChatMessage, ContextEnum}; use crate::global_context::GlobalContext; use crate::integrations::integr_abstract::{IntegrationTrait, IntegrationCommon, IntegrationConfirmation}; use crate::integrations::process_io_utils::AnsiStrippable; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::integrations::docker::docker_ssh_tunnel_utils::{SshConfig, forward_remote_docker_if_needed}; use crate::integrations::utils::{serialize_num_to_str, deserialize_str_to_num}; @@ -136,6 +136,23 @@ impl ToolDocker { #[async_trait] impl Tool for ToolDocker { fn as_any(&self) -> &dyn std::any::Any { self } + + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "docker".to_string(), + agentic: true, + experimental: true, + description: "Access to docker cli, in a non-interactive way, don't open a shell.".to_string(), + parameters: vec![ + ToolParam { + name: "command".to_string(), + description: "Examples: docker images".to_string(), + param_type: "string".to_string(), + }, + ], + parameters_required: vec!["command".to_string()], + } + } async fn tool_execute( &mut self, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index d4a688c45..215f4eb05 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -438,16 +438,7 @@ tools: parameters_required: - "query" - - name: "docker" - agentic: true - experimental: true - description: "Access to docker cli, in a non-interactive way, don't open a shell." - parameters: - - name: "command" - type: "string" - description: "Examples: docker images" - parameters_required: - - "command" + - name: "knowledge" agentic: true From fb679308c60bec926212a615e9bd93c8af7a12ed Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 11:32:30 +0200 Subject: [PATCH 14/90] move regex_search description from yaml to trait impl --- .../engine/src/tools/tool_regex_search.rs | 24 ++++++++++++++- .../engine/src/tools/tools_description.rs | 30 ------------------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_regex_search.rs b/refact-agent/engine/src/tools/tool_regex_search.rs index baf61f36a..f27d0cbc7 100644 --- a/refact-agent/engine/src/tools/tool_regex_search.rs +++ b/refact-agent/engine/src/tools/tool_regex_search.rs @@ -18,7 +18,7 @@ use crate::files_correction::shortify_paths; use crate::files_in_workspace::get_file_text_from_memory_or_disk; use crate::global_context::GlobalContext; use crate::tools::scope_utils::{resolve_scope, validate_scope_files}; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; pub struct ToolRegexSearch; @@ -165,6 +165,28 @@ async fn smart_compress_results( impl Tool for ToolRegexSearch { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "search_pattern".to_string(), + agentic: false, + experimental: false, + description: "Search for files and folders whose names or paths match the given regular expression patterns, and also search for text matches inside files using the same patterns. Reports both path matches and text matches in separate sections.".to_string(), + parameters: vec![ + ToolParam { + name: "patterns".to_string(), + description: "Comma-separated list of regular expression patterns. Each pattern is used to search for matching file/folder names/paths, and also for matching text inside files. Use (?i) at the start for case-insensitive search.".to_string(), + param_type: "string".to_string(), + }, + ToolParam { + name: "scope".to_string(), + description: "'workspace' to search all files in workspace, 'dir/subdir/' to search in files within a directory, 'dir/file.ext' to search in a single file.".to_string(), + param_type: "string".to_string(), + } + ], + parameters_required: vec!["patterns".to_string(), "scope".to_string()], + } + } + async fn tool_execute( &mut self, ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 215f4eb05..93a07cae3 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -345,21 +345,6 @@ tools: description: "If true, for each file an array of AST symbols will appear as well as its filename" parameters_required: [] - - - - - - - - - - - - - - - # -- agentic tools below -- - name: "locate" agentic: true @@ -438,8 +423,6 @@ tools: parameters_required: - "query" - - - name: "knowledge" agentic: true description: "Fetches successful trajectories to help you accomplish your task. Call each time you have a new task to increase your chances of success." @@ -450,19 +433,6 @@ tools: parameters_required: - "search_key" - - name: "search_pattern" - description: "Search for files and folders whose names or paths match the given regular expression pattern, and also search for text matches inside files using the same patterns. Reports both path matches and text matches in separate sections." - parameters: - - name: "pattern" - type: "string" - description: "The pattern is used to search for matching file/folder names/paths, and also for matching text inside files. Use (?i) at the start for case-insensitive search." - - name: "scope" - type: "string" - description: "'workspace' to search all files in workspace, 'dir/subdir/' to search in files within a directory, 'dir/file.ext' to search in a single file." - parameters_required: - - "pattern" - - "scope" - - name: "create_knowledge" agentic: true description: "Creates a new knowledge entry in the vector database to help with future tasks." From 700028f467c687827232abb985a4055aaf9af1a7 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 13:53:43 +0200 Subject: [PATCH 15/90] remove tree description from yaml as it's already implemented in the tool --- refact-agent/engine/src/tools/tools_description.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 93a07cae3..2eae126dc 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -334,17 +334,6 @@ tools: - "queries" - "scope" - - name: "tree" - description: "Get a files tree with symbols for the project. Use it to get familiar with the project, file names and symbols" - parameters: - - name: "path" - type: "string" - description: "An absolute path to get files tree for. Do not pass it if you need a full project tree." - - name: "use_ast" - type: "boolean" - description: "If true, for each file an array of AST symbols will appear as well as its filename" - parameters_required: [] - # -- agentic tools below -- - name: "locate" agentic: true From 22b286bcb3a8562f2db4204d62e7c0ede70da312 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 11:43:51 +0200 Subject: [PATCH 16/90] move deep_analysis description from yaml to trait impl --- .../src/tools/tool_strategic_planning.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 11 ++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_strategic_planning.rs b/refact-agent/engine/src/tools/tool_strategic_planning.rs index 5d9e95802..a773e8c69 100644 --- a/refact-agent/engine/src/tools/tool_strategic_planning.rs +++ b/refact-agent/engine/src/tools/tool_strategic_planning.rs @@ -7,7 +7,7 @@ use tokio::sync::Mutex as AMutex; use async_trait::async_trait; use axum::http::StatusCode; use crate::subchat::subchat_single; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ChatUsage, ContextEnum, SubchatParameters, ContextFile, PostprocessSettings}; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_file::{file_repair_candidates, return_one_candidate_or_a_good_error}; @@ -164,6 +164,23 @@ async fn _execute_subchat_iteration( impl Tool for ToolStrategicPlanning { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "strategic_planning".to_string(), + agentic: true, + experimental: false, + description: "Strategically plan a solution for a complex problem or create a comprehensive approach.".to_string(), + parameters: vec![ + ToolParam { + name: "important_paths".to_string(), + param_type: "string".to_string(), + description: "Comma-separated list of all filenames which are required to be considered for resolving the problem. More files - better, include them even if you are not sure.".to_string(), + } + ], + parameters_required: vec!["important_paths".to_string()], + } + } + async fn tool_execute( &mut self, ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 2eae126dc..5e1ff4cbf 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -348,15 +348,8 @@ tools: parameters_required: - "what_to_find" - - name: "strategic_planning" - agentic: true - description: "Strategically plan a solution for a complex problem or create a comprehensive approach." - parameters: - - name: "important_paths" - type: "string" - description: "Comma-separated list of all filenames which are required to be considered for resolving the problem. More files - better, include them even if you are not sure." - parameters_required: - - "important_paths" + + - name: "github" agentic: true From c7fb87fd52cbdefa6fbb7b301190d2b85407f972 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 11:57:52 +0200 Subject: [PATCH 17/90] move locate description from yaml to trait impl --- .../engine/src/tools/tool_locate_search.rs | 24 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 15 ------------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_locate_search.rs b/refact-agent/engine/src/tools/tool_locate_search.rs index 2076bfb86..5b1b0371f 100644 --- a/refact-agent/engine/src/tools/tool_locate_search.rs +++ b/refact-agent/engine/src/tools/tool_locate_search.rs @@ -10,7 +10,7 @@ use axum::http::StatusCode; use indexmap::IndexMap; use hashbrown::HashSet; use crate::subchat::subchat; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ChatUsage, ContextEnum, SubchatParameters, ContextFile, PostprocessSettings}; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; use crate::at_commands::at_commands::AtCommandsContext; @@ -215,6 +215,28 @@ pub fn check_for_inspected_files(inspected_files: &mut HashSet, messages impl Tool for ToolLocateSearch { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "locate".to_string(), + agentic: true, + experimental: false, + description: "Get a list of files that are relevant to solve a particular task.".to_string(), + parameters: vec![ + ToolParam { + name: "what_to_find".to_string(), + param_type: "string".to_string(), + description: "A short narrative that includes (1) the problem you’re trying to solve, (2) which files or symbols have already been examined, and (3) exactly what additional files, code symbols, or text patterns the agent should locate next".to_string(), + }, + ToolParam { + name: "important_paths".to_string(), + param_type: "string".to_string(), + description: "Comma-separated list of all filenames which are already explored.".to_string(), + }, + ], + parameters_required: vec!["what_to_find".to_string()], + } + } + async fn tool_execute( &mut self, ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 5e1ff4cbf..8b2551aa8 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -335,21 +335,6 @@ tools: - "scope" # -- agentic tools below -- - - name: "locate" - agentic: true - description: "Get a list of files that are relevant to solve a particular task." - parameters: - - name: "what_to_find" - type: "string" - description: "A short narrative that includes (1) the problem you’re trying to solve, (2) which files or symbols have already been examined, and (3) exactly what additional files, code symbols, or text patterns the agent should locate next" - - name: "important_paths" - type: "string" - description: "Comma-separated list of all filenames which are already explored." - parameters_required: - - "what_to_find" - - - - name: "github" agentic: true From 4004e49f2f0295afdbb26552db9fe8f65879c241 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 12:19:01 +0200 Subject: [PATCH 18/90] move search description from yaml to trait impl --- refact-agent/engine/src/tools/tools_description.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 8b2551aa8..a48f1a056 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -321,6 +321,7 @@ pub async fn tool_description_list_from_yaml( const BUILT_IN_TOOLS: &str = r####" tools: +<<<<<<< HEAD - name: "search_semantic" description: "Find semantically similar pieces of code or text using vector database (semantic search)" parameters: From 0a0595ca572f3890757ca308b667c203d2d13e08 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 12:19:39 +0200 Subject: [PATCH 19/90] remove deep_analysis description from yaml as it's already implemented in the tool --- refact-agent/engine/src/tools/tools_description.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index a48f1a056..6f3aa2ada 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -321,6 +321,7 @@ pub async fn tool_description_list_from_yaml( const BUILT_IN_TOOLS: &str = r####" tools: +<<<<<<< HEAD <<<<<<< HEAD - name: "search_semantic" description: "Find semantically similar pieces of code or text using vector database (semantic search)" From 0daf8cc71d9bfc0bafc49f30720297850f97a4f1 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 12:20:35 +0200 Subject: [PATCH 20/90] move github description from yaml to trait impl --- .../engine/src/integrations/integr_github.rs | 24 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 16 ------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/refact-agent/engine/src/integrations/integr_github.rs b/refact-agent/engine/src/integrations/integr_github.rs index 7bf4245d7..f7dd47ab4 100644 --- a/refact-agent/engine/src/integrations/integr_github.rs +++ b/refact-agent/engine/src/integrations/integr_github.rs @@ -12,7 +12,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ContextEnum, ChatMessage, ChatContent, ChatUsage}; use crate::files_correction::canonical_path; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use serde_json::Value; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation, IntegrationTrait}; use crate::integrations::process_io_utils::AnsiStrippable; @@ -66,6 +66,28 @@ impl IntegrationTrait for ToolGithub { impl Tool for ToolGithub { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "github".to_string(), + agentic: true, + experimental: false, + description: "Access to gh command line command, to fetch issues, review PRs.".to_string(), + parameters: vec![ + ToolParam { + name: "project_dir".to_string(), + param_type: "string".to_string(), + description: "Look at system prompt for location of version control (.git folder) of the active file.".to_string(), + }, + ToolParam { + name: "command".to_string(), + param_type: "string".to_string(), + description: "Examples:\ngh issue create --body \"hello world\" --title \"Testing gh integration\"\ngh issue list --author @me --json number,title,updatedAt,url\n".to_string(), + } + ], + parameters_required: vec!["project_dir".to_string(), "command".to_string()], + } + } + async fn tool_execute( &mut self, _ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 6f3aa2ada..fb5ce6615 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -321,8 +321,6 @@ pub async fn tool_description_list_from_yaml( const BUILT_IN_TOOLS: &str = r####" tools: -<<<<<<< HEAD -<<<<<<< HEAD - name: "search_semantic" description: "Find semantically similar pieces of code or text using vector database (semantic search)" parameters: @@ -338,20 +336,6 @@ tools: # -- agentic tools below -- - - name: "github" - agentic: true - description: "Access to gh command line command, to fetch issues, review PRs." - parameters: - - name: "project_dir" - type: "string" - description: "Look at system prompt for location of version control (.git folder) of the active file." - - name: "command" - type: "string" - description: 'Examples:\ngh issue create --body "hello world" --title "Testing gh integration"\ngh issue list --author @me --json number,title,updatedAt,url\n' - parameters_required: - - "project_dir" - - "command" - - name: "gitlab" agentic: true description: "Access to glab command line command, to fetch issues, review PRs." From 801ecc4c05b4fb8a450e7a105ca41e0cfe71dd76 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 12:21:40 +0200 Subject: [PATCH 21/90] move gitlab description from yaml to trait impl --- .../engine/src/integrations/integr_gitlab.rs | 22 +++++++++++++++++++ .../engine/src/tools/tools_description.rs | 14 ------------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/refact-agent/engine/src/integrations/integr_gitlab.rs b/refact-agent/engine/src/integrations/integr_gitlab.rs index 359bbf2e7..778dfdc3f 100644 --- a/refact-agent/engine/src/integrations/integr_gitlab.rs +++ b/refact-agent/engine/src/integrations/integr_gitlab.rs @@ -65,6 +65,28 @@ impl IntegrationTrait for ToolGitlab { impl Tool for ToolGitlab { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "gitlab".to_string(), + agentic: true, + experimental: false, + description: "Access to glab command line command, to fetch issues, review PRs.".to_string(), + parameters: vec![ + ToolParam { + name: "project_dir".to_string(), + param_type: "string".to_string(), + description: "Look at system prompt for location of version control (.git folder) of the active file.".to_string(), + }, + ToolParam { + name: "command".to_string(), + param_type: "string".to_string(), + description: "Examples:\nglab issue create --description \"hello world\" --title \"Testing glab integration\"\nglab issue list --author @me\n".to_string(), + }, + ], + parameters_required: vec!["project_dir".to_string(), "command".to_string()], + } + } + async fn tool_execute( &mut self, _ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index fb5ce6615..6e8e4766f 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -336,20 +336,6 @@ tools: # -- agentic tools below -- - - name: "gitlab" - agentic: true - description: "Access to glab command line command, to fetch issues, review PRs." - parameters: - - name: "project_dir" - type: "string" - description: "Look at system prompt for location of version control (.git folder) of the active file." - - name: "command" - type: "string" - description: 'Examples:\nglab issue create --description "hello world" --title "Testing glab integration"\nglab issue list --author @me\n' - parameters_required: - - "project_dir" - - "command" - - name: "postgres" agentic: true description: "PostgreSQL integration, can run a single query per call." From 53ef81f90a577eadfafa8905270224541955bdca Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 12:21:57 +0200 Subject: [PATCH 22/90] move postgres description from yaml to trait impl --- .../src/integrations/integr_postgres.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 13 ------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/refact-agent/engine/src/integrations/integr_postgres.rs b/refact-agent/engine/src/integrations/integr_postgres.rs index 4c537ffba..56d390b00 100644 --- a/refact-agent/engine/src/integrations/integr_postgres.rs +++ b/refact-agent/engine/src/integrations/integr_postgres.rs @@ -13,7 +13,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::ContextEnum; use crate::call_validation::{ChatContent, ChatMessage, ChatUsage}; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use super::process_io_utils::AnsiStrippable; @@ -113,6 +113,23 @@ impl ToolPostgres { impl Tool for ToolPostgres { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "postgres".to_string(), + agentic: true, + experimental: false, + description: "PostgreSQL integration, can run a single query per call.".to_string(), + parameters: vec![ + ToolParam { + name: "query".to_string(), + param_type: "string".to_string(), + description: "Don't forget semicolon at the end, examples:\nSELECT * FROM table_name;\nCREATE INDEX my_index_users_email ON my_users (email);".to_string(), + }, + ], + parameters_required: vec!["query".to_string()], + } + } + async fn tool_execute( &mut self, _ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 6e8e4766f..c76ad38db 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -336,19 +336,6 @@ tools: # -- agentic tools below -- - - name: "postgres" - agentic: true - description: "PostgreSQL integration, can run a single query per call." - parameters: - - name: "query" - type: "string" - description: | - Don't forget semicolon at the end, examples: - SELECT * FROM table_name; - CREATE INDEX my_index_users_email ON my_users (email); - parameters_required: - - "query" - - name: "mysql" agentic: true description: "MySQL integration, can run a single query per call." From 37dd5483cb939d005282ec0ea3ca297159f6a859 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 15:41:44 +0200 Subject: [PATCH 23/90] fix gitlab tool imports --- refact-agent/engine/src/integrations/integr_gitlab.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refact-agent/engine/src/integrations/integr_gitlab.rs b/refact-agent/engine/src/integrations/integr_gitlab.rs index 778dfdc3f..beac8b9ee 100644 --- a/refact-agent/engine/src/integrations/integr_gitlab.rs +++ b/refact-agent/engine/src/integrations/integr_gitlab.rs @@ -13,7 +13,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ContextEnum, ChatMessage, ChatContent, ChatUsage}; use crate::files_correction::canonical_path; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation, IntegrationTrait}; use crate::integrations::process_io_utils::AnsiStrippable; From b59719feccece52d812a7cad6b71599471daf631 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 15:42:23 +0200 Subject: [PATCH 24/90] move mysql description from yaml to trait impl --- .../engine/src/integrations/integr_mysql.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 13 ------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/refact-agent/engine/src/integrations/integr_mysql.rs b/refact-agent/engine/src/integrations/integr_mysql.rs index b91dd7b8c..74057d066 100644 --- a/refact-agent/engine/src/integrations/integr_mysql.rs +++ b/refact-agent/engine/src/integrations/integr_mysql.rs @@ -12,7 +12,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::ContextEnum; use crate::call_validation::{ChatContent, ChatMessage, ChatUsage}; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation, IntegrationTrait}; use super::process_io_utils::AnsiStrippable; @@ -114,6 +114,23 @@ impl ToolMysql { impl Tool for ToolMysql { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "mysql".to_string(), + agentic: true, + experimental: false, + description: "MySQL integration, can run a single query per call.".to_string(), + parameters: vec![ + ToolParam { + name: "query".to_string(), + param_type: "string".to_string(), + description: "Don't forget semicolon at the end, examples:\nSELECT * FROM table_name;\nCREATE INDEX my_index_users_email ON my_users (email);".to_string(), + }, + ], + parameters_required: vec!["query".to_string()], + } + } + async fn tool_execute( &mut self, _ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index c76ad38db..3534e427b 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -336,19 +336,6 @@ tools: # -- agentic tools below -- - - name: "mysql" - agentic: true - description: "MySQL integration, can run a single query per call." - parameters: - - name: "query" - type: "string" - description: | - Don't forget semicolon at the end, examples: - SELECT * FROM table_name; - CREATE INDEX my_index_users_email ON my_users (email); - parameters_required: - - "query" - - name: "knowledge" agentic: true description: "Fetches successful trajectories to help you accomplish your task. Call each time you have a new task to increase your chances of success." From 1a6e6f8a2e59f27ed71c7c42dd9410949c8595e7 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 15:42:55 +0200 Subject: [PATCH 25/90] fix: move search description from yaml to trait impl --- refact-agent/engine/src/tools/tool_search.rs | 24 +++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/refact-agent/engine/src/tools/tool_search.rs b/refact-agent/engine/src/tools/tool_search.rs index c282230a1..625a717f4 100644 --- a/refact-agent/engine/src/tools/tool_search.rs +++ b/refact-agent/engine/src/tools/tool_search.rs @@ -10,7 +10,7 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::{vec_context_file_to_context_tools, AtCommandsContext}; use crate::at_commands::at_search::execute_at_search; use crate::tools::scope_utils::create_scope_filter; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; @@ -33,6 +33,28 @@ async fn execute_att_search( #[async_trait] impl Tool for ToolSearch { fn as_any(&self) -> &dyn std::any::Any { self } + + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "search".to_string(), + agentic: false, + experimental: false, + description: "Find semantically similar pieces of code or text using vector database (semantic search)".to_string(), + parameters: vec![ + ToolParam { + name: "query".to_string(), + param_type: "string".to_string(), + description: "Single line, paragraph or code sample to search for semantically similar content.".to_string(), + }, + ToolParam { + name: "scope".to_string(), + param_type: "string".to_string(), + description: "'workspace' to search all files in workspace, 'dir/subdir/' to search in files within a directory, 'dir/file.ext' to search in a single file.".to_string(), + } + ], + parameters_required: vec!["query".to_string(), "scope".to_string()], + } + } async fn tool_execute( &mut self, From 0fdf824f3073d39a37f83f99311696068eb0bcc8 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 15:45:58 +0200 Subject: [PATCH 26/90] move knowledge description from yaml to trait impl --- .../engine/src/tools/tool_knowledge.rs | 19 ++++++++++++++++++- .../engine/src/tools/tools_description.rs | 10 ---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_knowledge.rs b/refact-agent/engine/src/tools/tool_knowledge.rs index 6d5c0c6a8..cfe8e5275 100644 --- a/refact-agent/engine/src/tools/tool_knowledge.rs +++ b/refact-agent/engine/src/tools/tool_knowledge.rs @@ -6,7 +6,7 @@ use tokio::sync::Mutex as AMutex; use async_trait::async_trait; use crate::at_commands::at_commands::AtCommandsContext; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::memories::memories_search; @@ -18,6 +18,23 @@ pub struct ToolGetKnowledge; impl Tool for ToolGetKnowledge { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "knowledge".to_string(), + agentic: true, + experimental: false, + description: "Fetches successful trajectories to help you accomplish your task. Call each time you have a new task to increase your chances of success.".to_string(), + parameters: vec![ + ToolParam { + name: "search_key".to_string(), + param_type: "string".to_string(), + description: "Search keys for the knowledge database. Write combined elements from all fields (tools, project components, objectives, and language/framework). This field is used for vector similarity search.".to_string(), + } + ], + parameters_required: vec!["search_key".to_string()], + } + } + async fn tool_execute( &mut self, ccx: Arc>, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 3534e427b..0d67cb21e 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -336,16 +336,6 @@ tools: # -- agentic tools below -- - - name: "knowledge" - agentic: true - description: "Fetches successful trajectories to help you accomplish your task. Call each time you have a new task to increase your chances of success." - parameters: - - name: "search_key" - type: "string" - description: "Search keys for the knowledge database. Write combined elements from all fields (tools, project components, objectives, and language/framework). This field is used for vector similarity search." - parameters_required: - - "search_key" - - name: "create_knowledge" agentic: true description: "Creates a new knowledge entry in the vector database to help with future tasks." From 92beb72eaa4032681d6bfd8d35205ef2da608bc5 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 15:47:00 +0200 Subject: [PATCH 27/90] force tools to implement tool_description --- refact-agent/engine/src/tools/tools_description.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 0d67cb21e..589e15ecc 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -41,7 +41,7 @@ pub trait Tool: Send + Sync { args: &HashMap ) -> Result<(bool, Vec), String>; - // fn tool_description(&self) -> ToolDesc; + fn tool_description(&self) -> ToolDesc; async fn match_against_confirm_deny( &self, @@ -111,10 +111,6 @@ pub trait Tool: Send + Sync { fn tool_name(&self) -> String { return "".to_string(); } - - fn tool_description(&self) -> ToolDesc { - unimplemented!(); - } } pub async fn tools_merged_and_filtered( From bcf5212faa59b54422d7d0db5992754b173b106b Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 20 May 2025 12:40:58 +0200 Subject: [PATCH 28/90] move create_knowledge description from yaml to trait impl --- .../engine/src/tools/tool_create_knowledge.rs | 23 +++++++++++++++++-- .../engine/src/tools/tools_description.rs | 10 -------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_create_knowledge.rs b/refact-agent/engine/src/tools/tool_create_knowledge.rs index e7fb6d78b..4440a4ac2 100644 --- a/refact-agent/engine/src/tools/tool_create_knowledge.rs +++ b/refact-agent/engine/src/tools/tool_create_knowledge.rs @@ -7,7 +7,7 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; -use crate::tools::tools_description::Tool; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; pub struct ToolCreateKnowledge; @@ -15,6 +15,25 @@ pub struct ToolCreateKnowledge; impl Tool for ToolCreateKnowledge { fn as_any(&self) -> &dyn std::any::Any { self } + fn tool_description(&self) -> ToolDesc { + ToolDesc { + name: "create_knowledge".to_string(), + agentic: true, + experimental: false, + description: "Creates a new knowledge entry in the vector database to help with future tasks.".to_string(), + parameters: vec![ + ToolParam { + name: "knowledge_entry".to_string(), + param_type: "string".to_string(), + description: "The detailed knowledge content to store. Include comprehensive information about implementation details, code patterns, architectural decisions, troubleshooting steps, or solution approaches. Document what you did, how you did it, why you made certain choices, and any important observations or lessons learned. This field should contain the rich, detailed content that future searches will retrieve.".to_string(), + } + ], + parameters_required: vec![ + "knowledge_entry".to_string(), + ], + } + } + async fn tool_execute( &mut self, ccx: Arc>, @@ -53,4 +72,4 @@ impl Tool for ToolCreateKnowledge { fn tool_depends_on(&self) -> Vec { vec!["knowledge".to_string()] } -} \ No newline at end of file +} diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 589e15ecc..0bc2c4188 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -332,16 +332,6 @@ tools: # -- agentic tools below -- - - name: "create_knowledge" - agentic: true - description: "Creates a new knowledge entry in the vector database to help with future tasks." - parameters: - - name: "knowledge_entry" - type: "string" - description: "The detailed knowledge content to store. Include comprehensive information about implementation details, code patterns, architectural decisions, troubleshooting steps, or solution approaches. Document what you did, how you did it, why you made certain choices, and any important observations or lessons learned. This field should contain the rich, detailed content that future searches will retrieve." - parameters_required: - - "knowledge_entry" - - name: "create_memory_bank" agentic: true description: "Gathers information about the project structure (modules, file relations, classes, etc.) and saves this data into the memory bank." From 875826514462b651b1fea999edc8b8007a5fd6de Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 7 May 2025 16:02:52 +0200 Subject: [PATCH 29/90] remove memory_bank description from yaml as it's already implemented in the tool --- refact-agent/engine/src/tools/tools_description.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 0bc2c4188..df912dfac 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -329,14 +329,6 @@ tools: parameters_required: - "queries" - "scope" - - # -- agentic tools below -- - - - name: "create_memory_bank" - agentic: true - description: "Gathers information about the project structure (modules, file relations, classes, etc.) and saves this data into the memory bank." - parameters: [] - parameters_required: [] "####; From 04ea954df77c6be09e2ea6178db9d9fa342bb34f Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 8 May 2025 12:11:01 +0200 Subject: [PATCH 30/90] remove remainders from tools description yaml --- refact-agent/engine/src/tools/tool_search.rs | 8 ++--- .../engine/src/tools/tools_description.rs | 29 +------------------ 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_search.rs b/refact-agent/engine/src/tools/tool_search.rs index 625a717f4..774f269a2 100644 --- a/refact-agent/engine/src/tools/tool_search.rs +++ b/refact-agent/engine/src/tools/tool_search.rs @@ -36,15 +36,15 @@ impl Tool for ToolSearch { fn tool_description(&self) -> ToolDesc { ToolDesc { - name: "search".to_string(), + name: "search_semantic".to_string(), agentic: false, experimental: false, description: "Find semantically similar pieces of code or text using vector database (semantic search)".to_string(), parameters: vec![ ToolParam { - name: "query".to_string(), + name: "queries".to_string(), param_type: "string".to_string(), - description: "Single line, paragraph or code sample to search for semantically similar content.".to_string(), + description: "Comma-separated list of queries. Each query can be a single line, paragraph or code sample to search for semantically similar content.".to_string(), }, ToolParam { name: "scope".to_string(), @@ -52,7 +52,7 @@ impl Tool for ToolSearch { description: "'workspace' to search all files in workspace, 'dir/subdir/' to search in files within a directory, 'dir/file.ext' to search in a single file.".to_string(), } ], - parameters_required: vec!["query".to_string(), "scope".to_string()], + parameters_required: vec!["queries".to_string(), "scope".to_string()], } } diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index df912dfac..f606ae8db 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -284,21 +284,12 @@ impl ToolDesc { } } -#[derive(Deserialize)] -pub struct ToolDictDeserialize { - pub tools: Vec, -} - pub async fn tool_description_list_from_yaml( tools: IndexMap>, turned_on: Option<&Vec>, allow_experimental: bool, ) -> Result, String> { - let tool_desc_deser: ToolDictDeserialize = serde_yaml::from_str(BUILT_IN_TOOLS) - .map_err(|e|format!("Failed to parse BUILT_IN_TOOLS: {}", e))?; - - let mut tool_desc_vec = vec![]; - tool_desc_vec.extend(tool_desc_deser.tools.iter().cloned()); + let mut tool_desc_vec: Vec = vec![]; for (tool_name, tool) in tools { if !tool_desc_vec.iter().any(|desc| desc.name == tool_name) { @@ -315,24 +306,6 @@ pub async fn tool_description_list_from_yaml( .collect::>()) } -const BUILT_IN_TOOLS: &str = r####" -tools: - - name: "search_semantic" - description: "Find semantically similar pieces of code or text using vector database (semantic search)" - parameters: - - name: "queries" - type: "string" - description: "Comma-separated list of queries. Each query can be a single line, paragraph or code sample to search for semantically similar content." - - name: "scope" - type: "string" - description: "'workspace' to search all files in workspace, 'dir/subdir/' to search in files within a directory, 'dir/file.ext' to search in a single file." - parameters_required: - - "queries" - - "scope" -"####; - - - #[allow(dead_code)] const NOT_READY_TOOLS: &str = r####" - name: "diff" From 5885b01cbae51dee8eaf5a77362f3c99bf0815cc Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 8 May 2025 12:14:42 +0200 Subject: [PATCH 31/90] refactor: move type to beggining of file --- .../engine/src/tools/tools_description.rs | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index f606ae8db..c95a075a1 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -30,6 +30,27 @@ pub struct MatchConfirmDeny { pub rule: String, } +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct ToolDesc { + pub name: String, + #[serde(default)] + pub agentic: bool, + #[serde(default)] + pub experimental: bool, + pub description: String, + pub parameters: Vec, + pub parameters_required: Vec, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct ToolParam { + #[serde(deserialize_with = "validate_snake_case")] + pub name: String, + #[serde(rename = "type", default = "default_param_type")] + pub param_type: String, + pub description: String, +} + #[async_trait] pub trait Tool: Send + Sync { fn as_any(&self) -> &dyn std::any::Any; @@ -178,27 +199,6 @@ pub async fn tools_merged_and_filtered( Ok(filtered_tools) } -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct ToolDesc { - pub name: String, - #[serde(default)] - pub agentic: bool, - #[serde(default)] - pub experimental: bool, - pub description: String, - pub parameters: Vec, - pub parameters_required: Vec, -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct ToolParam { - #[serde(deserialize_with = "validate_snake_case")] - pub name: String, - #[serde(rename = "type", default = "default_param_type")] - pub param_type: String, - pub description: String, -} - fn validate_snake_case<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, From 8f03654d364057de12b2b8cc2d0ca9efeb876905 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 8 May 2025 12:35:56 +0200 Subject: [PATCH 32/90] changed tools_merged_and_filtered, to simply get_available_tools also filter them by experimental right there --- .../src/scratchpads/chat_passthrough.rs | 4 +- refact-agent/engine/src/subchat.rs | 4 +- .../engine/src/tools/tools_description.rs | 39 +++++++++++-------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index 8fbe8ccde..a5995c98d 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -17,7 +17,7 @@ use crate::scratchpads::chat_utils_limit_history::fix_and_limit_messages_history use crate::scratchpads::scratchpad_utils::HasRagResults; use crate::scratchpads::chat_utils_prompts::prepend_the_right_system_prompt_and_maybe_more_initial_messages; use crate::scratchpads::passthrough_convert_messages::convert_messages_to_openai_format; -use crate::tools::tools_description::{tool_description_list_from_yaml, tools_merged_and_filtered}; +use crate::tools::tools_description::{tool_description_list_from_yaml, get_available_tools}; use crate::tools::tools_execute::{run_tools_locally, run_tools_remotely}; @@ -112,7 +112,7 @@ impl ScratchpadAbstract for ChatPassthrough { }; let style = self.post.style.clone(); let mut at_tools = if !should_execute_remotely { - tools_merged_and_filtered(gcx.clone(), self.supports_clicks).await? + get_available_tools(gcx.clone(), self.supports_clicks).await? } else { IndexMap::new() }; diff --git a/refact-agent/engine/src/subchat.rs b/refact-agent/engine/src/subchat.rs index ee76653a0..f47edd0f6 100644 --- a/refact-agent/engine/src/subchat.rs +++ b/refact-agent/engine/src/subchat.rs @@ -7,7 +7,7 @@ use tracing::{error, info, warn}; use crate::caps::resolve_chat_model; use crate::caps::ChatModelRecord; -use crate::tools::tools_description::{tools_merged_and_filtered, tool_description_list_from_yaml}; +use crate::tools::tools_description::{get_available_tools, tool_description_list_from_yaml}; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{SamplingParameters, PostprocessSettings, ChatPost, ChatMessage, ChatUsage, ChatToolCall, ReasoningEffort}; use crate::global_context::{GlobalContext, try_load_caps_quickly_if_not_present}; @@ -268,7 +268,7 @@ pub async fn subchat_single( let ccx_locked = ccx.lock().await; (ccx_locked.global_context.clone(), ccx_locked.should_execute_remotely) }; - let tools_turned_on_by_cmdline = tools_merged_and_filtered(gcx.clone(), false).await?; + let tools_turned_on_by_cmdline = get_available_tools(gcx.clone(), false).await?; let tools_turned_on_by_cmdline_set: HashSet = tools_turned_on_by_cmdline.keys().cloned().collect(); let tools_on_intersection: Vec = if let Some(tools_s) = &tools_subset { let tools_turn_on_set: HashSet = tools_s.iter().cloned().collect(); diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index c95a075a1..6c04007d8 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -12,6 +12,7 @@ use crate::call_validation::{ChatUsage, ContextEnum}; use crate::global_context::try_load_caps_quickly_if_not_present; use crate::global_context::GlobalContext; use crate::integrations::integr_abstract::IntegrationConfirmation; +use crate::integrations::running_integrations::load_integration_tools; use crate::tools::tools_execute::{command_should_be_confirmed_by_user, command_should_be_denied}; // use crate::integrations::docker::integr_docker::ToolDocker; @@ -134,17 +135,8 @@ pub trait Tool: Send + Sync { } } -pub async fn tools_merged_and_filtered( - gcx: Arc>, - _supports_clicks: bool, // XXX -) -> Result>, String> { - let (ast_on, vecdb_on, allow_experimental) = { - let gcx_locked = gcx.read().await; - let vecdb_on = gcx_locked.vec_db.lock().await.is_some(); - (gcx_locked.ast_service.is_some(), vecdb_on, gcx_locked.cmdline.experimental) - }; - - let mut tools_all = IndexMap::from([ +pub fn get_builtin_tools() -> IndexMap> { + IndexMap::from([ ("search_symbol_definition".to_string(), Box::new(crate::tools::tool_ast_definition::ToolAstDefinition{}) as Box), ("search_symbol_usages".to_string(), Box::new(crate::tools::tool_ast_reference::ToolAstReference{}) as Box), ("tree".to_string(), Box::new(crate::tools::tool_tree::ToolTree{}) as Box), @@ -162,13 +154,18 @@ pub async fn tools_merged_and_filtered( ("create_memory_bank".to_string(), Box::new(crate::tools::tool_create_memory_bank::ToolCreateMemoryBank{}) as Box), ("search_semantic".to_string(), Box::new(crate::tools::tool_search::ToolSearch{}) as Box), ("locate".to_string(), Box::new(crate::tools::tool_locate_search::ToolLocateSearch{}) as Box), - ]); + ]) +} - let integrations = crate::integrations::running_integrations::load_integration_tools( - gcx.clone(), - allow_experimental, - ).await; - tools_all.extend(integrations); +pub async fn get_available_tools( + gcx: Arc>, + _supports_clicks: bool, // XXX +) -> Result>, String> { + let (ast_on, vecdb_on, allow_experimental) = { + let gcx_locked = gcx.read().await; + let vecdb_on = gcx_locked.vec_db.lock().await.is_some(); + (gcx_locked.ast_service.is_some(), vecdb_on, gcx_locked.cmdline.experimental) + }; let (is_there_a_thinking_model, allow_knowledge) = match try_load_caps_quickly_if_not_present(gcx.clone(), 0).await { Ok(caps) => { @@ -178,6 +175,11 @@ pub async fn tools_merged_and_filtered( Err(_) => (false, false), }; + let mut tools_all = get_builtin_tools(); + tools_all.extend( + load_integration_tools(gcx, allow_experimental).await + ); + let mut filtered_tools = IndexMap::new(); for (tool_name, tool) in tools_all { let dependencies = tool.tool_depends_on(); @@ -193,6 +195,9 @@ pub async fn tools_merged_and_filtered( if dependencies.contains(&"knowledge".to_string()) && !allow_knowledge { continue; } + if tool.tool_description().experimental && !allow_experimental { + continue; + } filtered_tools.insert(tool_name, tool); } From 641770d66e59e13d23b294c79f85280fc6be2f78 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 8 May 2025 13:07:26 +0200 Subject: [PATCH 33/90] fix use get_available tools and handle_v1_tools refactor --- .../engine/src/http/routers/v1/at_tools.rs | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index b689732d0..ac163a17e 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -15,7 +15,7 @@ use crate::http::http_post_json; use crate::http::routers::v1::chat::CHAT_TOP_N; use crate::indexing_utils::wait_for_indexing_if_needed; use crate::integrations::docker::docker_container_manager::docker_container_get_host_lsp_port_to_connect; -use crate::tools::tools_description::{tool_description_list_from_yaml, tools_merged_and_filtered, MatchConfirmDenyResult}; +use crate::tools::tools_description::{get_available_tools, MatchConfirmDenyResult, ToolDesc}; use crate::custom_error::ScratchError; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; use crate::tools::tools_execute::run_tools; @@ -67,31 +67,18 @@ pub struct ToolExecuteResponse { pub async fn handle_v1_tools( Extension(gcx): Extension>>, - _: hyper::body::Bytes, ) -> axum::response::Result, ScratchError> { - let all_tools = match tools_merged_and_filtered(gcx.clone(), true).await { - Ok(tools) => tools, - Err(e) => { - let error_body = serde_json::json!({ "detail": e }).to_string(); - return Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .header("Content-Type", "application/json") - .body(Body::from(error_body)) - .unwrap()); - } - }; - - let turned_on = all_tools.keys().cloned().collect::>(); - let allow_experimental = gcx.read().await.cmdline.experimental; + let all_tools = get_available_tools(gcx.clone(), true).await.map_err(|e| { + ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, e) + })?; - let tool_desclist = tool_description_list_from_yaml(all_tools, Some(&turned_on), allow_experimental).await.unwrap_or_else(|e| { - tracing::error!("Error loading compiled_in_tools: {:?}", e); - vec![] - }); + let tools_desc: Vec = all_tools.values().map(|tool| { + tool.tool_description() + }).collect(); - let tools_openai_stype = tool_desclist.into_iter().map(|x| x.into_openai_style()).collect::>(); + let body = serde_json::to_string_pretty(&tools_desc) + .map_err(|e| ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, format!("JSON problem: {}", e)))?; - let body = serde_json::to_string_pretty(&tools_openai_stype).map_err(|e| ScratchError::new(StatusCode::UNPROCESSABLE_ENTITY, format!("JSON problem: {}", e)))?; Ok(Response::builder() .status(StatusCode::OK) .header("Content-Type", "application/json") @@ -144,7 +131,7 @@ pub async fn handle_v1_tools_check_if_confirmation_needed( "".to_string(), ).await)); // used only for should_confirm - let all_tools = match tools_merged_and_filtered(gcx.clone(), true).await { + let all_tools = match get_available_tools(gcx.clone(), true).await { Ok(tools) => tools, Err(e) => { let error_body = serde_json::json!({ "detail": e }).to_string(); @@ -248,7 +235,7 @@ pub async fn handle_v1_tools_execute( ccx.postprocess_parameters = tools_execute_post.postprocess_parameters.clone(); let ccx_arc = Arc::new(AMutex::new(ccx)); - let mut at_tools = tools_merged_and_filtered(gcx.clone(), false).await.map_err(|e|{ + let mut at_tools = get_available_tools(gcx.clone(), false).await.map_err(|e|{ ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, format!("Error getting at_tools: {}", e)) })?; let (messages, tools_ran) = run_tools( From 3e76898b57ea14ed0ac83f5bc3f16ef6b02e5748 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 8 May 2025 13:46:06 +0200 Subject: [PATCH 34/90] remove tool_name() in favor of tool_description().name --- .../engine/src/integrations/mcp/tool_mcp.rs | 38 +++++++++---------- .../src/integrations/running_integrations.rs | 2 +- refact-agent/engine/src/tools/tool_mv.rs | 4 -- refact-agent/engine/src/tools/tool_rm.rs | 4 -- .../engine/src/tools/tools_description.rs | 4 -- 5 files changed, 20 insertions(+), 32 deletions(-) diff --git a/refact-agent/engine/src/integrations/mcp/tool_mcp.rs b/refact-agent/engine/src/integrations/mcp/tool_mcp.rs index 5e941d060..5a38cb233 100644 --- a/refact-agent/engine/src/integrations/mcp/tool_mcp.rs +++ b/refact-agent/engine/src/integrations/mcp/tool_mcp.rs @@ -212,6 +212,25 @@ impl Tool for ToolMCP { } } + let tool_name = { + let yaml_name = std::path::Path::new(&self.config_path) + .file_stem() + .and_then(|name| name.to_str()) + .unwrap_or("unknown"); + let shortened_yaml_name = if let Some(stripped) = yaml_name.strip_prefix("mcp_stdio_") { + format!("mcp_{}", stripped) + } else if let Some(stripped) = yaml_name.strip_prefix("mcp_sse_") { + format!("mcp_{}", stripped) + } else { + yaml_name.to_string() + }; + let sanitized_tool_name = format!("{}_{}", shortened_yaml_name, self.mcp_tool.name) + .chars() + .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) + .collect::(); + sanitized_tool_name + }; + ToolDesc { name: self.tool_name(), agentic: true, @@ -222,25 +241,6 @@ impl Tool for ToolMCP { } } - fn tool_name(&self) -> String { - let yaml_name = std::path::Path::new(&self.config_path) - .file_stem() - .and_then(|name| name.to_str()) - .unwrap_or("unknown"); - let shortened_yaml_name = if let Some(stripped) = yaml_name.strip_prefix("mcp_stdio_") { - format!("mcp_{}", stripped) - } else if let Some(stripped) = yaml_name.strip_prefix("mcp_sse_") { - format!("mcp_{}", stripped) - } else { - yaml_name.to_string() - }; - let sanitized_tool_name = format!("{}_{}", shortened_yaml_name, self.mcp_tool.name) - .chars() - .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) - .collect::(); - sanitized_tool_name - } - async fn command_to_match_against_confirm_deny( &self, _ccx: Arc>, diff --git a/refact-agent/engine/src/integrations/running_integrations.rs b/refact-agent/engine/src/integrations/running_integrations.rs index e49676075..ced6175af 100644 --- a/refact-agent/engine/src/integrations/running_integrations.rs +++ b/refact-agent/engine/src/integrations/running_integrations.rs @@ -16,7 +16,7 @@ pub async fn load_integration_tools( let mut tools = IndexMap::new(); for (name, integr) in integraions_map { for tool in integr.integr_tools(&name).await { - let mut tool_name = tool.tool_name(); + let mut tool_name = tool.tool_description().name; if tool_name.is_empty() { tool_name = name.clone(); } diff --git a/refact-agent/engine/src/tools/tool_mv.rs b/refact-agent/engine/src/tools/tool_mv.rs index 243519d5e..e1166b985 100644 --- a/refact-agent/engine/src/tools/tool_mv.rs +++ b/refact-agent/engine/src/tools/tool_mv.rs @@ -326,10 +326,6 @@ impl Tool for ToolMv { }) } - fn tool_name(&self) -> String { - "mv".to_string() - } - fn tool_description(&self) -> ToolDesc { ToolDesc { name: "mv".to_string(), diff --git a/refact-agent/engine/src/tools/tool_rm.rs b/refact-agent/engine/src/tools/tool_rm.rs index 41fb14b56..be1de6f9f 100644 --- a/refact-agent/engine/src/tools/tool_rm.rs +++ b/refact-agent/engine/src/tools/tool_rm.rs @@ -259,10 +259,6 @@ impl Tool for ToolRm { Ok((corrections, messages)) } - fn tool_name(&self) -> String { - "rm".to_string() - } - fn tool_description(&self) -> ToolDesc { ToolDesc { name: "rm".to_string(), diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 6c04007d8..d2012f5a8 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -129,10 +129,6 @@ pub trait Tool: Send + Sync { #[allow(static_mut_refs)] unsafe { &mut DEFAULT_USAGE } } - - fn tool_name(&self) -> String { - return "".to_string(); - } } pub fn get_builtin_tools() -> IndexMap> { From 8e177fee1718692bb015629771a0c3f08419b092 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Fri, 9 May 2025 12:48:53 +0200 Subject: [PATCH 35/90] chage tools definition and move helpers to get all tools to another file --- .../engine/src/tools/tools_description.rs | 126 ++++++------------ refact-agent/engine/src/tools/tools_list.rs | 93 +++++++++++++ 2 files changed, 132 insertions(+), 87 deletions(-) create mode 100644 refact-agent/engine/src/tools/tools_list.rs diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index d2012f5a8..71bbaa668 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -4,18 +4,12 @@ use indexmap::IndexMap; use serde_json::{Value, json}; use serde::{Deserialize, Serialize}; use async_trait::async_trait; -use tokio::sync::RwLock as ARwLock; use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ChatUsage, ContextEnum}; -use crate::global_context::try_load_caps_quickly_if_not_present; -use crate::global_context::GlobalContext; use crate::integrations::integr_abstract::IntegrationConfirmation; -use crate::integrations::running_integrations::load_integration_tools; use crate::tools::tools_execute::{command_should_be_confirmed_by_user, command_should_be_denied}; -// use crate::integrations::docker::integr_docker::ToolDocker; - #[derive(Clone, Debug)] pub enum MatchConfirmDenyResult { @@ -31,6 +25,31 @@ pub struct MatchConfirmDeny { pub rule: String, } +pub enum ToolGroupCategory { + Builtin, + Integration, + MCP, +} + +pub struct ToolGroup { + pub name: String, + pub description: String, + pub category: ToolGroupCategory, + pub tools: Vec>, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub enum ToolSourceType { + Builtin, + Integration, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct ToolSource { + pub source_type: ToolSourceType, + pub config_path: String, +} + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ToolDesc { pub name: String, @@ -41,6 +60,8 @@ pub struct ToolDesc { pub description: String, pub parameters: Vec, pub parameters_required: Vec, + pub display_name: String, + pub source: ToolSource, } #[derive(Clone, Serialize, Deserialize, Debug)] @@ -60,7 +81,7 @@ pub trait Tool: Send + Sync { &mut self, ccx: Arc>, tool_call_id: &String, - args: &HashMap + args: &HashMap, ) -> Result<(bool, Vec), String>; fn tool_description(&self) -> ToolDesc; @@ -131,75 +152,6 @@ pub trait Tool: Send + Sync { } } -pub fn get_builtin_tools() -> IndexMap> { - IndexMap::from([ - ("search_symbol_definition".to_string(), Box::new(crate::tools::tool_ast_definition::ToolAstDefinition{}) as Box), - ("search_symbol_usages".to_string(), Box::new(crate::tools::tool_ast_reference::ToolAstReference{}) as Box), - ("tree".to_string(), Box::new(crate::tools::tool_tree::ToolTree{}) as Box), - ("create_textdoc".to_string(), Box::new(crate::tools::file_edit::tool_create_textdoc::ToolCreateTextDoc{}) as Box), - ("update_textdoc".to_string(), Box::new(crate::tools::file_edit::tool_update_textdoc::ToolUpdateTextDoc {}) as Box), - ("update_textdoc_regex".to_string(), Box::new(crate::tools::file_edit::tool_update_textdoc_regex::ToolUpdateTextDocRegex {}) as Box), - ("web".to_string(), Box::new(crate::tools::tool_web::ToolWeb{}) as Box), - ("cat".to_string(), Box::new(crate::tools::tool_cat::ToolCat{}) as Box), - ("rm".to_string(), Box::new(crate::tools::tool_rm::ToolRm{}) as Box), - ("mv".to_string(), Box::new(crate::tools::tool_mv::ToolMv{}) as Box), - ("strategic_planning".to_string(), Box::new(crate::tools::tool_strategic_planning::ToolStrategicPlanning{}) as Box), - ("search_pattern".to_string(), Box::new(crate::tools::tool_regex_search::ToolRegexSearch{}) as Box), - ("knowledge".to_string(), Box::new(crate::tools::tool_knowledge::ToolGetKnowledge{}) as Box), - ("create_knowledge".to_string(), Box::new(crate::tools::tool_create_knowledge::ToolCreateKnowledge{}) as Box), - ("create_memory_bank".to_string(), Box::new(crate::tools::tool_create_memory_bank::ToolCreateMemoryBank{}) as Box), - ("search_semantic".to_string(), Box::new(crate::tools::tool_search::ToolSearch{}) as Box), - ("locate".to_string(), Box::new(crate::tools::tool_locate_search::ToolLocateSearch{}) as Box), - ]) -} - -pub async fn get_available_tools( - gcx: Arc>, - _supports_clicks: bool, // XXX -) -> Result>, String> { - let (ast_on, vecdb_on, allow_experimental) = { - let gcx_locked = gcx.read().await; - let vecdb_on = gcx_locked.vec_db.lock().await.is_some(); - (gcx_locked.ast_service.is_some(), vecdb_on, gcx_locked.cmdline.experimental) - }; - - let (is_there_a_thinking_model, allow_knowledge) = match try_load_caps_quickly_if_not_present(gcx.clone(), 0).await { - Ok(caps) => { - (caps.chat_models.get(&caps.defaults.chat_thinking_model).is_some(), - caps.metadata.features.contains(&"knowledge".to_string())) - }, - Err(_) => (false, false), - }; - - let mut tools_all = get_builtin_tools(); - tools_all.extend( - load_integration_tools(gcx, allow_experimental).await - ); - - let mut filtered_tools = IndexMap::new(); - for (tool_name, tool) in tools_all { - let dependencies = tool.tool_depends_on(); - if dependencies.contains(&"ast".to_string()) && !ast_on { - continue; - } - if dependencies.contains(&"vecdb".to_string()) && !vecdb_on { - continue; - } - if dependencies.contains(&"thinking".to_string()) && !is_there_a_thinking_model { - continue; - } - if dependencies.contains(&"knowledge".to_string()) && !allow_knowledge { - continue; - } - if tool.tool_description().experimental && !allow_experimental { - continue; - } - filtered_tools.insert(tool_name, tool); - } - - Ok(filtered_tools) -} - fn validate_snake_case<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -246,18 +198,18 @@ pub fn make_openai_tool_value( }).collect::>(); let function_json = json!({ - "type": "function", - "function": { - "name": name, - "agentic": agentic, // this field is not OpenAI's - "description": description, - "parameters": { - "type": "object", - "properties": params_properties, - "required": parameters_required - } + "type": "function", + "function": { + "name": name, + "agentic": agentic, // this field is not OpenAI's + "description": description, + "parameters": { + "type": "object", + "properties": params_properties, + "required": parameters_required } - }); + } + }); function_json } diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs new file mode 100644 index 000000000..b1be76a27 --- /dev/null +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -0,0 +1,93 @@ +use std::sync::Arc; + +use indexmap::IndexMap; +use tokio::sync::RwLock as ARwLock; + +use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; +use crate::integrations::running_integrations::load_integration_tools; + +use super::tools_description::{Tool, ToolGroup, ToolGroupCategory}; + + +pub async fn get_builtin_tools( + gcx: Arc>, +) -> Vec { + let config_dir = gcx.read().await.config_dir.clone(); + let config_path = config_dir.join("builtin_tools.yaml").to_string_lossy().to_string(); + + let mut tools = vec![ + Box::new(crate::tools::tool_ast_definition::ToolAstDefinition{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_ast_reference::ToolAstReference{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_tree::ToolTree{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::file_edit::tool_create_textdoc::ToolCreateTextDoc{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::file_edit::tool_update_textdoc::ToolUpdateTextDoc {config_path: config_path.clone()}) as Box, + Box::new(crate::tools::file_edit::tool_update_textdoc_regex::ToolUpdateTextDocRegex {config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_web::ToolWeb{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_cat::ToolCat{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_rm::ToolRm{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_mv::ToolMv{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_deep_analysis::ToolDeepAnalysis{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_regex_search::ToolRegexSearch{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_knowledge::ToolGetKnowledge{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_create_knowledge::ToolCreateKnowledge{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_create_memory_bank::ToolCreateMemoryBank{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_search::ToolSearch{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_locate_search::ToolLocateSearch{config_path: config_path.clone()}) as Box, + ]; + + vec![ + ToolGroup { + name: "builtin".to_string(), + description: "Builtin tools".to_string(), + category: ToolGroupCategory::Builtin, + tools, + }, + ] +} + +pub async fn get_available_tools( + gcx: Arc>, + _supports_clicks: bool, // XXX +) -> Result>, String> { + let (ast_on, vecdb_on, allow_experimental) = { + let gcx_locked = gcx.read().await; + let vecdb_on = gcx_locked.vec_db.lock().await.is_some(); + (gcx_locked.ast_service.is_some(), vecdb_on, gcx_locked.cmdline.experimental) + }; + + let (is_there_a_thinking_model, allow_knowledge) = match try_load_caps_quickly_if_not_present(gcx.clone(), 0).await { + Ok(caps) => { + (caps.chat_models.get(&caps.defaults.chat_thinking_model).is_some(), + caps.metadata.features.contains(&"knowledge".to_string())) + }, + Err(_) => (false, false), + }; + + let mut tools_all = get_builtin_tools(); + tools_all.extend( + load_integration_tools(gcx, allow_experimental).await + ); + + let mut filtered_tools = IndexMap::new(); + for (tool_name, tool) in tools_all { + let dependencies = tool.tool_depends_on(); + if dependencies.contains(&"ast".to_string()) && !ast_on { + continue; + } + if dependencies.contains(&"vecdb".to_string()) && !vecdb_on { + continue; + } + if dependencies.contains(&"thinking".to_string()) && !is_there_a_thinking_model { + continue; + } + if dependencies.contains(&"knowledge".to_string()) && !allow_knowledge { + continue; + } + if tool.tool_description().experimental && !allow_experimental { + continue; + } + filtered_tools.insert(tool_name, tool); + } + + Ok(filtered_tools) +} From b03648ceaf199b1fac44d7ac35642ae3e9b38561 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Fri, 9 May 2025 12:49:38 +0200 Subject: [PATCH 36/90] fix: include mod --- refact-agent/engine/src/tools/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/refact-agent/engine/src/tools/mod.rs b/refact-agent/engine/src/tools/mod.rs index 6d87f5774..594ae2ed0 100644 --- a/refact-agent/engine/src/tools/mod.rs +++ b/refact-agent/engine/src/tools/mod.rs @@ -1,4 +1,5 @@ pub mod tools_description; +pub mod tools_list; pub mod tools_execute; pub mod scope_utils; From e472b39c1ae76ba2644cf5a941782169abd0602d Mon Sep 17 00:00:00 2001 From: MDario123 Date: Fri, 9 May 2025 12:50:50 +0200 Subject: [PATCH 37/90] impl display name and config path for some builtin tools --- .../engine/src/tools/file_edit/tool_create_textdoc.rs | 11 +++++++++-- .../engine/src/tools/file_edit/tool_update_textdoc.rs | 11 +++++++++-- .../src/tools/file_edit/tool_update_textdoc_regex.rs | 11 +++++++++-- refact-agent/engine/src/tools/tool_ast_definition.rs | 11 +++++++++-- refact-agent/engine/src/tools/tool_ast_reference.rs | 11 +++++++++-- refact-agent/engine/src/tools/tool_mv.rs | 2 +- refact-agent/engine/src/tools/tool_rm.rs | 2 +- refact-agent/engine/src/tools/tool_tree.rs | 11 +++++++++-- 8 files changed, 56 insertions(+), 14 deletions(-) diff --git a/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs b/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs index 6c4724fce..84a72ff57 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_create_textdoc.rs @@ -5,7 +5,7 @@ use crate::privacy::{check_file_privacy, load_privacy_if_needed, FilePrivacyLeve use crate::tools::file_edit::auxiliary::{ await_ast_indexing, convert_edit_to_diffchunks, sync_documents_ast, write_file, }; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use async_trait::async_trait; use serde_json::{json, Value}; use std::collections::HashMap; @@ -22,7 +22,9 @@ struct ToolCreateTextDocArgs { content: String, } -pub struct ToolCreateTextDoc; +pub struct ToolCreateTextDoc { + pub config_path: String, +} async fn parse_args( gcx: Arc>, @@ -185,6 +187,11 @@ impl Tool for ToolCreateTextDoc { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "create_textdoc".to_string(), + display_name: "Create Text Document".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Creates a new text document or code or completely replaces the content of an existing document. Avoid trailing spaces and tabs.".to_string(), diff --git a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs index b59332866..f7448d033 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs @@ -3,7 +3,7 @@ use crate::call_validation::{ChatContent, ChatMessage, ContextEnum, DiffChunk}; use crate::integrations::integr_abstract::IntegrationConfirmation; use crate::privacy::{check_file_privacy, load_privacy_if_needed, FilePrivacyLevel, PrivacySettings}; use crate::tools::file_edit::auxiliary::{await_ast_indexing, convert_edit_to_diffchunks, str_replace, sync_documents_ast}; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use async_trait::async_trait; use serde_json::{json, Value}; use std::collections::HashMap; @@ -22,7 +22,9 @@ struct ToolUpdateTextDocArgs { multiple: bool, } -pub struct ToolUpdateTextDoc; +pub struct ToolUpdateTextDoc { + pub config_path: String, +} async fn parse_args( gcx: Arc>, @@ -178,6 +180,11 @@ impl Tool for ToolUpdateTextDoc { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "update_textdoc".to_string(), + display_name: "Update Text Document".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Updates an existing document by replacing specific text, use this if file already exists. Optimized for large files or small changes where simple string replacement is sufficient. Avoid trailing spaces and tabs.".to_string(), diff --git a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs index 324ac9edf..98ba5502e 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs @@ -3,7 +3,7 @@ use crate::call_validation::{ChatContent, ChatMessage, ContextEnum, DiffChunk}; use crate::integrations::integr_abstract::IntegrationConfirmation; use crate::privacy::{check_file_privacy, load_privacy_if_needed, FilePrivacyLevel, PrivacySettings}; use crate::tools::file_edit::auxiliary::{await_ast_indexing, convert_edit_to_diffchunks, str_replace_regex, sync_documents_ast}; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use async_trait::async_trait; use serde_json::{json, Value}; use std::collections::HashMap; @@ -23,7 +23,9 @@ struct ToolUpdateTextDocRegexArgs { multiple: bool, } -pub struct ToolUpdateTextDocRegex; +pub struct ToolUpdateTextDocRegex { + pub config_path: String, +} async fn parse_args( gcx: Arc>, @@ -186,6 +188,11 @@ impl Tool for ToolUpdateTextDocRegex { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "update_textdoc_regex".to_string(), + display_name: "Update Text Document with Regex".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Updates an existing document using regex pattern matching. Ideal when changes can be expressed as a regular expression or when you need to match variable text patterns. Avoid trailing spaces and tabs.".to_string(), diff --git a/refact-agent/engine/src/tools/tool_ast_definition.rs b/refact-agent/engine/src/tools/tool_ast_definition.rs index 76a6001b8..7dc69f837 100644 --- a/refact-agent/engine/src/tools/tool_ast_definition.rs +++ b/refact-agent/engine/src/tools/tool_ast_definition.rs @@ -7,10 +7,12 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::ast::ast_structs::AstDB; use crate::ast::ast_db::fetch_counters; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; -pub struct ToolAstDefinition; +pub struct ToolAstDefinition { + pub config_path: String, +} #[async_trait] impl Tool for ToolAstDefinition { @@ -108,6 +110,11 @@ impl Tool for ToolAstDefinition { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "search_symbol_definition".to_string(), + display_name: "Definition".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Find definition of a symbol in the project using AST".to_string(), diff --git a/refact-agent/engine/src/tools/tool_ast_reference.rs b/refact-agent/engine/src/tools/tool_ast_reference.rs index c9c0b06a9..859e4aaa0 100644 --- a/refact-agent/engine/src/tools/tool_ast_reference.rs +++ b/refact-agent/engine/src/tools/tool_ast_reference.rs @@ -6,11 +6,13 @@ use serde_json::Value; use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; use crate::tools::tool_ast_definition::there_are_definitions_with_similar_names_though; -pub struct ToolAstReference; +pub struct ToolAstReference { + pub config_path: String, +} #[async_trait] impl Tool for ToolAstReference { @@ -132,6 +134,11 @@ impl Tool for ToolAstReference { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "search_symbol_usages".to_string(), + display_name: "References".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Find usages of a symbol within a project using AST".to_string(), diff --git a/refact-agent/engine/src/tools/tool_mv.rs b/refact-agent/engine/src/tools/tool_mv.rs index e1166b985..6b0fda617 100644 --- a/refact-agent/engine/src/tools/tool_mv.rs +++ b/refact-agent/engine/src/tools/tool_mv.rs @@ -352,4 +352,4 @@ impl Tool for ToolMv { parameters_required: vec!["source".to_string(), "destination".to_string()], } } -} \ No newline at end of file +} diff --git a/refact-agent/engine/src/tools/tool_rm.rs b/refact-agent/engine/src/tools/tool_rm.rs index be1de6f9f..497d69d6f 100644 --- a/refact-agent/engine/src/tools/tool_rm.rs +++ b/refact-agent/engine/src/tools/tool_rm.rs @@ -290,4 +290,4 @@ impl Tool for ToolRm { parameters_required: vec!["path".to_string()], } } -} \ No newline at end of file +} diff --git a/refact-agent/engine/src/tools/tool_tree.rs b/refact-agent/engine/src/tools/tool_tree.rs index 6c1bc51dc..bd98a515b 100644 --- a/refact-agent/engine/src/tools/tool_tree.rs +++ b/refact-agent/engine/src/tools/tool_tree.rs @@ -8,13 +8,15 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_file::return_one_candidate_or_a_good_error; use crate::at_commands::at_tree::{construct_tree_out_of_flat_list_of_paths, print_files_tree_with_budget}; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::files_correction::{correct_to_nearest_dir_path, correct_to_nearest_filename, get_project_dirs, paths_from_anywhere}; use crate::files_in_workspace::ls_files; -pub struct ToolTree; +pub struct ToolTree { + pub config_path: String, +} fn preformat_path(path: &String) -> String { path.trim_end_matches(&['/', '\\'][..]).to_string() @@ -27,6 +29,11 @@ impl Tool for ToolTree { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "tree".to_string(), + display_name: "Tree".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Get a files tree with symbols for the project. Use it to get familiar with the project, file names and symbols".to_string(), From 486d9030c91eebd738733bfe1fb22ce5dcfc78bd Mon Sep 17 00:00:00 2001 From: MDario123 Date: Fri, 9 May 2025 13:04:33 +0200 Subject: [PATCH 38/90] display name and config path for all builtin tools --- refact-agent/engine/src/tools/tool_cat.rs | 11 ++++++++-- .../engine/src/tools/tool_create_knowledge.rs | 11 ++++++++-- .../src/tools/tool_create_memory_bank.rs | 17 ++++++++++----- .../engine/src/tools/tool_knowledge.rs | 13 +++++++++--- .../engine/src/tools/tool_locate_search.rs | 21 +++++++++++-------- refact-agent/engine/src/tools/tool_mv.rs | 11 ++++++++-- .../engine/src/tools/tool_regex_search.rs | 11 ++++++++-- refact-agent/engine/src/tools/tool_rm.rs | 11 ++++++++-- refact-agent/engine/src/tools/tool_search.rs | 11 ++++++++-- .../src/tools/tool_strategic_planning.rs | 11 ++++++++-- refact-agent/engine/src/tools/tool_web.rs | 11 ++++++++-- refact-agent/engine/src/tools/tools_list.rs | 2 +- 12 files changed, 107 insertions(+), 34 deletions(-) diff --git a/refact-agent/engine/src/tools/tool_cat.rs b/refact-agent/engine/src/tools/tool_cat.rs index 082398235..b7d8da267 100644 --- a/refact-agent/engine/src/tools/tool_cat.rs +++ b/refact-agent/engine/src/tools/tool_cat.rs @@ -9,7 +9,7 @@ use async_trait::async_trait; use resvg::{tiny_skia, usvg}; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_file::{file_repair_candidates, return_one_candidate_or_a_good_error}; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; use crate::files_correction::{correct_to_nearest_dir_path, get_project_dirs}; use crate::files_in_workspace::{get_file_text_from_memory_or_disk, ls_files}; @@ -19,7 +19,9 @@ use std::io::Cursor; use image::imageops::FilterType; use image::{ImageFormat, ImageReader}; -pub struct ToolCat; +pub struct ToolCat { + pub config_path: String, +} const CAT_MAX_IMAGES_CNT: usize = 1; @@ -103,6 +105,11 @@ impl Tool for ToolCat { ToolDesc { name: "cat".to_string(), agentic: false, + display_name: "Cat".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, experimental: false, description: "Like cat in console, but better: it can read multiple files and images. Prefer to open full files.".to_string(), parameters: vec![ diff --git a/refact-agent/engine/src/tools/tool_create_knowledge.rs b/refact-agent/engine/src/tools/tool_create_knowledge.rs index 4440a4ac2..0d14e978f 100644 --- a/refact-agent/engine/src/tools/tool_create_knowledge.rs +++ b/refact-agent/engine/src/tools/tool_create_knowledge.rs @@ -7,9 +7,11 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; -pub struct ToolCreateKnowledge; +pub struct ToolCreateKnowledge { + pub config_path: String, +} #[async_trait] impl Tool for ToolCreateKnowledge { @@ -18,6 +20,11 @@ impl Tool for ToolCreateKnowledge { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "create_knowledge".to_string(), + display_name: "Create Knowledge".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Creates a new knowledge entry in the vector database to help with future tasks.".to_string(), diff --git a/refact-agent/engine/src/tools/tool_create_memory_bank.rs b/refact-agent/engine/src/tools/tool_create_memory_bank.rs index 55cc1448c..1e15b2e7c 100644 --- a/refact-agent/engine/src/tools/tool_create_memory_bank.rs +++ b/refact-agent/engine/src/tools/tool_create_memory_bank.rs @@ -20,7 +20,7 @@ use crate::{ global_context::GlobalContext, postprocessing::pp_context_files::postprocess_context_files, subchat::subchat, - tools::tools_description::Tool, + tools::tools_description::{Tool, ToolDesc, ToolSource, ToolSourceType}, }; use crate::caps::resolve_chat_model; use crate::global_context::try_load_caps_quickly_if_not_present; @@ -280,7 +280,9 @@ async fn read_and_compress_directory( .collect()) } -pub struct ToolCreateMemoryBank; +pub struct ToolCreateMemoryBank { + pub config_path: String, +} const MB_SYSTEM_PROMPT: &str = r###"• Objective: – Create a clear, natural language description of the project structure while building a comprehensive architectural understanding. @@ -485,9 +487,14 @@ impl Tool for ToolCreateMemoryBank { vec![] } - fn tool_description(&self) -> crate::tools::tools_description::ToolDesc { - crate::tools::tools_description::ToolDesc { + fn tool_description(&self) -> ToolDesc { + ToolDesc { name: "create_memory_bank".into(), + display_name: "Create Memory Bank".into(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: true, experimental: true, description: "Gathers information about the project structure (modules, file relations, classes, etc.) and saves this data into the memory bank.".into(), @@ -495,4 +502,4 @@ impl Tool for ToolCreateMemoryBank { parameters_required: Vec::new(), } } -} \ No newline at end of file +} diff --git a/refact-agent/engine/src/tools/tool_knowledge.rs b/refact-agent/engine/src/tools/tool_knowledge.rs index cfe8e5275..327a5622d 100644 --- a/refact-agent/engine/src/tools/tool_knowledge.rs +++ b/refact-agent/engine/src/tools/tool_knowledge.rs @@ -6,12 +6,14 @@ use tokio::sync::Mutex as AMutex; use async_trait::async_trait; use crate::at_commands::at_commands::AtCommandsContext; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::memories::memories_search; -pub struct ToolGetKnowledge; +pub struct ToolGetKnowledge { + pub config_path: String, +} #[async_trait] @@ -21,6 +23,11 @@ impl Tool for ToolGetKnowledge { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "knowledge".to_string(), + display_name: "Knowledge".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Fetches successful trajectories to help you accomplish your task. Call each time you have a new task to increase your chances of success.".to_string(), @@ -86,4 +93,4 @@ impl Tool for ToolGetKnowledge { fn tool_depends_on(&self) -> Vec { vec!["knowledge".to_string()] } -} \ No newline at end of file +} diff --git a/refact-agent/engine/src/tools/tool_locate_search.rs b/refact-agent/engine/src/tools/tool_locate_search.rs index 5b1b0371f..dbb571c6e 100644 --- a/refact-agent/engine/src/tools/tool_locate_search.rs +++ b/refact-agent/engine/src/tools/tool_locate_search.rs @@ -10,19 +10,20 @@ use axum::http::StatusCode; use indexmap::IndexMap; use hashbrown::HashSet; use crate::subchat::subchat; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ChatUsage, ContextEnum, SubchatParameters, ContextFile, PostprocessSettings}; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_file::{file_repair_candidates, return_one_candidate_or_a_good_error}; use crate::caps::resolve_chat_model; use crate::custom_error::ScratchError; -use crate::files_correction::{canonicalize_normalized_path, get_project_dirs, preprocess_path_for_normalization}; -use crate::files_in_workspace::get_file_text_from_memory_or_disk; +use crate::files_correction::{canonicalize_normalized_path, get_project_dirs, preprocess_path_for_normalization}; +use crate::files_in_workspace::get_file_text_from_memory_or_disk; use crate::postprocessing::pp_context_files::postprocess_context_files; use crate::tokens::count_text_tokens_with_fallback; - -pub struct ToolLocateSearch; +pub struct ToolLocateSearch { + pub config_path: String, +} const LS_SYSTEM_PROMPT: &str = r###"**Task** @@ -183,10 +184,7 @@ async fn _make_prompt( &mut context_files, tokenizer.clone(), subchat_params.subchat_tokens_for_rag + tokens_budget.max(0) as usize, - false, - &pp_settings, - ).await { - files_context.push_str( + false, &pp_settings,).await { files_context.push_str( &format!("📎 {}:{}-{}\n```\n{}```\n\n", context_file.file_name, context_file.line1, @@ -218,6 +216,11 @@ impl Tool for ToolLocateSearch { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "locate".to_string(), + display_name: "Locate".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Get a list of files that are relevant to solve a particular task.".to_string(), diff --git a/refact-agent/engine/src/tools/tool_mv.rs b/refact-agent/engine/src/tools/tool_mv.rs index 6b0fda617..2e4d4dd90 100644 --- a/refact-agent/engine/src/tools/tool_mv.rs +++ b/refact-agent/engine/src/tools/tool_mv.rs @@ -12,11 +12,13 @@ use crate::at_commands::at_file::return_one_candidate_or_a_good_error; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, DiffChunk}; use crate::files_correction::{canonical_path, correct_to_nearest_dir_path, correct_to_nearest_filename, get_project_dirs, preprocess_path_for_normalization}; use crate::files_in_workspace::get_file_text_from_memory_or_disk; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::integrations::integr_abstract::IntegrationConfirmation; use crate::privacy::{FilePrivacyLevel, load_privacy_if_needed, check_file_privacy}; -pub struct ToolMv; +pub struct ToolMv { + pub config_path: String, +} impl ToolMv { fn preformat_path(path: &String) -> String { @@ -329,6 +331,11 @@ impl Tool for ToolMv { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "mv".to_string(), + display_name: "mv".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Moves or renames files and directories. If a simple rename fails due to a cross-device error and the source is a file, it falls back to copying and deleting. Use overwrite=true to replace an existing target.".to_string(), diff --git a/refact-agent/engine/src/tools/tool_regex_search.rs b/refact-agent/engine/src/tools/tool_regex_search.rs index f27d0cbc7..bd14d50b8 100644 --- a/refact-agent/engine/src/tools/tool_regex_search.rs +++ b/refact-agent/engine/src/tools/tool_regex_search.rs @@ -18,9 +18,11 @@ use crate::files_correction::shortify_paths; use crate::files_in_workspace::get_file_text_from_memory_or_disk; use crate::global_context::GlobalContext; use crate::tools::scope_utils::{resolve_scope, validate_scope_files}; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; -pub struct ToolRegexSearch; +pub struct ToolRegexSearch { + pub config_path: String, +} async fn search_single_file( gcx: Arc>, @@ -168,6 +170,11 @@ impl Tool for ToolRegexSearch { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "search_pattern".to_string(), + display_name: "Regex Search".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Search for files and folders whose names or paths match the given regular expression patterns, and also search for text matches inside files using the same patterns. Reports both path matches and text matches in separate sections.".to_string(), diff --git a/refact-agent/engine/src/tools/tool_rm.rs b/refact-agent/engine/src/tools/tool_rm.rs index 497d69d6f..7e15eb66a 100644 --- a/refact-agent/engine/src/tools/tool_rm.rs +++ b/refact-agent/engine/src/tools/tool_rm.rs @@ -12,10 +12,12 @@ use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, DiffChunk}; use crate::files_correction::{canonical_path, correct_to_nearest_dir_path, correct_to_nearest_filename, get_project_dirs, preprocess_path_for_normalization}; use crate::files_in_workspace::get_file_text_from_memory_or_disk; use crate::privacy::{check_file_privacy, load_privacy_if_needed, FilePrivacyLevel}; -use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{MatchConfirmDeny, MatchConfirmDenyResult, Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::integrations::integr_abstract::IntegrationConfirmation; -pub struct ToolRm; +pub struct ToolRm { + pub config_path: String, +} impl ToolRm { fn preformat_path(path: &String) -> String { @@ -262,6 +264,11 @@ impl Tool for ToolRm { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "rm".to_string(), + display_name: "rm".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Deletes a file or directory. Use recursive=true for directories. Set dry_run=true to preview without deletion.".to_string(), diff --git a/refact-agent/engine/src/tools/tool_search.rs b/refact-agent/engine/src/tools/tool_search.rs index 774f269a2..86e117a24 100644 --- a/refact-agent/engine/src/tools/tool_search.rs +++ b/refact-agent/engine/src/tools/tool_search.rs @@ -10,11 +10,13 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::{vec_context_file_to_context_tools, AtCommandsContext}; use crate::at_commands::at_search::execute_at_search; use crate::tools::scope_utils::create_scope_filter; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; -pub struct ToolSearch; +pub struct ToolSearch { + pub config_path: String, +} async fn execute_att_search( ccx: Arc>, @@ -37,6 +39,11 @@ impl Tool for ToolSearch { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "search_semantic".to_string(), + display_name: "Search".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Find semantically similar pieces of code or text using vector database (semantic search)".to_string(), diff --git a/refact-agent/engine/src/tools/tool_strategic_planning.rs b/refact-agent/engine/src/tools/tool_strategic_planning.rs index a773e8c69..03fb2b362 100644 --- a/refact-agent/engine/src/tools/tool_strategic_planning.rs +++ b/refact-agent/engine/src/tools/tool_strategic_planning.rs @@ -7,7 +7,7 @@ use tokio::sync::Mutex as AMutex; use async_trait::async_trait; use axum::http::StatusCode; use crate::subchat::subchat_single; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ChatUsage, ContextEnum, SubchatParameters, ContextFile, PostprocessSettings}; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_file::{file_repair_candidates, return_one_candidate_or_a_good_error}; @@ -19,7 +19,9 @@ use crate::global_context::try_load_caps_quickly_if_not_present; use crate::postprocessing::pp_context_files::postprocess_context_files; use crate::tokens::count_text_tokens_with_fallback; -pub struct ToolStrategicPlanning; +pub struct ToolStrategicPlanning { + pub config_path: String, +} static TOKENS_EXTRA_BUDGET_PERCENT: f32 = 0.06; @@ -167,6 +169,11 @@ impl Tool for ToolStrategicPlanning { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "strategic_planning".to_string(), + display_name: "Strategic Planning".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Strategically plan a solution for a complex problem or create a comprehensive approach.".to_string(), diff --git a/refact-agent/engine/src/tools/tool_web.rs b/refact-agent/engine/src/tools/tool_web.rs index 5eaade40c..155308c9f 100644 --- a/refact-agent/engine/src/tools/tool_web.rs +++ b/refact-agent/engine/src/tools/tool_web.rs @@ -6,11 +6,13 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::at_web::execute_at_web; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; -pub struct ToolWeb; +pub struct ToolWeb { + pub config_path: String, +} #[async_trait] impl Tool for ToolWeb { @@ -19,6 +21,11 @@ impl Tool for ToolWeb { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "web".to_string(), + display_name: "Web".to_string(), + source: ToolSource { + source_type: ToolSourceType::Builtin, + config_path: self.config_path.clone(), + }, agentic: false, experimental: false, description: "Fetch a web page and convert to readable plain text.".to_string(), diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index b1be76a27..dbe9adad5 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -26,7 +26,7 @@ pub async fn get_builtin_tools( Box::new(crate::tools::tool_cat::ToolCat{config_path: config_path.clone()}) as Box, Box::new(crate::tools::tool_rm::ToolRm{config_path: config_path.clone()}) as Box, Box::new(crate::tools::tool_mv::ToolMv{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_deep_analysis::ToolDeepAnalysis{config_path: config_path.clone()}) as Box, + Box::new(crate::tools::tool_strategic_planning::ToolStrategicPlanning{config_path: config_path.clone()}) as Box, Box::new(crate::tools::tool_regex_search::ToolRegexSearch{config_path: config_path.clone()}) as Box, Box::new(crate::tools::tool_knowledge::ToolGetKnowledge{config_path: config_path.clone()}) as Box, Box::new(crate::tools::tool_create_knowledge::ToolCreateKnowledge{config_path: config_path.clone()}) as Box, From 5cbf391af75bfccf0491004ede47f40fac93b5b7 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Fri, 9 May 2025 13:07:37 +0200 Subject: [PATCH 39/90] fix imports for get_available_tools --- refact-agent/engine/src/http/routers/v1/at_tools.rs | 3 ++- refact-agent/engine/src/scratchpads/chat_passthrough.rs | 3 ++- refact-agent/engine/src/subchat.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index ac163a17e..7d1edbbef 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -15,7 +15,8 @@ use crate::http::http_post_json; use crate::http::routers::v1::chat::CHAT_TOP_N; use crate::indexing_utils::wait_for_indexing_if_needed; use crate::integrations::docker::docker_container_manager::docker_container_get_host_lsp_port_to_connect; -use crate::tools::tools_description::{get_available_tools, MatchConfirmDenyResult, ToolDesc}; +use crate::tools::tools_description::{MatchConfirmDenyResult, ToolDesc}; +use crate::tools::tools_list::get_available_tools; use crate::custom_error::ScratchError; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; use crate::tools::tools_execute::run_tools; diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index a5995c98d..6162033fe 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -17,7 +17,8 @@ use crate::scratchpads::chat_utils_limit_history::fix_and_limit_messages_history use crate::scratchpads::scratchpad_utils::HasRagResults; use crate::scratchpads::chat_utils_prompts::prepend_the_right_system_prompt_and_maybe_more_initial_messages; use crate::scratchpads::passthrough_convert_messages::convert_messages_to_openai_format; -use crate::tools::tools_description::{tool_description_list_from_yaml, get_available_tools}; +use crate::tools::tools_description::tool_description_list_from_yaml; +use crate::tools::tools_list::get_available_tools; use crate::tools::tools_execute::{run_tools_locally, run_tools_remotely}; diff --git a/refact-agent/engine/src/subchat.rs b/refact-agent/engine/src/subchat.rs index f47edd0f6..52861e1c2 100644 --- a/refact-agent/engine/src/subchat.rs +++ b/refact-agent/engine/src/subchat.rs @@ -7,7 +7,8 @@ use tracing::{error, info, warn}; use crate::caps::resolve_chat_model; use crate::caps::ChatModelRecord; -use crate::tools::tools_description::{get_available_tools, tool_description_list_from_yaml}; +use crate::tools::tools_description::tool_description_list_from_yaml; +use crate::tools::tools_list::get_available_tools; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{SamplingParameters, PostprocessSettings, ChatPost, ChatMessage, ChatUsage, ChatToolCall, ReasoningEffort}; use crate::global_context::{GlobalContext, try_load_caps_quickly_if_not_present}; From 5cbc27e8d6ddc59843415f0615dc45d3e2c08c6d Mon Sep 17 00:00:00 2001 From: MDario123 Date: Fri, 9 May 2025 13:42:28 +0200 Subject: [PATCH 40/90] load_integrations get allow_experimental from global context --- refact-agent/engine/src/http/routers/v1/links.rs | 3 +-- refact-agent/engine/src/integrations/docker/mod.rs | 2 +- refact-agent/engine/src/integrations/running_integrations.rs | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/links.rs b/refact-agent/engine/src/http/routers/v1/links.rs index 8ed788502..d92e754b6 100644 --- a/refact-agent/engine/src/http/routers/v1/links.rs +++ b/refact-agent/engine/src/http/routers/v1/links.rs @@ -80,8 +80,7 @@ pub async fn handle_v1_links( let mut uncommited_changes_warning = String::new(); tracing::info!("for links, post.meta.chat_mode == {:?}", post.meta.chat_mode); - let experimental = gcx.read().await.cmdline.experimental; - let (_integrations_map, integration_yaml_errors) = crate::integrations::running_integrations::load_integrations(gcx.clone(), experimental, &["**/*".to_string()]).await; + let (_integrations_map, integration_yaml_errors) = crate::integrations::running_integrations::load_integrations(gcx.clone(), &["**/*".to_string()]).await; if post.meta.chat_mode == ChatMode::CONFIGURE { if last_message_assistant_without_tools_with_code_blocks(&post.messages) { diff --git a/refact-agent/engine/src/integrations/docker/mod.rs b/refact-agent/engine/src/integrations/docker/mod.rs index 3a54a22d4..76df858e6 100644 --- a/refact-agent/engine/src/integrations/docker/mod.rs +++ b/refact-agent/engine/src/integrations/docker/mod.rs @@ -15,7 +15,7 @@ pub mod docker_container_manager; pub async fn docker_and_isolation_load(gcx: Arc>) -> Result<(ToolDocker, Option), String> { let include_paths_matching = ["**/docker.yaml".to_string(), "**/isolation.yaml".to_string()]; - let (integrations, _yaml_errors) = load_integrations(gcx.clone(), true, &include_paths_matching).await; + let (integrations, _yaml_errors) = load_integrations(gcx.clone(), &include_paths_matching).await; let docker_tools = integrations.get("docker") .ok_or("Docker integration not found".to_string())? diff --git a/refact-agent/engine/src/integrations/running_integrations.rs b/refact-agent/engine/src/integrations/running_integrations.rs index ced6175af..03cf1a0fe 100644 --- a/refact-agent/engine/src/integrations/running_integrations.rs +++ b/refact-agent/engine/src/integrations/running_integrations.rs @@ -35,14 +35,13 @@ pub async fn load_integration_tools( /// otherwise only those matching `include_paths_matching` glob patterns. pub async fn load_integrations( gcx: Arc>, - allow_experimental: bool, include_paths_matching: &[String], ) -> (IndexMap>, Vec) { let active_project_path = crate::files_correction::get_active_project_path(gcx.clone()).await; let (config_dirs, global_config_dir) = crate::integrations::setting_up_integrations::get_config_dirs(gcx.clone(), &active_project_path).await; - let (integrations_yaml_path, is_inside_container) = { + let (integrations_yaml_path, is_inside_container, allow_experimental) = { let gcx_locked = gcx.read().await; - (gcx_locked.cmdline.integrations_yaml.clone(), gcx_locked.cmdline.inside_container) + (gcx_locked.cmdline.integrations_yaml.clone(), gcx_locked.cmdline.inside_container, gcx_locked.cmdline.experimental) }; let mut error_log: Vec = Vec::new(); From 4365fee44988865b8ed3c251afc0a2e713850b51 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Mon, 12 May 2025 14:27:05 +0200 Subject: [PATCH 41/90] get_available tools with integrations --- .../src/integrations/running_integrations.rs | 23 --- refact-agent/engine/src/tools/tools_list.rs | 149 ++++++++++++------ 2 files changed, 104 insertions(+), 68 deletions(-) diff --git a/refact-agent/engine/src/integrations/running_integrations.rs b/refact-agent/engine/src/integrations/running_integrations.rs index 03cf1a0fe..988b3251a 100644 --- a/refact-agent/engine/src/integrations/running_integrations.rs +++ b/refact-agent/engine/src/integrations/running_integrations.rs @@ -3,32 +3,9 @@ use indexmap::IndexMap; use tokio::sync::RwLock as ARwLock; use crate::custom_error::YamlError; -use crate::tools::tools_description::Tool; use crate::global_context::GlobalContext; use crate::integrations::integr_abstract::IntegrationTrait; - -pub async fn load_integration_tools( - gcx: Arc>, - allow_experimental: bool, -) -> IndexMap> { - let (integraions_map, _yaml_errors) = load_integrations(gcx.clone(), allow_experimental, &["**/*".to_string()]).await; - let mut tools = IndexMap::new(); - for (name, integr) in integraions_map { - for tool in integr.integr_tools(&name).await { - let mut tool_name = tool.tool_description().name; - if tool_name.is_empty() { - tool_name = name.clone(); - } - if tools.contains_key(&tool_name) { - tracing::warn!("tool with name '{}' already exists, overwriting previous definition", tool_name); - } - tools.insert(tool_name, tool); - } - } - tools -} - /// Loads and set up integrations from config files. /// /// If `include_paths_matching` is `None`, all integrations are loaded, diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index dbe9adad5..97923ac35 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -1,21 +1,85 @@ use std::sync::Arc; -use indexmap::IndexMap; use tokio::sync::RwLock as ARwLock; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; -use crate::integrations::running_integrations::load_integration_tools; +use crate::integrations::running_integrations::load_integrations; use super::tools_description::{Tool, ToolGroup, ToolGroupCategory}; +fn tool_available( + tool: &Box, + ast_on: bool, + vecdb_on: bool, + is_there_a_thinking_model: bool, + allow_knowledge: bool, + allow_experimental: bool, +) -> bool { + let dependencies = tool.tool_depends_on(); + if dependencies.contains(&"ast".to_string()) && !ast_on { + return false; + } + if dependencies.contains(&"vecdb".to_string()) && !vecdb_on { + return false; + } + if dependencies.contains(&"thinking".to_string()) && !is_there_a_thinking_model { + return false; + } + if dependencies.contains(&"knowledge".to_string()) && !allow_knowledge { + return false; + } + if tool.tool_description().experimental && !allow_experimental { + return false; + } + true +} + +async fn tool_available_from_gcx( + gcx: Arc>, +) -> impl Fn(&Box) -> bool { + let (ast_on, vecdb_on, allow_experimental) = { + let gcx_locked = gcx.read().await; + let vecdb_on = gcx_locked.vec_db.lock().await.is_some(); + (gcx_locked.ast_service.is_some(), vecdb_on, gcx_locked.cmdline.experimental) + }; + + let (is_there_a_thinking_model, allow_knowledge) = match try_load_caps_quickly_if_not_present(gcx.clone(), 0).await { + Ok(caps) => { + (caps.chat_models.get(&caps.defaults.chat_thinking_model).is_some(), + caps.metadata.features.contains(&"knowledge".to_string())) + }, + Err(_) => (false, false), + }; + + move |tool: &Box| { + tool_available( + tool, + ast_on, + vecdb_on, + is_there_a_thinking_model, + allow_knowledge, + allow_experimental, + ) + } +} -pub async fn get_builtin_tools( +impl ToolGroup { + pub async fn retain_available_tools( + &mut self, + gcx: Arc>, + ) { + let tool_available = tool_available_from_gcx(gcx.clone()).await; + self.tools.retain(|tool| tool_available(tool)); + } +} + +async fn get_builtin_tools( gcx: Arc>, ) -> Vec { let config_dir = gcx.read().await.config_dir.clone(); let config_path = config_dir.join("builtin_tools.yaml").to_string_lossy().to_string(); - let mut tools = vec![ + let tools = vec![ Box::new(crate::tools::tool_ast_definition::ToolAstDefinition{config_path: config_path.clone()}) as Box, Box::new(crate::tools::tool_ast_reference::ToolAstReference{config_path: config_path.clone()}) as Box, Box::new(crate::tools::tool_tree::ToolTree{config_path: config_path.clone()}) as Box, @@ -35,59 +99,54 @@ pub async fn get_builtin_tools( Box::new(crate::tools::tool_locate_search::ToolLocateSearch{config_path: config_path.clone()}) as Box, ]; - vec![ + let mut tool_groups = vec![ ToolGroup { name: "builtin".to_string(), description: "Builtin tools".to_string(), category: ToolGroupCategory::Builtin, tools, }, - ] + ]; + + for tool_group in tool_groups.iter_mut() { + tool_group.retain_available_tools(gcx.clone()).await; + } + + tool_groups } -pub async fn get_available_tools( +async fn get_integration_tools( gcx: Arc>, - _supports_clicks: bool, // XXX -) -> Result>, String> { - let (ast_on, vecdb_on, allow_experimental) = { - let gcx_locked = gcx.read().await; - let vecdb_on = gcx_locked.vec_db.lock().await.is_some(); - (gcx_locked.ast_service.is_some(), vecdb_on, gcx_locked.cmdline.experimental) - }; - - let (is_there_a_thinking_model, allow_knowledge) = match try_load_caps_quickly_if_not_present(gcx.clone(), 0).await { - Ok(caps) => { - (caps.chat_models.get(&caps.defaults.chat_thinking_model).is_some(), - caps.metadata.features.contains(&"knowledge".to_string())) +) -> Vec { + let mut tool_groups = vec![ + ToolGroup { + name: "integrations".to_string(), + description: "Integration tools".to_string(), + category: ToolGroupCategory::Integration, + tools: vec![], }, - Err(_) => (false, false), - }; + ]; + let (integrations_map, _yaml_errors) = load_integrations(gcx.clone(), &["**/*".to_string()]).await; + for (name, integr) in integrations_map { + for tool in integr.integr_tools(&name).await { + tool_groups[0].tools.push(tool); + } + } + for tool_group in tool_groups.iter_mut() { + tool_group.retain_available_tools(gcx.clone()).await; + } + + tool_groups +} - let mut tools_all = get_builtin_tools(); +pub async fn get_available_tools( + gcx: Arc>, + _supports_clicks: bool, // XXX +) -> Vec { + let mut tools_all = get_builtin_tools(gcx.clone()).await; tools_all.extend( - load_integration_tools(gcx, allow_experimental).await + get_integration_tools(gcx).await ); - let mut filtered_tools = IndexMap::new(); - for (tool_name, tool) in tools_all { - let dependencies = tool.tool_depends_on(); - if dependencies.contains(&"ast".to_string()) && !ast_on { - continue; - } - if dependencies.contains(&"vecdb".to_string()) && !vecdb_on { - continue; - } - if dependencies.contains(&"thinking".to_string()) && !is_there_a_thinking_model { - continue; - } - if dependencies.contains(&"knowledge".to_string()) && !allow_knowledge { - continue; - } - if tool.tool_description().experimental && !allow_experimental { - continue; - } - filtered_tools.insert(tool_name, tool); - } - - Ok(filtered_tools) + tools_all } From a9a29b34a260b9474d552a34948a8335e11eb53d Mon Sep 17 00:00:00 2001 From: MDario123 Date: Mon, 12 May 2025 14:27:27 +0200 Subject: [PATCH 42/90] config method for tools --- .../engine/src/tools/tools_description.rs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 71bbaa668..f7045a1b1 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -64,6 +64,19 @@ pub struct ToolDesc { pub source: ToolSource, } +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] +pub struct ToolConfig { + pub enabled: bool, +} + +impl Default for ToolConfig { + fn default() -> Self { + ToolConfig { + enabled: true, + } + } +} + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ToolParam { #[serde(deserialize_with = "validate_snake_case")] @@ -143,6 +156,32 @@ pub trait Tool: Send + Sync { return None; } + fn config(&self) -> Result { + let tool_desc = self.tool_description(); + + let tool_name = tool_desc.name; + let config_path = tool_desc.source.config_path; + + // Read the config file as yaml, and get field tools.tool_name + let config = std::fs::read_to_string(config_path) + .map_err(|e| format!("Error reading config file: {}", e))?; + + let config: serde_yaml::Value = serde_yaml::from_str(&config) + .map_err(|e| format!("Error parsing config file: {}", e))?; + + let config = config.get("tools") + .and_then(|tools| tools.get(&tool_name)); + + match config { + None => Ok(ToolConfig::default()), + Some(config) => { + let config: ToolConfig = serde_yaml::from_value(config.clone()) + .unwrap_or_default(); + Ok(config) + } + } + } + fn tool_depends_on(&self) -> Vec { vec![] } // "ast", "vecdb" fn usage(&mut self) -> &mut Option { From 45fe6723fb555d5d8ed76eb0f2d0703bdc9677c2 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Mon, 12 May 2025 16:25:50 +0200 Subject: [PATCH 43/90] add display name and source for integrations --- refact-agent/engine/src/integrations/integr_chrome.rs | 7 ++++++- refact-agent/engine/src/integrations/integr_cmdline.rs | 7 ++++++- .../engine/src/integrations/integr_cmdline_service.rs | 7 ++++++- refact-agent/engine/src/integrations/integr_github.rs | 10 +++++++--- refact-agent/engine/src/integrations/integr_gitlab.rs | 7 ++++++- refact-agent/engine/src/integrations/integr_mysql.rs | 7 ++++++- refact-agent/engine/src/integrations/integr_pdb.rs | 7 ++++++- .../engine/src/integrations/integr_postgres.rs | 7 ++++++- refact-agent/engine/src/integrations/integr_shell.rs | 9 +++++++-- refact-agent/engine/src/integrations/mcp/tool_mcp.rs | 9 +++++++-- 10 files changed, 63 insertions(+), 14 deletions(-) diff --git a/refact-agent/engine/src/integrations/integr_chrome.rs b/refact-agent/engine/src/integrations/integr_chrome.rs index 532b648a2..186e30f11 100644 --- a/refact-agent/engine/src/integrations/integr_chrome.rs +++ b/refact-agent/engine/src/integrations/integr_chrome.rs @@ -14,7 +14,7 @@ use crate::global_context::GlobalContext; use crate::call_validation::{ChatContent, ChatMessage}; use crate::scratchpads::multimodality::MultimodalElement; use crate::postprocessing::pp_command_output::{CmdlineOutputFilter, output_mini_postprocessing}; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::integrations::integr_abstract::{IntegrationTrait, IntegrationCommon, IntegrationConfirmation}; use crate::integrations::docker::docker_container_manager::get_container_name; @@ -279,6 +279,11 @@ impl Tool for ToolChrome { Supported commands:\n{}", supported_commands.join("\n")); ToolDesc { name: "chrome".to_string(), + display_name: "Chrome".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "A real web browser with graphical interface.".to_string(), diff --git a/refact-agent/engine/src/integrations/integr_cmdline.rs b/refact-agent/engine/src/integrations/integr_cmdline.rs index ab5dddc34..7d9680073 100644 --- a/refact-agent/engine/src/integrations/integr_cmdline.rs +++ b/refact-agent/engine/src/integrations/integr_cmdline.rs @@ -17,7 +17,7 @@ use crate::files_correction::CommandSimplifiedDirExt; use crate::global_context::GlobalContext; use crate::at_commands::at_commands::AtCommandsContext; use crate::integrations::process_io_utils::{execute_command, AnsiStrippable}; -use crate::tools::tools_description::{ToolParam, Tool, ToolDesc}; +use crate::tools::tools_description::{ToolParam, Tool, ToolDesc, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::postprocessing::pp_command_output::{CmdlineOutputFilter, output_mini_postprocessing}; use crate::integrations::integr_abstract::{IntegrationTrait, IntegrationCommon, IntegrationConfirmation}; @@ -297,6 +297,11 @@ impl Tool for ToolCmdline { }); ToolDesc { name: self.name.clone(), + display_name: self.name.clone(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: self.cfg.description.clone(), diff --git a/refact-agent/engine/src/integrations/integr_cmdline_service.rs b/refact-agent/engine/src/integrations/integr_cmdline_service.rs index e0939adc8..e44d1d03a 100644 --- a/refact-agent/engine/src/integrations/integr_cmdline_service.rs +++ b/refact-agent/engine/src/integrations/integr_cmdline_service.rs @@ -9,7 +9,7 @@ use async_trait::async_trait; use process_wrap::tokio::*; use crate::at_commands::at_commands::AtCommandsContext; -use crate::tools::tools_description::{Tool, ToolParam, ToolDesc}; +use crate::tools::tools_description::{Tool, ToolParam, ToolDesc, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::global_context::GlobalContext; use crate::postprocessing::pp_command_output::output_mini_postprocessing; @@ -323,6 +323,11 @@ impl Tool for ToolService { ToolDesc { name: self.name.clone(), + display_name: self.name.clone(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: self.cfg.description.clone(), diff --git a/refact-agent/engine/src/integrations/integr_github.rs b/refact-agent/engine/src/integrations/integr_github.rs index f7dd47ab4..7b699034b 100644 --- a/refact-agent/engine/src/integrations/integr_github.rs +++ b/refact-agent/engine/src/integrations/integr_github.rs @@ -12,7 +12,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ContextEnum, ChatMessage, ChatContent, ChatUsage}; use crate::files_correction::canonical_path; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use serde_json::Value; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation, IntegrationTrait}; use crate::integrations::process_io_utils::AnsiStrippable; @@ -28,8 +28,7 @@ pub struct SettingsGitHub { #[derive(Default)] pub struct ToolGithub { pub common: IntegrationCommon, - pub settings_github: SettingsGitHub, - pub config_path: String, + pub settings_github: SettingsGitHub, pub config_path: String, } #[async_trait] @@ -69,6 +68,11 @@ impl Tool for ToolGithub { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "github".to_string(), + display_name: "GitHub CLI".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Access to gh command line command, to fetch issues, review PRs.".to_string(), diff --git a/refact-agent/engine/src/integrations/integr_gitlab.rs b/refact-agent/engine/src/integrations/integr_gitlab.rs index beac8b9ee..c2bcc3a85 100644 --- a/refact-agent/engine/src/integrations/integr_gitlab.rs +++ b/refact-agent/engine/src/integrations/integr_gitlab.rs @@ -13,7 +13,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ContextEnum, ChatMessage, ChatContent, ChatUsage}; use crate::files_correction::canonical_path; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation, IntegrationTrait}; use crate::integrations::process_io_utils::AnsiStrippable; @@ -68,6 +68,11 @@ impl Tool for ToolGitlab { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "gitlab".to_string(), + display_name: "GitLab".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Access to glab command line command, to fetch issues, review PRs.".to_string(), diff --git a/refact-agent/engine/src/integrations/integr_mysql.rs b/refact-agent/engine/src/integrations/integr_mysql.rs index 74057d066..50a04d354 100644 --- a/refact-agent/engine/src/integrations/integr_mysql.rs +++ b/refact-agent/engine/src/integrations/integr_mysql.rs @@ -12,7 +12,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::ContextEnum; use crate::call_validation::{ChatContent, ChatMessage, ChatUsage}; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation, IntegrationTrait}; use super::process_io_utils::AnsiStrippable; @@ -117,6 +117,11 @@ impl Tool for ToolMysql { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "mysql".to_string(), + display_name: "MySQL".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "MySQL integration, can run a single query per call.".to_string(), diff --git a/refact-agent/engine/src/integrations/integr_pdb.rs b/refact-agent/engine/src/integrations/integr_pdb.rs index e8e3bf14b..c08a6c574 100644 --- a/refact-agent/engine/src/integrations/integr_pdb.rs +++ b/refact-agent/engine/src/integrations/integr_pdb.rs @@ -20,7 +20,7 @@ use crate::files_correction::{get_active_project_path, CommandSimplifiedDirExt}; use crate::integrations::sessions::{IntegrationSession, get_session_hashmap_key}; use crate::global_context::GlobalContext; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation, IntegrationTrait}; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::integrations::process_io_utils::{first_n_chars, last_n_chars, last_n_lines, write_to_stdin_and_flush, blocking_read_until_token_or_timeout}; @@ -174,6 +174,11 @@ impl Tool for ToolPdb { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "pdb".to_string(), + display_name: "Python Debugger".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Python debugger for inspecting variables and exploring what the program really does. This tool executes only one command at a time. Start with python -m pdb ...".to_string(), diff --git a/refact-agent/engine/src/integrations/integr_postgres.rs b/refact-agent/engine/src/integrations/integr_postgres.rs index 56d390b00..eb1b19d0f 100644 --- a/refact-agent/engine/src/integrations/integr_postgres.rs +++ b/refact-agent/engine/src/integrations/integr_postgres.rs @@ -13,7 +13,7 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::ContextEnum; use crate::call_validation::{ChatContent, ChatMessage, ChatUsage}; use crate::integrations::go_to_configuration_message; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use super::process_io_utils::AnsiStrippable; @@ -116,6 +116,11 @@ impl Tool for ToolPostgres { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "postgres".to_string(), + display_name: "PostgreSQL".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "PostgreSQL integration, can run a single query per call.".to_string(), diff --git a/refact-agent/engine/src/integrations/integr_shell.rs b/refact-agent/engine/src/integrations/integr_shell.rs index 31a047b6a..5265cd850 100644 --- a/refact-agent/engine/src/integrations/integr_shell.rs +++ b/refact-agent/engine/src/integrations/integr_shell.rs @@ -20,7 +20,7 @@ use crate::files_correction::preprocess_path_for_normalization; use crate::files_correction::CommandSimplifiedDirExt; use crate::global_context::GlobalContext; use crate::integrations::process_io_utils::{execute_command, AnsiStrippable}; -use crate::tools::tools_description::{ToolParam, Tool, ToolDesc, MatchConfirmDeny, MatchConfirmDenyResult}; +use crate::tools::tools_description::{ToolParam, Tool, ToolDesc, ToolSource, ToolSourceType, MatchConfirmDeny, MatchConfirmDenyResult}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::postprocessing::pp_command_output::CmdlineOutputFilter; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationTrait}; @@ -120,6 +120,11 @@ impl Tool for ToolShell { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "shell".to_string(), + display_name: "Shell".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: "Execute a single command, using the \"sh\" on unix-like systems and \"powershell.exe\" on windows. Use it for one-time tasks like dependencies installation. Don't call this unless you have to. Not suitable for regular work because it requires a confirmation at each step.".to_string(), @@ -298,4 +303,4 @@ available: confirmation: ask_user_default: ["*"] deny_default: ["sudo*"] -"#; \ No newline at end of file +"#; diff --git a/refact-agent/engine/src/integrations/mcp/tool_mcp.rs b/refact-agent/engine/src/integrations/mcp/tool_mcp.rs index 5a38cb233..808ec95c5 100644 --- a/refact-agent/engine/src/integrations/mcp/tool_mcp.rs +++ b/refact-agent/engine/src/integrations/mcp/tool_mcp.rs @@ -11,7 +11,7 @@ use tokio::time::Duration; use crate::caps::resolve_chat_model; use crate::at_commands::at_commands::AtCommandsContext; use crate::scratchpads::multimodality::MultimodalElement; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::integrations::integr_abstract::{IntegrationCommon, IntegrationConfirmation}; use super::session_mcp::{add_log_entry, mcp_session_wait_startup}; @@ -232,7 +232,12 @@ impl Tool for ToolMCP { }; ToolDesc { - name: self.tool_name(), + name: tool_name.clone(), + display_name: self.mcp_tool.name, + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: false, description: self.mcp_tool.description.to_owned().unwrap_or_default().to_string(), From 0cd4891da6e35f587d14fd801a0d699043965c8d Mon Sep 17 00:00:00 2001 From: MDario123 Date: Mon, 12 May 2025 16:26:28 +0200 Subject: [PATCH 44/90] derives for group category and comment unnused cattegory --- refact-agent/engine/src/tools/tools_description.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index f7045a1b1..ecc29468f 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -25,10 +25,11 @@ pub struct MatchConfirmDeny { pub rule: String, } +#[derive(Clone, Copy, Serialize, Debug)] pub enum ToolGroupCategory { Builtin, Integration, - MCP, + // MCP, } pub struct ToolGroup { From ad708b246acbd9fdbbeabf874bb17b8ce8e0b9d3 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Mon, 12 May 2025 16:28:18 +0200 Subject: [PATCH 45/90] rework tools GET handler --- .../engine/src/http/routers/v1/at_tools.rs | 77 +++++++++++-------- .../src/scratchpads/chat_passthrough.rs | 4 +- refact-agent/engine/src/subchat.rs | 45 ++++++----- refact-agent/engine/src/tools/tools_list.rs | 9 ++- 4 files changed, 84 insertions(+), 51 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index 7d1edbbef..992026624 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::sync::Arc; -use axum::Extension; +use axum::{Extension, Json}; use axum::http::{Response, StatusCode}; use hyper::Body; use indexmap::IndexMap; @@ -15,8 +15,8 @@ use crate::http::http_post_json; use crate::http::routers::v1::chat::CHAT_TOP_N; use crate::indexing_utils::wait_for_indexing_if_needed; use crate::integrations::docker::docker_container_manager::docker_container_get_host_lsp_port_to_connect; -use crate::tools::tools_description::{MatchConfirmDenyResult, ToolDesc}; -use crate::tools::tools_list::get_available_tools; +use crate::tools::tools_description::{MatchConfirmDenyResult, ToolDesc, ToolGroupCategory}; +use crate::tools::tools_list::{get_available_tool_groups, get_available_tools}; use crate::custom_error::ScratchError; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; use crate::tools::tools_execute::run_tools; @@ -66,25 +66,43 @@ pub struct ToolExecuteResponse { pub tools_ran: bool, } +#[derive(Serialize)] +pub struct ToolResponse { + pub spec: ToolDesc, + pub enabled: bool, +} + +#[derive(Serialize)] +pub struct ToolGroupResponse { + pub name: String, + pub description: String, + pub category: ToolGroupCategory, + pub tools: Vec, +} + pub async fn handle_v1_tools( Extension(gcx): Extension>>, -) -> axum::response::Result, ScratchError> { - let all_tools = get_available_tools(gcx.clone(), true).await.map_err(|e| { - ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, e) - })?; +) -> Json> { + let tool_groups = get_available_tool_groups(gcx.clone(), true).await; - let tools_desc: Vec = all_tools.values().map(|tool| { - tool.tool_description() - }).collect(); + let tool_groups: Vec = tool_groups.into_iter().map(|tool_group| { + let tools: Vec = tool_group.tools.into_iter().map(|tool| { + let spec = tool.tool_description(); + ToolResponse { + spec, + enabled: tool.config().unwrap_or_default().enabled, + } + }).collect(); - let body = serde_json::to_string_pretty(&tools_desc) - .map_err(|e| ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, format!("JSON problem: {}", e)))?; + ToolGroupResponse { + name: tool_group.name, + description: tool_group.description, + category: tool_group.category, + tools, + } + }).collect(); - Ok(Response::builder() - .status(StatusCode::OK) - .header("Content-Type", "application/json") - .body(Body::from(body)) - .unwrap()) + Json(tool_groups) } pub async fn handle_v1_tools_check_if_confirmation_needed( @@ -132,17 +150,11 @@ pub async fn handle_v1_tools_check_if_confirmation_needed( "".to_string(), ).await)); // used only for should_confirm - let all_tools = match get_available_tools(gcx.clone(), true).await { - Ok(tools) => tools, - Err(e) => { - let error_body = serde_json::json!({ "detail": e }).to_string(); - return Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .header("Content-Type", "application/json") - .body(Body::from(error_body)) - .unwrap()); - } - }; + let all_tools = get_available_tools(gcx.clone(), true).await.into_iter() + .map(|tool| { + let spec = tool.tool_description(); + (spec.name, tool) + }).collect::>(); let mut result_messages = vec![]; for tool_call in &post.tool_calls { @@ -236,9 +248,12 @@ pub async fn handle_v1_tools_execute( ccx.postprocess_parameters = tools_execute_post.postprocess_parameters.clone(); let ccx_arc = Arc::new(AMutex::new(ccx)); - let mut at_tools = get_available_tools(gcx.clone(), false).await.map_err(|e|{ - ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, format!("Error getting at_tools: {}", e)) - })?; + let mut at_tools = get_available_tools(gcx.clone(), false).await.into_iter() + .map(|tool| { + let spec = tool.tool_description(); + (spec.name, tool) + }).collect::>(); + let (messages, tools_ran) = run_tools( ccx_arc.clone(), &mut at_tools, tokenizer.clone(), tools_execute_post.maxgen, &tools_execute_post.messages, &tools_execute_post.style ).await.map_err(|e| ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, format!("Error running tools: {}", e)))?; diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index 6162033fe..547e5c040 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -113,7 +113,9 @@ impl ScratchpadAbstract for ChatPassthrough { }; let style = self.post.style.clone(); let mut at_tools = if !should_execute_remotely { - get_available_tools(gcx.clone(), self.supports_clicks).await? + get_available_tools(gcx.clone(), self.supports_clicks).await.into_iter().map(|x| { + (x.tool_description().name, x) + }).collect() } else { IndexMap::new() }; diff --git a/refact-agent/engine/src/subchat.rs b/refact-agent/engine/src/subchat.rs index 52861e1c2..457e253f2 100644 --- a/refact-agent/engine/src/subchat.rs +++ b/refact-agent/engine/src/subchat.rs @@ -1,13 +1,12 @@ use std::sync::Arc; -use std::collections::HashSet; use tokio::sync::RwLock as ARwLock; use tokio::sync::Mutex as AMutex; use serde_json::{json, Value}; -use tracing::{error, info, warn}; +use tracing::{info, warn}; use crate::caps::resolve_chat_model; use crate::caps::ChatModelRecord; -use crate::tools::tools_description::tool_description_list_from_yaml; +use crate::tools::tools_description::ToolDesc; use crate::tools::tools_list::get_available_tools; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{SamplingParameters, PostprocessSettings, ChatPost, ChatMessage, ChatUsage, ChatToolCall, ReasoningEffort}; @@ -269,23 +268,33 @@ pub async fn subchat_single( let ccx_locked = ccx.lock().await; (ccx_locked.global_context.clone(), ccx_locked.should_execute_remotely) }; - let tools_turned_on_by_cmdline = get_available_tools(gcx.clone(), false).await?; - let tools_turned_on_by_cmdline_set: HashSet = tools_turned_on_by_cmdline.keys().cloned().collect(); - let tools_on_intersection: Vec = if let Some(tools_s) = &tools_subset { - let tools_turn_on_set: HashSet = tools_s.iter().cloned().collect(); - tools_turn_on_set.intersection(&tools_turned_on_by_cmdline_set).cloned().collect() - } else { - tools_turned_on_by_cmdline_set.iter().cloned().collect() + + info!("tools_subset {:?}", tools_subset); + + let tools_desclist: Vec = { + let tools_turned_on_by_cmdline = get_available_tools(gcx.clone(), false).await.iter().map(|tool| { + tool.tool_description() + }).collect::>(); + + info!("tools_turned_on_by_cmdline {:?}", tools_turned_on_by_cmdline.iter().map(|tool| { + &tool.name + }).collect::>()); + + match tools_subset { + Some(tools_subset) => { + tools_turned_on_by_cmdline.into_iter().filter(|tool| { + tools_subset.contains(&tool.name) + }).collect() + } + None => tools_turned_on_by_cmdline, + } }; - let allow_experimental = gcx.read().await.cmdline.experimental; - let tools_desclist = tool_description_list_from_yaml(tools_turned_on_by_cmdline, Some(&tools_on_intersection), allow_experimental).await.unwrap_or_else(|e|{ - error!("Error loading compiled_in_tools: {:?}", e); - vec![] - }); + + info!("tools_on_intersection {:?}", tools_desclist.iter().map(|tool| { + &tool.name + }).collect::>()); + let tools = tools_desclist.into_iter().filter(|x| x.is_supported_by(model_id)).map(|x|x.into_openai_style()).collect::>(); - info!("tools_subset {:?}", tools_subset); - info!("tools_turned_on_by_cmdline_set {:?}", tools_turned_on_by_cmdline_set); - info!("tools_on_intersection {:?}", tools_on_intersection); let max_new_tokens = max_new_tokens.unwrap_or(MAX_NEW_TOKENS); let (mut chat_post, spad, model_rec) = create_chat_post_and_scratchpad( diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index 97923ac35..f173f589f 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -139,7 +139,7 @@ async fn get_integration_tools( tool_groups } -pub async fn get_available_tools( +pub async fn get_available_tool_groups( gcx: Arc>, _supports_clicks: bool, // XXX ) -> Vec { @@ -150,3 +150,10 @@ pub async fn get_available_tools( tools_all } + +pub async fn get_available_tools( + gcx: Arc>, + _supports_clicks: bool, // XXX +) -> Vec> { + get_available_tool_groups(gcx, _supports_clicks).await.into_iter().flat_map(|g| g.tools).collect() +} From cbbad19edb5857117b7027a7e10d215c47fdf7a1 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Mon, 12 May 2025 16:28:48 +0200 Subject: [PATCH 46/90] display name and source for integration docker --- .../engine/src/integrations/docker/integr_docker.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/refact-agent/engine/src/integrations/docker/integr_docker.rs b/refact-agent/engine/src/integrations/docker/integr_docker.rs index df12f1783..9ca6e1d52 100644 --- a/refact-agent/engine/src/integrations/docker/integr_docker.rs +++ b/refact-agent/engine/src/integrations/docker/integr_docker.rs @@ -11,7 +11,7 @@ use crate::call_validation::{ChatContent, ChatMessage, ContextEnum}; use crate::global_context::GlobalContext; use crate::integrations::integr_abstract::{IntegrationTrait, IntegrationCommon, IntegrationConfirmation}; use crate::integrations::process_io_utils::AnsiStrippable; -use crate::tools::tools_description::{Tool, ToolDesc, ToolParam}; +use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::integrations::docker::docker_ssh_tunnel_utils::{SshConfig, forward_remote_docker_if_needed}; use crate::integrations::utils::{serialize_num_to_str, deserialize_str_to_num}; @@ -140,6 +140,11 @@ impl Tool for ToolDocker { fn tool_description(&self) -> ToolDesc { ToolDesc { name: "docker".to_string(), + display_name: "Docker CLI".to_string(), + source: ToolSource { + source_type: ToolSourceType::Integration, + config_path: self.config_path.clone(), + }, agentic: true, experimental: true, description: "Access to docker cli, in a non-interactive way, don't open a shell.".to_string(), From 9f1f688d7a6ed233b4fba499bf4c20e24a76910b Mon Sep 17 00:00:00 2001 From: MDario123 Date: Tue, 13 May 2025 13:54:39 +0200 Subject: [PATCH 47/90] add tools post endpoint and lowercase enums --- refact-agent/engine/src/http/routers/v1.rs | 6 ++- .../engine/src/http/routers/v1/at_tools.rs | 39 +++++++++++++++++-- .../engine/src/tools/tools_description.rs | 35 +++++++++++++++++ 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1.rs b/refact-agent/engine/src/http/routers/v1.rs index 37cfb22f9..eabea4b48 100644 --- a/refact-agent/engine/src/http/routers/v1.rs +++ b/refact-agent/engine/src/http/routers/v1.rs @@ -1,3 +1,4 @@ +use at_tools::handle_v1_post_tools; use axum::Router; use axum::routing::{get, post, delete}; use tower_http::cors::CorsLayer; @@ -7,7 +8,7 @@ use crate::http::routers::v1::code_completion::{handle_v1_code_completion_web, h use crate::http::routers::v1::code_lens::handle_v1_code_lens; use crate::http::routers::v1::ast::{handle_v1_ast_file_dump, handle_v1_ast_file_symbols, handle_v1_ast_status}; use crate::http::routers::v1::at_commands::{handle_v1_command_completion, handle_v1_command_preview, handle_v1_at_command_execute}; -use crate::http::routers::v1::at_tools::{handle_v1_tools, handle_v1_tools_check_if_confirmation_needed, handle_v1_tools_execute}; +use crate::http::routers::v1::at_tools::{handle_v1_get_tools, handle_v1_tools_check_if_confirmation_needed, handle_v1_tools_execute}; use crate::http::routers::v1::caps::handle_v1_caps; use crate::http::routers::v1::caps::handle_v1_ping; use crate::http::routers::v1::chat::{handle_v1_chat, handle_v1_chat_completions}; @@ -84,7 +85,8 @@ pub fn make_v1_router() -> Router { .route("/caps", get(handle_v1_caps)) - .route("/tools", get(handle_v1_tools)) + .route("/tools", get(handle_v1_get_tools)) + .route("/tools", post(handle_v1_post_tools)) .route("/tools-check-if-confirmation-needed", post(handle_v1_tools_check_if_confirmation_needed)) .route("/tools-execute", post(handle_v1_tools_execute)) // because it works remotely diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index 992026624..8313ab03f 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -5,7 +5,7 @@ use axum::http::{Response, StatusCode}; use hyper::Body; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use serde_json::Value; +use serde_json::{json, Value}; use tokio::sync::{Mutex as AMutex, RwLock as ARwLock}; use crate::at_commands::at_commands::AtCommandsContext; @@ -15,7 +15,7 @@ use crate::http::http_post_json; use crate::http::routers::v1::chat::CHAT_TOP_N; use crate::indexing_utils::wait_for_indexing_if_needed; use crate::integrations::docker::docker_container_manager::docker_container_get_host_lsp_port_to_connect; -use crate::tools::tools_description::{MatchConfirmDenyResult, ToolDesc, ToolGroupCategory}; +use crate::tools::tools_description::{set_tool_config, MatchConfirmDenyResult, ToolConfig, ToolDesc, ToolGroupCategory, ToolSource}; use crate::tools::tools_list::{get_available_tool_groups, get_available_tools}; use crate::custom_error::ScratchError; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; @@ -80,7 +80,7 @@ pub struct ToolGroupResponse { pub tools: Vec, } -pub async fn handle_v1_tools( +pub async fn handle_v1_get_tools( Extension(gcx): Extension>>, ) -> Json> { let tool_groups = get_available_tool_groups(gcx.clone(), true).await; @@ -105,6 +105,39 @@ pub async fn handle_v1_tools( Json(tool_groups) } +#[derive(Deserialize)] +pub struct ToolPost { + name: String, + source: ToolSource, + enabled: bool, +} + +#[derive(Serialize)] +pub struct ToolPostResponse { + sucess: bool, +} + +pub async fn handle_v1_post_tools( + body_bytes: hyper::body::Bytes, +) -> Result, ScratchError> { + let tools = serde_json::from_slice::>(&body_bytes) + .map_err(|e| ScratchError::new(StatusCode::UNPROCESSABLE_ENTITY, format!("JSON problem: {}", e)))?; + + for tool in tools { + set_tool_config( + tool.source.config_path, + tool.name, + ToolConfig { + enabled: tool.enabled, + } + ).await.map_err(|e| ScratchError::new(StatusCode::INTERNAL_SERVER_ERROR, format!("Error setting tool config: {}", e)))?; + } + + Ok(Json(ToolPostResponse { + sucess: true, + })) +} + pub async fn handle_v1_tools_check_if_confirmation_needed( Extension(gcx): Extension>>, body_bytes: hyper::body::Bytes, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index ecc29468f..ec30d955d 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -8,6 +8,7 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ChatUsage, ContextEnum}; +use crate::custom_error::MapErrToString; use crate::integrations::integr_abstract::IntegrationConfirmation; use crate::tools::tools_execute::{command_should_be_confirmed_by_user, command_should_be_denied}; @@ -26,6 +27,7 @@ pub struct MatchConfirmDeny { } #[derive(Clone, Copy, Serialize, Debug)] +#[serde(rename_all = "lowercase")] pub enum ToolGroupCategory { Builtin, Integration, @@ -40,6 +42,7 @@ pub struct ToolGroup { } #[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] pub enum ToolSourceType { Builtin, Integration, @@ -192,6 +195,38 @@ pub trait Tool: Send + Sync { } } +pub async fn set_tool_config(config_path: String, tool_name: String, new_config: ToolConfig) -> Result<(), String> { + let config_file = tokio::fs::read_to_string(&config_path) + .await + .map_err(|e| format!("Error reading config file: {}", e))?; + + let mut config: serde_yaml::Mapping = serde_yaml::from_str(&config_file) + .map_err(|e| format!("Error parsing config file: {}", e))?; + + let tools: &mut serde_yaml::Mapping = match config.get_mut("tools").and_then(|tools| tools.as_mapping_mut()) { + Some(tools) => tools, + None => { + config.insert(serde_yaml::Value::String("tools".to_string()), serde_yaml::Value::Mapping(serde_yaml::Mapping::new())); + config.get_mut("tools") + .expect("tools was just inserted") + .as_mapping_mut() + .expect("tools is a mapping, it was just inserted") + } + }; + + tools.insert( + serde_yaml::Value::String(tool_name), + serde_yaml::to_value(new_config) + .map_err_with_prefix("ToolConfig should always be serializable.")? + ); + + tokio::fs::write(config_path, serde_yaml::to_string(&config).unwrap()) + .await + .map_err(|e| format!("Error writing config file: {}", e))?; + + Ok(()) +} + fn validate_snake_case<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, From cb74f6d6f2e19f6cdb61907969ce7123cce5dc82 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 09:10:20 +0200 Subject: [PATCH 48/90] fix: handle tools post fix req format --- refact-agent/engine/src/http/routers/v1/at_tools.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index 8313ab03f..b6b3e8a07 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -112,6 +112,11 @@ pub struct ToolPost { enabled: bool, } +#[derive(Deserialize)] +pub struct ToolPostReq { + tools: Vec, +} + #[derive(Serialize)] pub struct ToolPostResponse { sucess: bool, @@ -120,8 +125,9 @@ pub struct ToolPostResponse { pub async fn handle_v1_post_tools( body_bytes: hyper::body::Bytes, ) -> Result, ScratchError> { - let tools = serde_json::from_slice::>(&body_bytes) - .map_err(|e| ScratchError::new(StatusCode::UNPROCESSABLE_ENTITY, format!("JSON problem: {}", e)))?; + let tools = serde_json::from_slice::(&body_bytes) + .map_err(|e| ScratchError::new(StatusCode::UNPROCESSABLE_ENTITY, format!("JSON problem: {}", e)))? + .tools; for tool in tools { set_tool_config( From 68cc79793dfc0090de3d2eea5b1d4dc1f7ef5592 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 09:16:48 +0200 Subject: [PATCH 49/90] fix: make an mcp tool group --- refact-agent/engine/src/tools/tools_description.rs | 2 +- refact-agent/engine/src/tools/tools_list.rs | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index ec30d955d..ce6cab969 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -31,7 +31,7 @@ pub struct MatchConfirmDeny { pub enum ToolGroupCategory { Builtin, Integration, - // MCP, + MCP, } pub struct ToolGroup { diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index f173f589f..36d685368 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -125,11 +125,21 @@ async fn get_integration_tools( category: ToolGroupCategory::Integration, tools: vec![], }, + ToolGroup { + name: "mcp".to_string(), + description: "MCP tools".to_string(), + category: ToolGroupCategory::MCP, + tools: vec![], + }, ]; let (integrations_map, _yaml_errors) = load_integrations(gcx.clone(), &["**/*".to_string()]).await; for (name, integr) in integrations_map { for tool in integr.integr_tools(&name).await { - tool_groups[0].tools.push(tool); + if tool.tool_description().name.starts_with("mcp") { + tool_groups[1].tools.push(tool); + } else { + tool_groups[0].tools.push(tool); + } } } for tool_group in tool_groups.iter_mut() { From b1331f7da626a36b7d466caceca58606f34408ee Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 09:22:25 +0200 Subject: [PATCH 50/90] fix: tools get endpoint return only non-empty groups --- refact-agent/engine/src/http/routers/v1/at_tools.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index b6b3e8a07..c002a0c14 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -85,7 +85,11 @@ pub async fn handle_v1_get_tools( ) -> Json> { let tool_groups = get_available_tool_groups(gcx.clone(), true).await; - let tool_groups: Vec = tool_groups.into_iter().map(|tool_group| { + let tool_groups: Vec = tool_groups.into_iter().filter_map(|tool_group| { + if tool_group.tools.is_empty() { + return None; + } + let tools: Vec = tool_group.tools.into_iter().map(|tool| { let spec = tool.tool_description(); ToolResponse { @@ -94,12 +98,12 @@ pub async fn handle_v1_get_tools( } }).collect(); - ToolGroupResponse { + Some(ToolGroupResponse { name: tool_group.name, description: tool_group.description, category: tool_group.category, tools, - } + }) }).collect(); Json(tool_groups) From 86d195b9dff4f55e6c9fdd64dbf1325ffa35c25e Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 10:05:32 +0200 Subject: [PATCH 51/90] feat: chat gets tools internally --- .../engine/src/http/routers/v1/chat.rs | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/chat.rs b/refact-agent/engine/src/http/routers/v1/chat.rs index 3eaee3532..eaa5e80d8 100644 --- a/refact-agent/engine/src/http/routers/v1/chat.rs +++ b/refact-agent/engine/src/http/routers/v1/chat.rs @@ -15,6 +15,7 @@ use crate::git::checkpoints::create_workspace_checkpoint; use crate::global_context::{GlobalContext, SharedGlobalContext}; use crate::indexing_utils::wait_for_indexing_if_needed; use crate::integrations::docker::docker_container_manager::docker_container_check_status_or_start; +use crate::tools::tools_list::get_available_tools; pub fn available_tools_by_chat_mode(current_tools: Vec, chat_mode: &ChatMode) -> Vec { @@ -121,27 +122,31 @@ async fn _chat( tracing::info!("chat_mode {:?}", chat_post.meta.chat_mode); - if chat_post.meta.chat_mode == ChatMode::NO_TOOLS { - chat_post.tools = None; + + chat_post.tools = if chat_post.meta.chat_mode == ChatMode::NO_TOOLS { + None } else { - if let Some(tools) = &mut chat_post.tools { - for tool in &mut *tools { - if let Some(function) = tool.get_mut("function") { - function.as_object_mut().unwrap().remove("agentic"); - } + // All available tools for the current lsp config in openai style + let tools: Vec = get_available_tools(gcx.clone(), false).await.into_iter().filter_map(|tool| { + if tool.config().is_ok_and(|t| t.enabled) { + let mut openai_style_tool = tool.tool_description().into_openai_style(); + openai_style_tool["function"].as_object_mut().unwrap().remove("agentic"); + Some(openai_style_tool) + } else { + None } - chat_post.tools = Some(available_tools_by_chat_mode(tools.clone(), &chat_post.meta.chat_mode)); - } else { - // TODO at some point, get rid of /tools call on client, make so we can have chat_post.tools==None and just fill the tools here - chat_post.tools = Some(available_tools_by_chat_mode(vec![], &chat_post.meta.chat_mode)); - } - tracing::info!("tools [{}]", chat_post.tools.as_ref().map_or("".to_string(), |tools| { + }).collect(); + + tracing::info!( + "tools [{}]", tools.iter() .filter_map(|tool| tool.get("function").and_then(|f| f.get("name")).and_then(|n| n.as_str())) .collect::>() .join(", ") - })); - } + ); + + Some(available_tools_by_chat_mode(tools, &chat_post.meta.chat_mode)) + }; let caps = crate::global_context::try_load_caps_quickly_if_not_present(gcx.clone(), 0).await?; let model_rec = resolve_chat_model(caps, &chat_post.model) From 1e3d52829cbc4285762d60744660aed109875e1d Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 10:08:46 +0200 Subject: [PATCH 52/90] fix: unconfigured tool should be enabled --- refact-agent/engine/src/http/routers/v1/chat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refact-agent/engine/src/http/routers/v1/chat.rs b/refact-agent/engine/src/http/routers/v1/chat.rs index eaa5e80d8..e66bb96dc 100644 --- a/refact-agent/engine/src/http/routers/v1/chat.rs +++ b/refact-agent/engine/src/http/routers/v1/chat.rs @@ -128,7 +128,7 @@ async fn _chat( } else { // All available tools for the current lsp config in openai style let tools: Vec = get_available_tools(gcx.clone(), false).await.into_iter().filter_map(|tool| { - if tool.config().is_ok_and(|t| t.enabled) { + if tool.config().unwrap_or_default().enabled { let mut openai_style_tool = tool.tool_description().into_openai_style(); openai_style_tool["function"].as_object_mut().unwrap().remove("agentic"); Some(openai_style_tool) From bda4dbfb92b9a01771bcfac999d09c40ed34f4c3 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 14:32:21 +0200 Subject: [PATCH 53/90] fix: create builtin_tools.yaml if it doesn't exist --- refact-agent/engine/src/yaml_configs/create_configs.rs | 1 + refact-agent/engine/src/yaml_configs/default_builtin_tools.yaml | 0 2 files changed, 1 insertion(+) create mode 100644 refact-agent/engine/src/yaml_configs/default_builtin_tools.yaml diff --git a/refact-agent/engine/src/yaml_configs/create_configs.rs b/refact-agent/engine/src/yaml_configs/create_configs.rs index 38e13e75e..a3b753cbe 100644 --- a/refact-agent/engine/src/yaml_configs/create_configs.rs +++ b/refact-agent/engine/src/yaml_configs/create_configs.rs @@ -32,6 +32,7 @@ pub async fn yaml_configs_try_create_all(gcx: Arc>) -> St ("customization.yaml", include_str!("default_customization.yaml")), ("privacy.yaml", include_str!("default_privacy.yaml")), ("indexing.yaml", include_str!("default_indexing.yaml")), + ("builtin_tools.yaml", include_str!("default_builtin_tools.yaml")), ("integrations.d/shell.yaml", include_str!("default_shell.yaml")), ]; diff --git a/refact-agent/engine/src/yaml_configs/default_builtin_tools.yaml b/refact-agent/engine/src/yaml_configs/default_builtin_tools.yaml new file mode 100644 index 000000000..e69de29bb From 66c3825b02ebc74b7eed981ef6f839815508f6a6 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 14:45:30 +0200 Subject: [PATCH 54/90] fix: chat properly handle agent mode --- refact-agent/engine/src/http/routers/v1/chat.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/chat.rs b/refact-agent/engine/src/http/routers/v1/chat.rs index e66bb96dc..d39540149 100644 --- a/refact-agent/engine/src/http/routers/v1/chat.rs +++ b/refact-agent/engine/src/http/routers/v1/chat.rs @@ -128,8 +128,9 @@ async fn _chat( } else { // All available tools for the current lsp config in openai style let tools: Vec = get_available_tools(gcx.clone(), false).await.into_iter().filter_map(|tool| { - if tool.config().unwrap_or_default().enabled { - let mut openai_style_tool = tool.tool_description().into_openai_style(); + let tool_desc = tool.tool_description(); + if tool.config().unwrap_or_default().enabled && tool_desc.agentic == (chat_post.meta.chat_mode == ChatMode::AGENT) { + let mut openai_style_tool = tool_desc.into_openai_style(); openai_style_tool["function"].as_object_mut().unwrap().remove("agentic"); Some(openai_style_tool) } else { From 91563d289794e474f2dc5f4ff231308826c943c8 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 14 May 2025 20:10:31 +0200 Subject: [PATCH 55/90] chore: fixed typo in success word --- refact-agent/engine/src/http/routers/v1/at_tools.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index c002a0c14..ae2588c99 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -123,7 +123,7 @@ pub struct ToolPostReq { #[derive(Serialize)] pub struct ToolPostResponse { - sucess: bool, + success: bool, } pub async fn handle_v1_post_tools( @@ -144,7 +144,7 @@ pub async fn handle_v1_post_tools( } Ok(Json(ToolPostResponse { - sucess: true, + success: true, })) } From 81cd19ff5784d6767c9fa2a4270d7e46fb883f5a Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 15:57:52 +0200 Subject: [PATCH 56/90] refactor: remove unused use --- refact-agent/engine/src/tools/tools_description.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index ce6cab969..58015aa47 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; use std::sync::Arc; -use indexmap::IndexMap; use serde_json::{Value, json}; use serde::{Deserialize, Serialize}; use async_trait::async_trait; From 2b43909a489034f46a77ba0d56b976390ea6e73e Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 16:02:51 +0200 Subject: [PATCH 57/90] refactor: get available tools don't take support_clicks --- refact-agent/engine/src/http/routers/v1/at_tools.rs | 6 +++--- refact-agent/engine/src/http/routers/v1/chat.rs | 2 +- refact-agent/engine/src/scratchpads/chat_passthrough.rs | 2 +- refact-agent/engine/src/subchat.rs | 2 +- refact-agent/engine/src/tools/tools_list.rs | 4 +--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index ae2588c99..7d7d39433 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -83,7 +83,7 @@ pub struct ToolGroupResponse { pub async fn handle_v1_get_tools( Extension(gcx): Extension>>, ) -> Json> { - let tool_groups = get_available_tool_groups(gcx.clone(), true).await; + let tool_groups = get_available_tool_groups(gcx.clone()).await; let tool_groups: Vec = tool_groups.into_iter().filter_map(|tool_group| { if tool_group.tools.is_empty() { @@ -193,7 +193,7 @@ pub async fn handle_v1_tools_check_if_confirmation_needed( "".to_string(), ).await)); // used only for should_confirm - let all_tools = get_available_tools(gcx.clone(), true).await.into_iter() + let all_tools = get_available_tools(gcx.clone()).await.into_iter() .map(|tool| { let spec = tool.tool_description(); (spec.name, tool) @@ -291,7 +291,7 @@ pub async fn handle_v1_tools_execute( ccx.postprocess_parameters = tools_execute_post.postprocess_parameters.clone(); let ccx_arc = Arc::new(AMutex::new(ccx)); - let mut at_tools = get_available_tools(gcx.clone(), false).await.into_iter() + let mut at_tools = get_available_tools(gcx.clone()).await.into_iter() .map(|tool| { let spec = tool.tool_description(); (spec.name, tool) diff --git a/refact-agent/engine/src/http/routers/v1/chat.rs b/refact-agent/engine/src/http/routers/v1/chat.rs index d39540149..b85a6ec0c 100644 --- a/refact-agent/engine/src/http/routers/v1/chat.rs +++ b/refact-agent/engine/src/http/routers/v1/chat.rs @@ -127,7 +127,7 @@ async fn _chat( None } else { // All available tools for the current lsp config in openai style - let tools: Vec = get_available_tools(gcx.clone(), false).await.into_iter().filter_map(|tool| { + let tools: Vec = get_available_tools(gcx.clone()).await.into_iter().filter_map(|tool| { let tool_desc = tool.tool_description(); if tool.config().unwrap_or_default().enabled && tool_desc.agentic == (chat_post.meta.chat_mode == ChatMode::AGENT) { let mut openai_style_tool = tool_desc.into_openai_style(); diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index 547e5c040..48ea338e5 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -113,7 +113,7 @@ impl ScratchpadAbstract for ChatPassthrough { }; let style = self.post.style.clone(); let mut at_tools = if !should_execute_remotely { - get_available_tools(gcx.clone(), self.supports_clicks).await.into_iter().map(|x| { + get_available_tools(gcx.clone()).await.into_iter().map(|x| { (x.tool_description().name, x) }).collect() } else { diff --git a/refact-agent/engine/src/subchat.rs b/refact-agent/engine/src/subchat.rs index 457e253f2..4055eb6ff 100644 --- a/refact-agent/engine/src/subchat.rs +++ b/refact-agent/engine/src/subchat.rs @@ -272,7 +272,7 @@ pub async fn subchat_single( info!("tools_subset {:?}", tools_subset); let tools_desclist: Vec = { - let tools_turned_on_by_cmdline = get_available_tools(gcx.clone(), false).await.iter().map(|tool| { + let tools_turned_on_by_cmdline = get_available_tools(gcx.clone()).await.iter().map(|tool| { tool.tool_description() }).collect::>(); diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index 36d685368..e16a59229 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -151,7 +151,6 @@ async fn get_integration_tools( pub async fn get_available_tool_groups( gcx: Arc>, - _supports_clicks: bool, // XXX ) -> Vec { let mut tools_all = get_builtin_tools(gcx.clone()).await; tools_all.extend( @@ -163,7 +162,6 @@ pub async fn get_available_tool_groups( pub async fn get_available_tools( gcx: Arc>, - _supports_clicks: bool, // XXX ) -> Vec> { - get_available_tool_groups(gcx, _supports_clicks).await.into_iter().flat_map(|g| g.tools).collect() + get_available_tool_groups(gcx).await.into_iter().flat_map(|g| g.tools).collect() } From 31b6cb0bddf364d29e0e8cb17b12341cfde250d2 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 14 May 2025 16:05:47 +0200 Subject: [PATCH 58/90] refactor: remove unused use --- refact-agent/engine/src/http/routers/v1/at_tools.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index 7d7d39433..23b54adc2 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -5,7 +5,7 @@ use axum::http::{Response, StatusCode}; use hyper::Body; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use serde_json::Value; use tokio::sync::{Mutex as AMutex, RwLock as ARwLock}; use crate::at_commands::at_commands::AtCommandsContext; From fe15ee427a532e2931ce6fc14ce1b257547fed19 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 15 May 2025 23:42:51 +0200 Subject: [PATCH 59/90] fix: imported IndexMap, so LSP builds --- refact-agent/engine/src/tools/tools_description.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 58015aa47..7a99af36f 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -4,6 +4,7 @@ use serde_json::{Value, json}; use serde::{Deserialize, Serialize}; use async_trait::async_trait; use tokio::sync::Mutex as AMutex; +use indexmap::IndexMap; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ChatUsage, ContextEnum}; From 885ff4122cd5a782adda0432456fff3d1ee71c68 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 8 May 2025 22:45:02 +0200 Subject: [PATCH 60/90] wip: draft tools on/off UI for agent capabilities & cut off from deprecated /tools --- refact-agent/gui/package-lock.json | 47 +- refact-agent/gui/package.json | 1 + .../gui/src/__fixtures__/tools_response.ts | 432 ++++++++---------- refact-agent/gui/src/app/middleware.ts | 4 +- .../components/ChatForm/AgentCapabilities.tsx | 205 ++++++++- .../gui/src/features/Chat/Thread/actions.ts | 38 +- .../gui/src/hooks/useSendChatRequest.ts | 36 +- refact-agent/gui/src/services/refact/chat.ts | 8 +- refact-agent/gui/src/services/refact/tools.ts | 34 +- 9 files changed, 507 insertions(+), 298 deletions(-) diff --git a/refact-agent/gui/package-lock.json b/refact-agent/gui/package-lock.json index cfbb17007..bc7bd89e0 100644 --- a/refact-agent/gui/package-lock.json +++ b/refact-agent/gui/package-lock.json @@ -15,11 +15,13 @@ "@tanstack/react-table": "^8.20.6", "@types/react": "^18.2.43", "debug": "^4.3.7", + "framer-motion": "^12.10.4", "graphql": "^16.11.0", "react-arborist": "^3.4.3", "react-redux": "^9.1.2", "urql": "^4.2.2", "zod": "^3.25.20" + "react-redux": "^9.1.2" }, "devDependencies": { "@0no-co/graphqlsp": "^1.12.16", @@ -16239,6 +16241,33 @@ "node": ">= 0.6" } }, + "node_modules/framer-motion": { + "version": "12.10.4", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.10.4.tgz", + "integrity": "sha512-gGkavMW3QFDCxI+adVu5sn2NtIRHYPGVLDSJ0S/6B0ZoxPaql2F9foWdg9sGIP6sPA8cbNDfxYf9VlhD3+FkVQ==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.10.4", + "motion-utils": "^12.9.4", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -21258,6 +21287,21 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true }, + "node_modules/motion-dom": { + "version": "12.10.4", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.10.4.tgz", + "integrity": "sha512-GSv8kz0ANFfeGrFKi99s3GQjLiL1IKH3KtSNEqrPiVbloHVRiNbNtpsYQq9rkV2AV+7jxvd1X1ObUMVDnAEnXA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.9.4" + } + }, + "node_modules/motion-utils": { + "version": "12.9.4", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.9.4.tgz", + "integrity": "sha512-BW3I65zeM76CMsfh3kHid9ansEJk9Qvl+K5cu4DVHKGsI52n76OJ4z2CUJUV+Mn3uEP9k1JJA3tClG0ggSrRcg==", + "license": "MIT" + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -25988,8 +26032,7 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tsutils": { "version": "3.21.0", diff --git a/refact-agent/gui/package.json b/refact-agent/gui/package.json index f1e387b0a..a3c72f707 100644 --- a/refact-agent/gui/package.json +++ b/refact-agent/gui/package.json @@ -56,6 +56,7 @@ "@tanstack/react-table": "^8.20.6", "@types/react": "^18.2.43", "debug": "^4.3.7", + "framer-motion": "^12.10.4", "graphql": "^16.11.0", "react-arborist": "^3.4.3", "react-redux": "^9.1.2", diff --git a/refact-agent/gui/src/__fixtures__/tools_response.ts b/refact-agent/gui/src/__fixtures__/tools_response.ts index e4d5d3d3c..9ec6041ef 100644 --- a/refact-agent/gui/src/__fixtures__/tools_response.ts +++ b/refact-agent/gui/src/__fixtures__/tools_response.ts @@ -1,270 +1,222 @@ -import { ToolCommand } from "../services/refact"; +import { ToolGroup } from "../services/refact"; -export const STUB_TOOL_RESPONSE: ToolCommand[] = [ +export const STUB_TOOL_RESPONSE: ToolGroup[] = [ { - type: "function", - function: { - name: "search", - agentic: false, - description: "Find similar pieces of code or text using vector database", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: - "Single line, paragraph or code sample to search for similar content.", - }, - scope: { - type: "string", - description: - "'workspace' to search all files in workspace, 'dir/subdir/' to search in files within a directory, 'dir/file.ext' to search in a single file.", - }, - }, - required: ["query", "scope"], - }, - }, - }, - { - type: "function", - function: { - name: "definition", - agentic: false, - description: "Read definition of a symbol in the project using AST", - parameters: { - type: "object", - properties: { - symbol: { - type: "string", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - }, - skeleton: { - type: "boolean", - description: - "Skeletonize ouput. Set true to explore, set false when as much context as possible is needed.", - }, - }, - required: ["symbol"], - }, - }, - }, - { - type: "function", - function: { - name: "references", - agentic: false, - description: "Find usages of a symbol within a project using AST", - parameters: { - type: "object", - properties: { - symbol: { - type: "string", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - }, - skeleton: { - type: "boolean", - description: - "Skeletonize ouput. Set true to explore, set false when as much context as possible is needed.", - }, - }, - required: ["symbol"], - }, - }, - }, - { - type: "function", - function: { - name: "tree", - agentic: false, - description: - "Get a files tree with symbols for the project. Use it to get familiar with the project, file names and symbols", - parameters: { - type: "object", - properties: { - path: { - type: "string", - description: - "An absolute path to get files tree for. Do not pass it if you need a full project tree.", - }, - use_ast: { - type: "boolean", - description: - "If true, for each file an array of AST symbols will appear as well as its filename", + name: "ast", + description: "Use AST to find information", + tools: [ + { + enabled: true, + spec: { + name: "definition", + display_name: "Definition", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "builtin", + config_path: "~/.config/refact/builtin_tools.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: [], }, - }, - }, - { - type: "function", - function: { - name: "web", - agentic: false, - description: "Fetch a web page and convert to readable plain text.", - parameters: { - type: "object", - properties: { - url: { - type: "string", - description: "URL of the web page to fetch.", + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: ["url"], }, - }, + ], }, { - type: "function", - function: { - name: "cat", - agentic: false, - description: - "Like cat in console, but better: it can read multiple files and skeletonize them. Give it AST symbols important for the goal (classes, functions, variables, etc) to see them in full. It can also read images just fine.", - parameters: { - type: "object", - properties: { - paths: { - type: "string", - description: - "Comma separated file names or directories: dir1/file1.ext, dir2/file2.ext, dir3/dir4", - }, - symbols: { - type: "string", - description: - "Comma separated AST symbols: MyClass, MyClass::method, my_function", - }, - skeleton: { - type: "boolean", - description: - "if true, files will be skeletonized - mostly only AST symbols will be visible", + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: ["paths"], }, - }, - }, - { - type: "function", - function: { - name: "locate", - agentic: true, - description: - "Get a list of files that are relevant to solve a particular task.", - parameters: { - type: "object", - properties: { - problem_statement: { - type: "string", - description: - "Copy word-for-word the problem statement as provided by the user, if available. Otherwise, tell what you need to do in your own words.", + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: ["problem_statement"], }, - }, - }, - { - type: "function", - function: { - name: "patch", - agentic: true, - description: - "Collect context first, then write the necessary changes using the 📍-notation before code blocks, then call this function to apply the changes.\nTo make this call correctly, you only need the tickets.\nIf you wrote changes for multiple files, call this tool in parallel for each file.\nIf you have several attempts to change a single thing, for example following a correction from the user, pass only the ticket for the latest one.\nMultiple tickets is allowed only for PARTIAL_EDIT, otherwise only one ticket must be provided.\n", - parameters: { - type: "object", - properties: { - path: { - type: "string", - description: "Path to the file to change.", - }, - tickets: { - type: "string", - description: - "Use 3-digit tickets comma separated to refer to the changes within ONE file. No need to copy anything else. Additionaly, you can put DELETE here to delete the file.", + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: ["tickets", "path"], }, - }, - }, - { - type: "function", - function: { - name: "postgres", - agentic: true, - description: "PostgreSQL integration, can run a single query per call.", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: - "Don't forget semicolon at the end, examples:\nSELECT * FROM table_name;\nCREATE INDEX my_index_users_email ON my_users (email);\n", + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: ["query"], }, - }, - }, - { - type: "function", - function: { - name: "docker", - agentic: true, - description: - "Access to docker cli, in a non-interactive way, don't open a shell.", - parameters: { - type: "object", - properties: { - command: { - type: "string", - description: "Examples: docker images", + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: ["project_dir", "command"], }, - }, - }, - { - type: "function", - function: { - name: "knowledge", - agentic: true, - description: - "Fetches successful trajectories to help you accomplish your task. Call each time you have a new task to increase your chances of success.", - parameters: { - type: "object", - properties: { - im_going_to_use_tools: { - type: "string", - description: - "Which tools are you about to use? Comma-separated list, examples: hg, git, gitlab, rust debugger, patch", - }, - im_going_to_apply_to: { - type: "string", - description: - "What your actions will be applied to? List all you can identify, starting with the project name. Comma-separated list, examples: project1, file1.cpp, MyClass, PRs, issues", - }, - goal: { - type: "string", - description: "What is your goal here?", - }, - language_slash_framework: { - type: "string", - description: - "What programming language and framework is the current project using? Use lowercase, dashes and dots. Examples: python/django, typescript/node.js, rust/tokio, ruby/rails, php/laravel, c++/boost-asio", + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, }, - required: [ - "im_going_to_use_tools", - "im_going_to_apply_to", - "goal", - "language_slash_framework", - ], }, - }, + ], }, ]; diff --git a/refact-agent/gui/src/app/middleware.ts b/refact-agent/gui/src/app/middleware.ts index 84cb1a8aa..8d8031cc0 100644 --- a/refact-agent/gui/src/app/middleware.ts +++ b/refact-agent/gui/src/app/middleware.ts @@ -383,14 +383,14 @@ startListening({ toolsApi.endpoints.getTools.initiate(undefined), ); toolsRequest.unsubscribe(); - const toolResult = await toolsRequest.unwrap(); + // const toolResult = await toolsRequest.unwrap(); // TODO: set mode to configure ? or infer it later // TODO: create a dedicated thunk for this. await listenerApi.dispatch( chatAskQuestionThunk({ messages: state.chat.thread.messages, chatId: state.chat.thread.id, - tools: toolResult, + // tools: toolResult, }), ); }, diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx index e513046b6..c366ac1fd 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx @@ -1,8 +1,23 @@ import { + ChevronLeftIcon, + ChevronRightIcon, MixerVerticalIcon, QuestionMarkCircledIcon, } from "@radix-ui/react-icons"; -import { Flex, HoverCard, IconButton, Popover, Text } from "@radix-ui/themes"; +import { + Badge, + Button, + Card, + Flex, + Heading, + HoverCard, + IconButton, + Popover, + Separator, + Switch, + Text, + Tooltip, +} from "@radix-ui/themes"; import { AgentRollbackSwitch, ApplyPatchSwitch, @@ -14,9 +29,23 @@ import { selectAutomaticPatch, selectCheckpointsEnabled, } from "../../features/Chat"; -import { useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; +import { AnimatePresence, motion } from "framer-motion"; +import { STUB_TOOL_RESPONSE } from "../../__fixtures__/tools_response"; +import { ToolGroup } from "../../services/refact"; + +const TOOLS_PER_PAGE = 5; export const AgentCapabilities = () => { + const [selectedToolGroup, setSelectedToolGroup] = useState( + null, + ); + const [toolGroupPage, setToolGroupPage] = useState(0); + + useEffect(() => { + setToolGroupPage(0); + }, [selectedToolGroup]); + const isPatchAutomatic = useAppSelector(selectAutomaticPatch); const isAgentRollbackEnabled = useAppSelector(selectCheckpointsEnabled); const areFollowUpsEnabled = useAppSelector(selectAreFollowUpsEnabled); @@ -25,15 +54,24 @@ export const AgentCapabilities = () => { { name: "Auto-patch", enabled: isPatchAutomatic, + switcher: , + }, + { + name: "Files rollback", + enabled: isAgentRollbackEnabled, + switcher: , + }, + { + name: "Follow-Ups", + enabled: areFollowUpsEnabled, + switcher: , }, - { name: "Files rollback", enabled: isAgentRollbackEnabled }, - { name: "Follow-Ups", enabled: areFollowUpsEnabled }, ]; }, [isPatchAutomatic, isAgentRollbackEnabled, areFollowUpsEnabled]); return ( - + @@ -41,9 +79,160 @@ export const AgentCapabilities = () => { - - - + {agenticFeatures.map((feature) => { + return feature.switcher; + })} + + + + Manage Tool Groups + + + {!selectedToolGroup ? ( + + + {STUB_TOOL_RESPONSE.map((toolGroup) => ( + setSelectedToolGroup(toolGroup)} + style={{ + cursor: "pointer", + }} + > + + + + {/* TODO: do we want to differentiate somehow groups by their source types or only tools themselves? */} + {toolGroup.name} + + + + + + + {toolGroup.description} + + + + + + + + {toolGroup.tools.length} + + + + + + + + ))} + + + ) : ( + + + + + + {selectedToolGroup.name} + + + + + {selectedToolGroup.tools + .slice( + toolGroupPage * TOOLS_PER_PAGE, + (toolGroupPage + 1) * TOOLS_PER_PAGE, + ) + .map((tool) => ( + + + + 🔨 {tool.spec.display_name} + + + + + + + + {tool.spec.description} + + + + + + + ))} + + {/* Pagination controls */} + {selectedToolGroup.tools.length > TOOLS_PER_PAGE && ( + + + + Page {toolGroupPage + 1} of{" "} + {Math.ceil( + selectedToolGroup.tools.length / TOOLS_PER_PAGE, + )} + + + + )} + + + )} + + diff --git a/refact-agent/gui/src/features/Chat/Thread/actions.ts b/refact-agent/gui/src/features/Chat/Thread/actions.ts index 8809be84b..747d71e06 100644 --- a/refact-agent/gui/src/features/Chat/Thread/actions.ts +++ b/refact-agent/gui/src/features/Chat/Thread/actions.ts @@ -30,7 +30,7 @@ import { generateChatTitle, sendChat, } from "../../../services/refact/chat"; -import { ToolCommand, toolsApi } from "../../../services/refact/tools"; +// import { ToolCommand, toolsApi } from "../../../services/refact/tools"; import { scanFoDuplicatesWith, takeFromEndWhile } from "../../../utils"; import { ChatHistoryItem } from "../../History/historySlice"; import { ideToolCallResponse } from "../../../hooks/useEventBusForIDE"; @@ -313,14 +313,14 @@ export const chatAskQuestionThunk = createAppAsyncThunk< { messages: ChatMessages; chatId: string; - tools: ToolCommand[] | null; + // tools: ToolCommand[] | null; checkpointsEnabled?: boolean; mode?: LspChatMode; // used once for actions // TODO: make a separate function for this... and it'll need to be saved. } >( "chatThread/sendChat", - ({ messages, chatId, tools, mode, checkpointsEnabled }, thunkAPI) => { + ({ messages, chatId, /*tools,*/ mode, checkpointsEnabled }, thunkAPI) => { const state = thunkAPI.getState(); const thread = @@ -343,7 +343,7 @@ export const chatAskQuestionThunk = createAppAsyncThunk< messages: messagesForLsp, last_user_message_id: maybeLastUserMessageId, model: state.chat.thread.model, - tools, + // tools, stream: true, abortSignal: thunkAPI.signal, increase_max_tokens: increaseMaxTokens, @@ -407,7 +407,7 @@ export const sendCurrentChatToLspAfterToolCallUpdate = createAppAsyncThunk< "chatThread/sendCurrentChatToLspAfterToolCallUpdate", async ({ chatId, toolCallId }, thunkApi) => { const state = thunkApi.getState(); - const toolUse = state.chat.thread.tool_use; + // const toolUse = state.chat.thread.tool_use; if (state.chat.thread.id !== chatId) return; if ( state.chat.streaming || @@ -429,24 +429,24 @@ export const sendCurrentChatToLspAfterToolCallUpdate = createAppAsyncThunk< if (!toolUseInThisSet) return; thunkApi.dispatch(setIsWaitingForResponse(true)); // duplicate in sendChat - let tools = await thunkApi - .dispatch(toolsApi.endpoints.getTools.initiate(undefined)) - .unwrap(); - - if (toolUse === "quick") { - tools = []; - } else if (toolUse === "explore") { - tools = tools.filter((t) => !t.function.agentic); - } - tools = tools.map((t) => { - const { agentic: _, ...remaining } = t.function; - return { ...t, function: { ...remaining } }; - }); + // let tools = await thunkApi + // .dispatch(toolsApi.endpoints.getTools.initiate(undefined)) + // .unwrap(); + + // if (toolUse === "quick") { + // tools = []; + // } else if (toolUse === "explore") { + // tools = tools.filter((t) => !t.function.agentic); + // } + // tools = tools.map((t) => { + // const { agentic: _, ...remaining } = t.function; + // return { ...t, function: { ...remaining } }; + // }); return thunkApi.dispatch( chatAskQuestionThunk({ messages: state.chat.thread.messages, - tools, + // tools, chatId, mode: state.chat.thread.mode, checkpointsEnabled: state.chat.checkpoints_enabled, diff --git a/refact-agent/gui/src/hooks/useSendChatRequest.ts b/refact-agent/gui/src/hooks/useSendChatRequest.ts index 806d30414..0ab2007cb 100644 --- a/refact-agent/gui/src/hooks/useSendChatRequest.ts +++ b/refact-agent/gui/src/hooks/useSendChatRequest.ts @@ -19,7 +19,7 @@ import { } from "../features/Chat/Thread/selectors"; import { useCheckForConfirmationMutation, - useGetToolsLazyQuery, + // useGetToolsLazyQuery, } from "./useGetToolsQuery"; import { ChatMessage, @@ -95,7 +95,7 @@ export const useSendChatRequest = () => { const dispatch = useAppDispatch(); const abortControllers = useAbortControllers(); - const [triggerGetTools] = useGetToolsLazyQuery(); + // const [triggerGetTools] = useGetToolsLazyQuery(); const [triggerCheckForConfirmation] = useCheckForConfirmationMutation(); const chatId = useAppSelector(selectChatId); @@ -129,20 +129,22 @@ export const useSendChatRequest = () => { const sendMessages = useCallback( async (messages: ChatMessages, maybeMode?: LspChatMode) => { dispatch(setIsWaitingForResponse(true)); - let tools = await triggerGetTools(undefined).unwrap(); - // TODO: save tool use to state.chat - // if (toolUse && isToolUse(toolUse)) { - // dispatch(setToolUse(toolUse)); + // TODO: should be safe to remove + + // let tools = await triggerGetTools(undefined).unwrap(); + // // TODO: save tool use to state.chat + // // if (toolUse && isToolUse(toolUse)) { + // // dispatch(setToolUse(toolUse)); + // // } + // if (toolUse === "quick") { + // tools = []; + // } else if (toolUse === "explore") { + // tools = tools.filter((t) => !t.function.agentic); // } - if (toolUse === "quick") { - tools = []; - } else if (toolUse === "explore") { - tools = tools.filter((t) => !t.function.agentic); - } - tools = tools.map((t) => { - const { agentic: _, ...remaining } = t.function; - return { ...t, function: { ...remaining } }; - }); + // tools = tools.map((t) => { + // const { agentic: _, ...remaining } = t.function; + // return { ...t, function: { ...remaining } }; + // }); const lastMessage = messages.slice(-1)[0]; @@ -184,7 +186,7 @@ export const useSendChatRequest = () => { const action = chatAskQuestionThunk({ messages, - tools, + // tools checkpointsEnabled, chatId, mode, @@ -194,7 +196,7 @@ export const useSendChatRequest = () => { abortControllers.addAbortController(chatId, dispatchedAction.abort); }, [ - triggerGetTools, + // triggerGetTools, toolUse, isWaiting, dispatch, diff --git a/refact-agent/gui/src/services/refact/chat.ts b/refact-agent/gui/src/services/refact/chat.ts index 1b9f6706e..e6b1751a5 100644 --- a/refact-agent/gui/src/services/refact/chat.ts +++ b/refact-agent/gui/src/services/refact/chat.ts @@ -1,6 +1,6 @@ import { IntegrationMeta, LspChatMode } from "../../features/Chat"; import { CHAT_URL } from "./consts"; -import { ToolCommand } from "./tools"; +// import { ToolCommand } from "./tools"; import { ChatRole, ThinkingBlock, @@ -59,7 +59,7 @@ type SendChatArgs = { takeNote?: boolean; onlyDeterministicMessages?: boolean; chatId?: string; - tools: ToolCommand[] | null; + // tools: ToolCommand[] | null; port?: number; apiKey?: string | null; // isConfig?: boolean; @@ -150,7 +150,7 @@ export async function sendChat({ // takeNote = false, onlyDeterministicMessages: only_deterministic_messages, chatId: chat_id, - tools, + // tools, port = 8001, apiKey, checkpointsEnabled = true, @@ -175,7 +175,7 @@ export async function sendChat({ messages, model: model, stream, - tools, + // tools, only_deterministic_messages, checkpoints_enabled: checkpointsEnabled, // chat_id, diff --git a/refact-agent/gui/src/services/refact/tools.ts b/refact-agent/gui/src/services/refact/tools.ts index bb78de449..8f34f2d8d 100644 --- a/refact-agent/gui/src/services/refact/tools.ts +++ b/refact-agent/gui/src/services/refact/tools.ts @@ -122,23 +122,45 @@ export const toolsApi = createApi({ refetchOnMountOrArgChange: true, }); -export type ToolParams = { +export type ToolGroup = { + name: string; + description: string; + tools: Tool[]; +}; + +export type ToolSource = { + source_type: "builtin" | "integration"; + config_path: string; +}; + +export type ToolParam = { name: string; type: string; description: string; }; -export type ToolFunction = { - agentic?: boolean; +export type ToolSpec = { name: string; + display_name: string; description: string; - // parameters: ToolParams[]; - parameters: Record; + + // TODO: investigate on parameters + parameters: ToolParam[]; + // parameters: Record; + source: ToolSource; + parameters_required?: string[]; + agentic: boolean; + experimental?: boolean; +}; + +export type Tool = { + spec: ToolSpec; + enabled: boolean; }; export type ToolCommand = { - function: ToolFunction; + function: ToolSpec; type: "function"; }; From b85374c6360ae770038dac7a56a117a5ecfcd51f Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 9 May 2025 13:23:39 +0200 Subject: [PATCH 61/90] refactor: better code management for AgentCapabilities and ToolGroups --- .../gui/src/__fixtures__/tools_response.ts | 1610 +++++++++++++++++ .../components/ChatForm/AgentCapabilities.tsx | 261 --- .../AgentCapabilities/AgentCapabilities.tsx | 91 + .../AgentCapabilities/ToolGroup.module.css | 9 + .../ChatForm/AgentCapabilities/ToolGroup.tsx | 63 + .../ChatForm/AgentCapabilities/ToolGroups.tsx | 118 ++ .../ChatForm/AgentCapabilities/index.ts | 1 + .../gui/src/components/ChatForm/ChatForm.tsx | 2 +- 8 files changed, 1893 insertions(+), 262 deletions(-) delete mode 100644 refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx create mode 100644 refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx create mode 100644 refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css create mode 100644 refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx create mode 100644 refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx create mode 100644 refact-agent/gui/src/components/ChatForm/AgentCapabilities/index.ts diff --git a/refact-agent/gui/src/__fixtures__/tools_response.ts b/refact-agent/gui/src/__fixtures__/tools_response.ts index 9ec6041ef..1e764c702 100644 --- a/refact-agent/gui/src/__fixtures__/tools_response.ts +++ b/refact-agent/gui/src/__fixtures__/tools_response.ts @@ -212,6 +212,1616 @@ export const STUB_TOOL_RESPONSE: ToolGroup[] = [ "~/.config/refact/integrations.d/youShouldNotCare.yaml", }, + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + ], + }, + { + name: "mcp_fetch", + description: "Use Fetch MCP to execute HTTP requests ", + tools: [ + { + enabled: true, + spec: { + name: "mcp_fetch", + display_name: "MCP Fetch", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: "~/.config/refact/integration_tools.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition2", + display_name: "Definition Two", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition3", + display_name: "Definition Three", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition4", + display_name: "Definition Four", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition5", + display_name: "Definition Five", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + + parameters_required: ["symbol"], + agentic: false, + experimental: false, + }, + }, + { + enabled: false, + spec: { + name: "definition6", + display_name: "Definition Six", + description: "Find definition of a symbol in the project using AST", + + parameters: [ + { + name: "symbol", + description: + "The exact name of a function, method, class, type alias. No spaces allowed.", + type: "string", + }, + ], + source: { + source_type: "integration", + config_path: + "~/.config/refact/integrations.d/youShouldNotCare.yaml", + }, + parameters_required: ["symbol"], agentic: false, experimental: false, diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx deleted file mode 100644 index c366ac1fd..000000000 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities.tsx +++ /dev/null @@ -1,261 +0,0 @@ -import { - ChevronLeftIcon, - ChevronRightIcon, - MixerVerticalIcon, - QuestionMarkCircledIcon, -} from "@radix-ui/react-icons"; -import { - Badge, - Button, - Card, - Flex, - Heading, - HoverCard, - IconButton, - Popover, - Separator, - Switch, - Text, - Tooltip, -} from "@radix-ui/themes"; -import { - AgentRollbackSwitch, - ApplyPatchSwitch, - FollowUpsSwitch, -} from "./ChatControls"; -import { useAppSelector } from "../../hooks"; -import { - selectAreFollowUpsEnabled, - selectAutomaticPatch, - selectCheckpointsEnabled, -} from "../../features/Chat"; -import { useEffect, useMemo, useState } from "react"; -import { AnimatePresence, motion } from "framer-motion"; -import { STUB_TOOL_RESPONSE } from "../../__fixtures__/tools_response"; -import { ToolGroup } from "../../services/refact"; - -const TOOLS_PER_PAGE = 5; - -export const AgentCapabilities = () => { - const [selectedToolGroup, setSelectedToolGroup] = useState( - null, - ); - const [toolGroupPage, setToolGroupPage] = useState(0); - - useEffect(() => { - setToolGroupPage(0); - }, [selectedToolGroup]); - - const isPatchAutomatic = useAppSelector(selectAutomaticPatch); - const isAgentRollbackEnabled = useAppSelector(selectCheckpointsEnabled); - const areFollowUpsEnabled = useAppSelector(selectAreFollowUpsEnabled); - const agenticFeatures = useMemo(() => { - return [ - { - name: "Auto-patch", - enabled: isPatchAutomatic, - switcher: , - }, - { - name: "Files rollback", - enabled: isAgentRollbackEnabled, - switcher: , - }, - { - name: "Follow-Ups", - enabled: areFollowUpsEnabled, - switcher: , - }, - ]; - }, [isPatchAutomatic, isAgentRollbackEnabled, areFollowUpsEnabled]); - - return ( - - - - - - - - - - {agenticFeatures.map((feature) => { - return feature.switcher; - })} - - - - Manage Tool Groups - - - {!selectedToolGroup ? ( - - - {STUB_TOOL_RESPONSE.map((toolGroup) => ( - setSelectedToolGroup(toolGroup)} - style={{ - cursor: "pointer", - }} - > - - - - {/* TODO: do we want to differentiate somehow groups by their source types or only tools themselves? */} - {toolGroup.name} - - - - - - - {toolGroup.description} - - - - - - - - {toolGroup.tools.length} - - - - - - - - ))} - - - ) : ( - - - - - - {selectedToolGroup.name} - - - - - {selectedToolGroup.tools - .slice( - toolGroupPage * TOOLS_PER_PAGE, - (toolGroupPage + 1) * TOOLS_PER_PAGE, - ) - .map((tool) => ( - - - - 🔨 {tool.spec.display_name} - - - - - - - - {tool.spec.description} - - - - - - - ))} - - {/* Pagination controls */} - {selectedToolGroup.tools.length > TOOLS_PER_PAGE && ( - - - - Page {toolGroupPage + 1} of{" "} - {Math.ceil( - selectedToolGroup.tools.length / TOOLS_PER_PAGE, - )} - - - - )} - - - )} - - - - - - - Enabled Features: - - {" "} - {agenticFeatures - .filter((feature) => feature.enabled) - .map((feature) => feature.name) - .join(", ") || "None"} - - - - - - - - - Here you can control special features affecting Agent behaviour - - - - - ); -}; diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx new file mode 100644 index 000000000..82718558c --- /dev/null +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx @@ -0,0 +1,91 @@ +import { + MixerVerticalIcon, + QuestionMarkCircledIcon, +} from "@radix-ui/react-icons"; +import { + Flex, + HoverCard, + IconButton, + Popover, + Separator, + Text, +} from "@radix-ui/themes"; +import { + AgentRollbackSwitch, + ApplyPatchSwitch, + FollowUpsSwitch, +} from "../ChatControls"; +import { useAppSelector } from "../../../hooks"; +import { + selectAreFollowUpsEnabled, + selectAutomaticPatch, + selectCheckpointsEnabled, +} from "../../../features/Chat"; +import { useMemo } from "react"; +import { ToolGroups } from "./ToolGroups"; + +export const AgentCapabilities = () => { + const isPatchAutomatic = useAppSelector(selectAutomaticPatch); + const isAgentRollbackEnabled = useAppSelector(selectCheckpointsEnabled); + const areFollowUpsEnabled = useAppSelector(selectAreFollowUpsEnabled); + const agenticFeatures = useMemo(() => { + return [ + { + name: "Auto-patch", + enabled: isPatchAutomatic, + switcher: , + }, + { + name: "Files rollback", + enabled: isAgentRollbackEnabled, + switcher: , + }, + { + name: "Follow-Ups", + enabled: areFollowUpsEnabled, + switcher: , + }, + ]; + }, [isPatchAutomatic, isAgentRollbackEnabled, areFollowUpsEnabled]); + + return ( + + + + + + + + + + {agenticFeatures.map((feature) => { + return feature.switcher; + })} + + + + + + + Enabled Features: + + {" "} + {agenticFeatures + .filter((feature) => feature.enabled) + .map((feature) => feature.name) + .join(", ") || "None"} + + + + + + + + + Here you can control special features affecting Agent behaviour + + + + + ); +}; diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css new file mode 100644 index 000000000..133307f4a --- /dev/null +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css @@ -0,0 +1,9 @@ +.toolGroup { + box-sizing: border-box; + border-radius: 4px; + transition: background-color 0.15s ease-in-out; + cursor: pointer; + &:hover { + background-color: var(--gray-a2); + } +} diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx new file mode 100644 index 000000000..9f2bdc44b --- /dev/null +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx @@ -0,0 +1,63 @@ +import { + ChevronRightIcon, + QuestionMarkCircledIcon, +} from "@radix-ui/react-icons"; +import { + Badge, + Box, + Flex, + Heading, + HoverCard, + Text, + Tooltip, +} from "@radix-ui/themes"; +import React from "react"; +import { ToolGroup as ToolGroupType } from "../../../services/refact"; + +import styles from "./ToolGroup.module.css"; + +export type ToolGroupProps = { + group: ToolGroupType; + setSelectedToolGroup: (group: ToolGroupType) => void; +}; + +export const ToolGroup: React.FC = ({ + group, + setSelectedToolGroup, +}) => { + return ( + setSelectedToolGroup(group)} + py="1" + pl="2" + pr="1" + className={styles.toolGroup} + > + + + + {/* TODO: do we want to differentiate somehow groups by their source types or only tools themselves? */} + {group.name} + + + + + + + {group.description} + + + + + + + {group.tools.length} + + + + + + + ); +}; diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx new file mode 100644 index 000000000..5bbdd379d --- /dev/null +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -0,0 +1,118 @@ +import { + Button, + Flex, + Heading, + HoverCard, + Switch, + Text, +} from "@radix-ui/themes"; +import { AnimatePresence, motion } from "framer-motion"; +import React, { useState } from "react"; +import { STUB_TOOL_RESPONSE } from "../../../__fixtures__/tools_response"; +import { + ChevronLeftIcon, + QuestionMarkCircledIcon, +} from "@radix-ui/react-icons"; +import { ToolGroup as ToolGroupType } from "../../../services/refact"; +import { ToolGroup } from "./ToolGroup"; +import { ScrollArea } from "../../ScrollArea"; + +export const ToolGroups: React.FC = () => { + const [selectedToolGroup, setSelectedToolGroup] = + useState(null); + + return ( + + + Manage Tool Groups + + + {!selectedToolGroup ? ( + + + + {STUB_TOOL_RESPONSE.map((toolGroup) => ( + + ))} + + + + ) : ( + + + + + + {selectedToolGroup.name} + + + + + + {selectedToolGroup.tools.map((tool) => ( + + + + 🔨 {tool.spec.display_name} + + + + + + + + {tool.spec.description} + + + + + + + ))} + + + + + )} + + + ); +}; diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/index.ts b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/index.ts new file mode 100644 index 000000000..9a8932dde --- /dev/null +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/index.ts @@ -0,0 +1 @@ +export { AgentCapabilities } from "./AgentCapabilities"; diff --git a/refact-agent/gui/src/components/ChatForm/ChatForm.tsx b/refact-agent/gui/src/components/ChatForm/ChatForm.tsx index 0fa766fe9..b29351ebd 100644 --- a/refact-agent/gui/src/components/ChatForm/ChatForm.tsx +++ b/refact-agent/gui/src/components/ChatForm/ChatForm.tsx @@ -63,7 +63,7 @@ import { } from "../../features/Chat"; import { telemetryApi } from "../../services/refact"; import { push } from "../../features/Pages/pagesSlice"; -import { AgentCapabilities } from "./AgentCapabilities"; +import { AgentCapabilities } from "./AgentCapabilities/AgentCapabilities"; import { TokensPreview } from "./TokensPreview"; import classNames from "classnames"; import { ArchiveIcon } from "@radix-ui/react-icons"; From 1ca83f93bc20d6bec970aa9b2784f2bbd42e8afb Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 9 May 2025 13:36:54 +0200 Subject: [PATCH 62/90] wip: tool group categories --- .../gui/src/__fixtures__/tools_response.ts | 1612 +---------------- .../AgentCapabilities/ToolGroup.module.css | 9 + .../ChatForm/AgentCapabilities/ToolGroup.tsx | 33 +- refact-agent/gui/src/services/refact/tools.ts | 1 + 4 files changed, 42 insertions(+), 1613 deletions(-) diff --git a/refact-agent/gui/src/__fixtures__/tools_response.ts b/refact-agent/gui/src/__fixtures__/tools_response.ts index 1e764c702..09afe2e72 100644 --- a/refact-agent/gui/src/__fixtures__/tools_response.ts +++ b/refact-agent/gui/src/__fixtures__/tools_response.ts @@ -3,6 +3,7 @@ import { ToolGroup } from "../services/refact"; export const STUB_TOOL_RESPONSE: ToolGroup[] = [ { name: "ast", + category: "builtin", description: "Use AST to find information", tools: [ { @@ -60,1616 +61,7 @@ export const STUB_TOOL_RESPONSE: ToolGroup[] = [ }, { name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", - description: "Use Fetch MCP to execute HTTP requests ", - tools: [ - { - enabled: true, - spec: { - name: "mcp_fetch", - display_name: "MCP Fetch", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: "~/.config/refact/integration_tools.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition2", - display_name: "Definition Two", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition3", - display_name: "Definition Three", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition4", - display_name: "Definition Four", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition5", - display_name: "Definition Five", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - { - enabled: false, - spec: { - name: "definition6", - display_name: "Definition Six", - description: "Find definition of a symbol in the project using AST", - - parameters: [ - { - name: "symbol", - description: - "The exact name of a function, method, class, type alias. No spaces allowed.", - type: "string", - }, - ], - source: { - source_type: "integration", - config_path: - "~/.config/refact/integrations.d/youShouldNotCare.yaml", - }, - - parameters_required: ["symbol"], - agentic: false, - experimental: false, - }, - }, - ], - }, - { - name: "mcp_fetch", + category: "mcp", description: "Use Fetch MCP to execute HTTP requests ", tools: [ { diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css index 133307f4a..81584ad7a 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css @@ -7,3 +7,12 @@ background-color: var(--gray-a2); } } + +.categoryBadge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + padding: 0; +} diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx index 9f2bdc44b..2f8b33e0f 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx @@ -4,6 +4,7 @@ import { } from "@radix-ui/react-icons"; import { Badge, + BadgeProps, Box, Flex, Heading, @@ -11,7 +12,7 @@ import { Text, Tooltip, } from "@radix-ui/themes"; -import React from "react"; +import React, { useMemo } from "react"; import { ToolGroup as ToolGroupType } from "../../../services/refact"; import styles from "./ToolGroup.module.css"; @@ -25,6 +26,22 @@ export const ToolGroup: React.FC = ({ group, setSelectedToolGroup, }) => { + const categoryBadge = useMemo(() => { + const categoryMap: Record< + string, + { color: BadgeProps["color"]; tooltip: string } + > = { + builtin: { color: "red", tooltip: "Built-In Tools" }, + integration: { color: "yellow", tooltip: "Integrations Tools" }, + mcp: { color: "green", tooltip: "MCP Tools" }, + }; + + const { color, tooltip } = categoryMap[group.category]; + const label = group.category.charAt(0).toUpperCase(); + + return { label, color, tooltip }; + }, [group.category]); + return ( = ({ - {/* TODO: do we want to differentiate somehow groups by their source types or only tools themselves? */} - {group.name} + + + + {categoryBadge.label} + + + {group.name} + diff --git a/refact-agent/gui/src/services/refact/tools.ts b/refact-agent/gui/src/services/refact/tools.ts index 8f34f2d8d..7f5f37b1b 100644 --- a/refact-agent/gui/src/services/refact/tools.ts +++ b/refact-agent/gui/src/services/refact/tools.ts @@ -124,6 +124,7 @@ export const toolsApi = createApi({ export type ToolGroup = { name: string; + category: "integration" | "mcp" | "builtin"; description: string; tools: Tool[]; }; From e2daec8fbf64152f34a089ed36b0fb5a3227bacb Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 9 May 2025 13:37:42 +0200 Subject: [PATCH 63/90] chore: fixed formatting --- .../AgentCapabilities/ToolGroup.module.css | 36 +-- .../ChatForm/AgentCapabilities/ToolGroup.tsx | 180 ++++++------- .../ChatForm/AgentCapabilities/ToolGroups.tsx | 236 +++++++++--------- 3 files changed, 226 insertions(+), 226 deletions(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css index 81584ad7a..b784cbb2a 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.module.css @@ -1,18 +1,18 @@ -.toolGroup { - box-sizing: border-box; - border-radius: 4px; - transition: background-color 0.15s ease-in-out; - cursor: pointer; - &:hover { - background-color: var(--gray-a2); - } -} - -.categoryBadge { - display: inline-flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - padding: 0; -} +.toolGroup { + box-sizing: border-box; + border-radius: 4px; + transition: background-color 0.15s ease-in-out; + cursor: pointer; + &:hover { + background-color: var(--gray-a2); + } +} + +.categoryBadge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + padding: 0; +} diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx index 2f8b33e0f..ed51fc1a8 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx @@ -1,90 +1,90 @@ -import { - ChevronRightIcon, - QuestionMarkCircledIcon, -} from "@radix-ui/react-icons"; -import { - Badge, - BadgeProps, - Box, - Flex, - Heading, - HoverCard, - Text, - Tooltip, -} from "@radix-ui/themes"; -import React, { useMemo } from "react"; -import { ToolGroup as ToolGroupType } from "../../../services/refact"; - -import styles from "./ToolGroup.module.css"; - -export type ToolGroupProps = { - group: ToolGroupType; - setSelectedToolGroup: (group: ToolGroupType) => void; -}; - -export const ToolGroup: React.FC = ({ - group, - setSelectedToolGroup, -}) => { - const categoryBadge = useMemo(() => { - const categoryMap: Record< - string, - { color: BadgeProps["color"]; tooltip: string } - > = { - builtin: { color: "red", tooltip: "Built-In Tools" }, - integration: { color: "yellow", tooltip: "Integrations Tools" }, - mcp: { color: "green", tooltip: "MCP Tools" }, - }; - - const { color, tooltip } = categoryMap[group.category]; - const label = group.category.charAt(0).toUpperCase(); - - return { label, color, tooltip }; - }, [group.category]); - - return ( - setSelectedToolGroup(group)} - py="1" - pl="2" - pr="1" - className={styles.toolGroup} - > - - - - - - - {categoryBadge.label} - - - {group.name} - - - - - - - - {group.description} - - - - - - - {group.tools.length} - - - - - - - ); -}; +import { + ChevronRightIcon, + QuestionMarkCircledIcon, +} from "@radix-ui/react-icons"; +import { + Badge, + BadgeProps, + Box, + Flex, + Heading, + HoverCard, + Text, + Tooltip, +} from "@radix-ui/themes"; +import React, { useMemo } from "react"; +import { ToolGroup as ToolGroupType } from "../../../services/refact"; + +import styles from "./ToolGroup.module.css"; + +export type ToolGroupProps = { + group: ToolGroupType; + setSelectedToolGroup: (group: ToolGroupType) => void; +}; + +export const ToolGroup: React.FC = ({ + group, + setSelectedToolGroup, +}) => { + const categoryBadge = useMemo(() => { + const categoryMap: Record< + string, + { color: BadgeProps["color"]; tooltip: string } + > = { + builtin: { color: "red", tooltip: "Built-In Tools" }, + integration: { color: "yellow", tooltip: "Integrations Tools" }, + mcp: { color: "green", tooltip: "MCP Tools" }, + }; + + const { color, tooltip } = categoryMap[group.category]; + const label = group.category.charAt(0).toUpperCase(); + + return { label, color, tooltip }; + }, [group.category]); + + return ( + setSelectedToolGroup(group)} + py="1" + pl="2" + pr="1" + className={styles.toolGroup} + > + + + + + + + {categoryBadge.label} + + + {group.name} + + + + + + + + {group.description} + + + + + + + {group.tools.length} + + + + + + + ); +}; diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx index 5bbdd379d..049259b14 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -1,118 +1,118 @@ -import { - Button, - Flex, - Heading, - HoverCard, - Switch, - Text, -} from "@radix-ui/themes"; -import { AnimatePresence, motion } from "framer-motion"; -import React, { useState } from "react"; -import { STUB_TOOL_RESPONSE } from "../../../__fixtures__/tools_response"; -import { - ChevronLeftIcon, - QuestionMarkCircledIcon, -} from "@radix-ui/react-icons"; -import { ToolGroup as ToolGroupType } from "../../../services/refact"; -import { ToolGroup } from "./ToolGroup"; -import { ScrollArea } from "../../ScrollArea"; - -export const ToolGroups: React.FC = () => { - const [selectedToolGroup, setSelectedToolGroup] = - useState(null); - - return ( - - - Manage Tool Groups - - - {!selectedToolGroup ? ( - - - - {STUB_TOOL_RESPONSE.map((toolGroup) => ( - - ))} - - - - ) : ( - - - - - - {selectedToolGroup.name} - - - - - - {selectedToolGroup.tools.map((tool) => ( - - - - 🔨 {tool.spec.display_name} - - - - - - - - {tool.spec.description} - - - - - - - ))} - - - - - )} - - - ); -}; +import { + Button, + Flex, + Heading, + HoverCard, + Switch, + Text, +} from "@radix-ui/themes"; +import { AnimatePresence, motion } from "framer-motion"; +import React, { useState } from "react"; +import { STUB_TOOL_RESPONSE } from "../../../__fixtures__/tools_response"; +import { + ChevronLeftIcon, + QuestionMarkCircledIcon, +} from "@radix-ui/react-icons"; +import { ToolGroup as ToolGroupType } from "../../../services/refact"; +import { ToolGroup } from "./ToolGroup"; +import { ScrollArea } from "../../ScrollArea"; + +export const ToolGroups: React.FC = () => { + const [selectedToolGroup, setSelectedToolGroup] = + useState(null); + + return ( + + + Manage Tool Groups + + + {!selectedToolGroup ? ( + + + + {STUB_TOOL_RESPONSE.map((toolGroup) => ( + + ))} + + + + ) : ( + + + + + + {selectedToolGroup.name} + + + + + + {selectedToolGroup.tools.map((tool) => ( + + + + 🔨 {tool.spec.display_name} + + + + + + + + {tool.spec.description} + + + + + + + ))} + + + + + )} + + + ); +}; From 810ff12d0cd0cef1f24099b63c92789a6d0f6bd0 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Mon, 12 May 2025 20:05:02 +0200 Subject: [PATCH 64/90] feat: added chat title generation switch to agentic features --- .../AgentCapabilities/AgentCapabilities.tsx | 39 ++++++++--- .../src/components/ChatForm/ChatControls.tsx | 70 ++++++++++++++++++- .../gui/src/features/Chat/Thread/actions.ts | 3 + .../gui/src/features/Chat/Thread/reducer.ts | 6 ++ .../gui/src/features/Chat/Thread/selectors.ts | 2 + .../gui/src/features/Chat/Thread/types.ts | 1 + .../gui/src/features/History/historySlice.ts | 4 +- 7 files changed, 113 insertions(+), 12 deletions(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx index 82718558c..0df668506 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/AgentCapabilities.tsx @@ -14,20 +14,26 @@ import { AgentRollbackSwitch, ApplyPatchSwitch, FollowUpsSwitch, + TitleGenerationSwitch, } from "../ChatControls"; import { useAppSelector } from "../../../hooks"; import { selectAreFollowUpsEnabled, selectAutomaticPatch, selectCheckpointsEnabled, + selectIsTitleGenerationEnabled, } from "../../../features/Chat"; -import { useMemo } from "react"; +import { Fragment, useMemo } from "react"; import { ToolGroups } from "./ToolGroups"; export const AgentCapabilities = () => { const isPatchAutomatic = useAppSelector(selectAutomaticPatch); const isAgentRollbackEnabled = useAppSelector(selectCheckpointsEnabled); const areFollowUpsEnabled = useAppSelector(selectAreFollowUpsEnabled); + const isTitleGenerationEnabled = useAppSelector( + selectIsTitleGenerationEnabled, + ); + const agenticFeatures = useMemo(() => { return [ { @@ -45,8 +51,27 @@ export const AgentCapabilities = () => { enabled: areFollowUpsEnabled, switcher: , }, + { + name: "Chat Titles", + enabled: isTitleGenerationEnabled, + switcher: , + }, ]; - }, [isPatchAutomatic, isAgentRollbackEnabled, areFollowUpsEnabled]); + }, [ + isPatchAutomatic, + isAgentRollbackEnabled, + areFollowUpsEnabled, + isTitleGenerationEnabled, + ]); + + const enabledAgenticFeatures = useMemo( + () => + agenticFeatures + .filter((feature) => feature.enabled) + .map((feature) => feature.name) + .join(", ") || "None", + [agenticFeatures], + ); return ( @@ -59,7 +84,7 @@ export const AgentCapabilities = () => { {agenticFeatures.map((feature) => { - return feature.switcher; + return {feature.switcher}; })} @@ -68,13 +93,7 @@ export const AgentCapabilities = () => { Enabled Features: - - {" "} - {agenticFeatures - .filter((feature) => feature.enabled) - .map((feature) => feature.name) - .join(", ") || "None"} - + {enabledAgenticFeatures} diff --git a/refact-agent/gui/src/components/ChatForm/ChatControls.tsx b/refact-agent/gui/src/components/ChatForm/ChatControls.tsx index b0cbf6b25..cb8f7ba0c 100644 --- a/refact-agent/gui/src/components/ChatForm/ChatControls.tsx +++ b/refact-agent/gui/src/components/ChatForm/ChatControls.tsx @@ -33,10 +33,12 @@ import { selectChatId, selectCheckpointsEnabled, selectIsStreaming, + selectIsTitleGenerationEnabled, selectIsWaiting, selectMessages, selectToolUse, setAreFollowUpsEnabled, + setIsTitleGenerationEnabled, setAutomaticPatch, setEnabledCheckpoints, setToolUse, @@ -123,7 +125,7 @@ export const AgentRollbackSwitch: React.FC = () => { @@ -231,6 +233,72 @@ export const FollowUpsSwitch: React.FC = () => { ); }; +export const TitleGenerationSwitch: React.FC = () => { + const dispatch = useAppDispatch(); + const isTitleGenerationEnabled = useAppSelector( + selectIsTitleGenerationEnabled, + ); + + const handleTitleGenerationEnabledChange = (checked: boolean) => { + dispatch(setIsTitleGenerationEnabled(checked)); + }; + + return ( + + + Chat Titles + + + + + + + + + + + When enabled, Refact Agent will automatically generate + summarized chat title for the conversation + + + + + + Warning: may increase coins spending + + + + + + + + + ); +}; + export const CapsSelect: React.FC<{ disabled?: boolean }> = ({ disabled }) => { const refs = useTourRefs(); const caps = useCapsForToolUse(); diff --git a/refact-agent/gui/src/features/Chat/Thread/actions.ts b/refact-agent/gui/src/features/Chat/Thread/actions.ts index 747d71e06..ea926f6fb 100644 --- a/refact-agent/gui/src/features/Chat/Thread/actions.ts +++ b/refact-agent/gui/src/features/Chat/Thread/actions.ts @@ -119,6 +119,9 @@ export const setPreventSend = createAction( export const setAreFollowUpsEnabled = createAction( "chat/setAreFollowUpsEnabled", ); +export const setIsTitleGenerationEnabled = createAction( + "chat/setIsTitleGenerationEnabled", +); export const setToolUse = createAction("chatThread/setToolUse"); diff --git a/refact-agent/gui/src/features/Chat/Thread/reducer.ts b/refact-agent/gui/src/features/Chat/Thread/reducer.ts index b9d575fb6..e8d724266 100644 --- a/refact-agent/gui/src/features/Chat/Thread/reducer.ts +++ b/refact-agent/gui/src/features/Chat/Thread/reducer.ts @@ -40,6 +40,7 @@ import { upsertToolCall, setIncreaseMaxTokens, setAreFollowUpsEnabled, + setIsTitleGenerationEnabled, } from "./actions"; import { formatChatResponse } from "./utils"; import { @@ -148,6 +149,10 @@ export const chatReducer = createReducer(initialState, (builder) => { state.follow_ups_enabled = action.payload; }); + builder.addCase(setIsTitleGenerationEnabled, (state, action) => { + state.title_generation_enabled = action.payload; + }); + builder.addCase(clearChatError, (state, action) => { if (state.thread.id !== action.payload.id) return state; state.error = null; @@ -174,6 +179,7 @@ export const chatReducer = createReducer(initialState, (builder) => { next.system_prompt = state.system_prompt; next.checkpoints_enabled = state.checkpoints_enabled; next.follow_ups_enabled = state.follow_ups_enabled; + next.title_generation_enabled = state.title_generation_enabled; next.thread.boost_reasoning = state.thread.boost_reasoning; // next.thread.automatic_patch = state.thread.automatic_patch; if (action.payload?.messages) { diff --git a/refact-agent/gui/src/features/Chat/Thread/selectors.ts b/refact-agent/gui/src/features/Chat/Thread/selectors.ts index ec2ced48c..6cb772d0f 100644 --- a/refact-agent/gui/src/features/Chat/Thread/selectors.ts +++ b/refact-agent/gui/src/features/Chat/Thread/selectors.ts @@ -35,6 +35,8 @@ export const selectIsWaiting = (state: RootState) => state.chat.waiting_for_response; export const selectAreFollowUpsEnabled = (state: RootState) => state.chat.follow_ups_enabled; +export const selectIsTitleGenerationEnabled = (state: RootState) => + state.chat.title_generation_enabled; export const selectIsStreaming = (state: RootState) => state.chat.streaming; export const selectPreventSend = (state: RootState) => state.chat.prevent_send; export const selectChatError = (state: RootState) => state.chat.error; diff --git a/refact-agent/gui/src/features/Chat/Thread/types.ts b/refact-agent/gui/src/features/Chat/Thread/types.ts index 7a8e31e88..bf6756d8c 100644 --- a/refact-agent/gui/src/features/Chat/Thread/types.ts +++ b/refact-agent/gui/src/features/Chat/Thread/types.ts @@ -51,6 +51,7 @@ export type Chat = { tool_use: ToolUse; send_immediately: boolean; follow_ups_enabled?: boolean; + title_generation_enabled?: boolean; }; export type PayloadWithId = { id: string }; diff --git a/refact-agent/gui/src/features/History/historySlice.ts b/refact-agent/gui/src/features/History/historySlice.ts index d19b83f66..9f9fabe22 100644 --- a/refact-agent/gui/src/features/History/historySlice.ts +++ b/refact-agent/gui/src/features/History/historySlice.ts @@ -208,6 +208,8 @@ startHistoryListening({ actionCreator: doneStreaming, effect: (action, listenerApi) => { const state = listenerApi.getState(); + const isTitleGenerationEnabled = state.chat.title_generation_enabled; + const thread = action.payload.id in state.chat.cache ? state.chat.cache[action.payload.id] @@ -225,7 +227,7 @@ startHistoryListening({ const firstUserMessage = thread.messages.find(isUserMessage); if (firstUserMessage) { // Checking if chat title is already generated, if not - generating it - if (!isTitleGenerated) { + if (!isTitleGenerated && isTitleGenerationEnabled) { listenerApi .dispatch( chatGenerateTitleThunk({ From 283156e37845073aa4f3b94285621f4dd1808b37 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 28 May 2025 15:09:35 +0200 Subject: [PATCH 65/90] feat: getting tool groups from the LSP, rendering them & skeletons when waiting for response to improve UPP --- .../ChatForm/AgentCapabilities/ToolGroups.tsx | 49 ++++++++++++-- refact-agent/gui/src/hooks/index.ts | 2 +- refact-agent/gui/src/hooks/useCanUseTools.ts | 4 +- ...ToolsQuery.ts => useGetToolGroupsQuery.ts} | 6 +- .../gui/src/hooks/useSendChatRequest.ts | 2 +- .../gui/src/services/refact/consts.ts | 2 +- refact-agent/gui/src/services/refact/tools.ts | 67 +++++++++++++++---- 7 files changed, 104 insertions(+), 28 deletions(-) rename refact-agent/gui/src/hooks/{useGetToolsQuery.ts => useGetToolGroupsQuery.ts} (64%) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx index 049259b14..02c2bb3ec 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -3,24 +3,30 @@ import { Flex, Heading, HoverCard, + Skeleton, Switch, Text, } from "@radix-ui/themes"; import { AnimatePresence, motion } from "framer-motion"; import React, { useState } from "react"; -import { STUB_TOOL_RESPONSE } from "../../../__fixtures__/tools_response"; import { ChevronLeftIcon, QuestionMarkCircledIcon, } from "@radix-ui/react-icons"; + +import { useGetToolGroupsQuery } from "../../../hooks"; import { ToolGroup as ToolGroupType } from "../../../services/refact"; -import { ToolGroup } from "./ToolGroup"; + import { ScrollArea } from "../../ScrollArea"; +import { ToolGroup } from "./ToolGroup"; export const ToolGroups: React.FC = () => { + const { data: toolsGroups, isLoading, isSuccess } = useGetToolGroupsQuery(); const [selectedToolGroup, setSelectedToolGroup] = useState(null); + if (isLoading || !isSuccess) return ; + return ( @@ -42,8 +48,12 @@ export const ToolGroups: React.FC = () => { maxHeight: "125px", }} > - - {STUB_TOOL_RESPONSE.map((toolGroup) => ( + + {toolsGroups.map((toolGroup) => ( { > {selectedToolGroup.tools.map((tool) => ( - + 🔨 {tool.spec.display_name} @@ -104,7 +119,7 @@ export const ToolGroups: React.FC = () => { - + ))} @@ -116,3 +131,25 @@ export const ToolGroups: React.FC = () => { ); }; + +const ToolGroupsSkeleton: React.FC = () => { + return ( + + + + Manage Tool Groups + + + + {[1, 2].map((idx) => ( + + + + + + + ))} + + + ); +}; diff --git a/refact-agent/gui/src/hooks/index.ts b/refact-agent/gui/src/hooks/index.ts index 025c865ce..22d2c9e15 100644 --- a/refact-agent/gui/src/hooks/index.ts +++ b/refact-agent/gui/src/hooks/index.ts @@ -15,7 +15,7 @@ export * from "./useGetCapsQuery"; export * from "./useHasCaps"; export * from "./useGetPromptsQuery"; export * from "./useGetStatisticDataQuery"; -export * from "./useGetToolsQuery"; +export * from "./useGetToolGroupsQuery"; export * from "./useAppearance"; export * from "./useConfig"; export * from "./useAppDispatch"; diff --git a/refact-agent/gui/src/hooks/useCanUseTools.ts b/refact-agent/gui/src/hooks/useCanUseTools.ts index e781175bd..9c1b62377 100644 --- a/refact-agent/gui/src/hooks/useCanUseTools.ts +++ b/refact-agent/gui/src/hooks/useCanUseTools.ts @@ -1,13 +1,13 @@ import { useMemo } from "react"; import { useAppSelector } from "./useAppSelector"; -import { useGetToolsQuery } from "./useGetToolsQuery"; +import { useGetToolGroupsQuery } from "./useGetToolGroupsQuery"; import { useGetCapsQuery } from "./useGetCapsQuery"; import { selectModel } from "../features/Chat/Thread/selectors"; import { CodeChatModel } from "../services/refact/models"; export const useCanUseTools = () => { const capsRequest = useGetCapsQuery(); - const toolsRequest = useGetToolsQuery(); + const toolsRequest = useGetToolGroupsQuery(); const chatModel = useAppSelector(selectModel); const loading = useMemo(() => { diff --git a/refact-agent/gui/src/hooks/useGetToolsQuery.ts b/refact-agent/gui/src/hooks/useGetToolGroupsQuery.ts similarity index 64% rename from refact-agent/gui/src/hooks/useGetToolsQuery.ts rename to refact-agent/gui/src/hooks/useGetToolGroupsQuery.ts index 1b783d2b1..c237748e2 100644 --- a/refact-agent/gui/src/hooks/useGetToolsQuery.ts +++ b/refact-agent/gui/src/hooks/useGetToolGroupsQuery.ts @@ -1,13 +1,13 @@ import { toolsApi } from "../services/refact/tools"; import { useHasCaps } from "./useHasCaps"; -export const useGetToolsQuery = () => { +export const useGetToolGroupsQuery = () => { const hasCaps = useHasCaps(); - return toolsApi.useGetToolsQuery(undefined, { skip: !hasCaps }); + return toolsApi.useGetToolGroupsQuery(undefined, { skip: !hasCaps }); }; export const useGetToolsLazyQuery = () => { - return toolsApi.useLazyGetToolsQuery(); + return toolsApi.useLazyGetToolGroupsQuery(); }; export const useCheckForConfirmationMutation = () => { diff --git a/refact-agent/gui/src/hooks/useSendChatRequest.ts b/refact-agent/gui/src/hooks/useSendChatRequest.ts index 0ab2007cb..92e3e534c 100644 --- a/refact-agent/gui/src/hooks/useSendChatRequest.ts +++ b/refact-agent/gui/src/hooks/useSendChatRequest.ts @@ -20,7 +20,7 @@ import { import { useCheckForConfirmationMutation, // useGetToolsLazyQuery, -} from "./useGetToolsQuery"; +} from "./useGetToolGroupsQuery"; import { ChatMessage, ChatMessages, diff --git a/refact-agent/gui/src/services/refact/consts.ts b/refact-agent/gui/src/services/refact/consts.ts index 060fac2bd..7d0553a34 100644 --- a/refact-agent/gui/src/services/refact/consts.ts +++ b/refact-agent/gui/src/services/refact/consts.ts @@ -4,7 +4,7 @@ export const STATISTIC_URL = `/v1/get-dashboard-plots`; export const AT_COMMAND_COMPLETION = "/v1/at-command-completion"; export const AT_COMMAND_PREVIEW = "/v1/at-command-preview"; export const CUSTOM_PROMPTS_URL = "/v1/customization"; -export const AT_TOOLS_AVAILABLE_URL = "/v1/tools"; +export const TOOLS = "/v1/tools"; export const TOOLS_CHECK_CONFIRMATION = "/v1/tools-check-if-confirmation-needed"; export const EDIT_TOOL_DRY_RUN_URL = "/v1/file_edit_tool_dry_run"; diff --git a/refact-agent/gui/src/services/refact/tools.ts b/refact-agent/gui/src/services/refact/tools.ts index 7f5f37b1b..f3f924768 100644 --- a/refact-agent/gui/src/services/refact/tools.ts +++ b/refact-agent/gui/src/services/refact/tools.ts @@ -1,8 +1,8 @@ import { RootState } from "../../app/store"; import { - AT_TOOLS_AVAILABLE_URL, TOOLS_CHECK_CONFIRMATION, EDIT_TOOL_DRY_RUN_URL, + TOOLS, } from "./consts"; import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; import { ChatMessage, DiffChunk, isDiffChunk, ToolCall } from "./types"; @@ -22,12 +22,12 @@ export const toolsApi = createApi({ }, }), endpoints: (builder) => ({ - getTools: builder.query({ + getToolGroups: builder.query({ queryFn: async (_args, api, _extraOptions, baseQuery) => { const getState = api.getState as () => RootState; const state = getState(); const port = state.config.lspPort; - const url = `http://127.0.0.1:${port}${AT_TOOLS_AVAILABLE_URL}`; + const url = `http://127.0.0.1:${port}${TOOLS}`; const result = await baseQuery({ url, credentials: "same-origin", @@ -43,8 +43,42 @@ export const toolsApi = createApi({ }, }; } - const tools = result.data.filter((d) => isToolCommand(d)); - return { data: tools }; + const toolGroups = result.data.filter((d) => + isToolGroup(d), + ) as ToolGroup[]; + return { data: toolGroups }; + }, + }), + updateToolGroups: builder.mutation({ + queryFn: async (newToolGroups, api, _extraOptions, baseQuery) => { + const getState = api.getState as () => RootState; + const state = getState(); + const port = state.config.lspPort; + const url = `http://127.0.0.1:${port}${TOOLS}`; + const result = await baseQuery({ + method: "POST", + url, + body: JSON.stringify({ + tools: newToolGroups, + }), + credentials: "same-origin", + redirect: "follow", + }); + if (result.error) return result; + if (!Array.isArray(result.data)) { + return { + error: { + error: "Invalid response from tools", + data: result.data, + status: "CUSTOM_ERROR", + }, + }; + } + const updatedToolGroups = result.data.filter((d) => + isToolGroup(d), + ) as ToolGroup[]; + + return { data: updatedToolGroups }; }, }), checkForConfirmation: builder.mutation< @@ -122,6 +156,12 @@ export const toolsApi = createApi({ refetchOnMountOrArgChange: true, }); +export type ToolGroupUpdate = { + name: string; + source: ToolSource; + enabled: boolean; +}; + export type ToolGroup = { name: string; category: "integration" | "mcp" | "builtin"; @@ -160,11 +200,6 @@ export type Tool = { enabled: boolean; }; -export type ToolCommand = { - function: ToolSpec; - type: "function"; -}; - export type ToolConfirmationPauseReason = { type: "confirmation" | "denial"; command: string; @@ -183,10 +218,14 @@ export type ToolConfirmationRequest = { messages: ChatMessage[]; }; -function isToolCommand(tool: unknown): tool is ToolCommand { - if (!tool) return false; - if (typeof tool !== "object") return false; - if (!("type" in tool) || !("function" in tool)) return false; +export function isToolGroup(tool: unknown): tool is ToolGroup { + if (!tool || typeof tool !== "object") return false; + const group = tool as ToolGroup; + if (typeof group.name !== "string") return false; + if (typeof group.category !== "string") return false; + if (typeof group.description !== "string") return false; + if (!Array.isArray(group.tools)) return false; + // Optionally, check that every element in tools is a Tool (if you have isTool) return true; } From 840b1fdf4c7f76b900a3ce23d131cd8ad10b7464 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 14 May 2025 20:12:29 +0200 Subject: [PATCH 66/90] feat: toggling all tools within a group at once by button click --- refact-agent/gui/src/app/middleware.ts | 10 +-- .../ChatForm/AgentCapabilities/ToolGroups.tsx | 69 ++++++++++++++++--- refact-agent/gui/src/events/index.ts | 1 - .../src/hooks/useUpdateToolGroupsMutation.ts | 8 +++ refact-agent/gui/src/services/refact/tools.ts | 17 +++-- 5 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 refact-agent/gui/src/hooks/useUpdateToolGroupsMutation.ts diff --git a/refact-agent/gui/src/app/middleware.ts b/refact-agent/gui/src/app/middleware.ts index 8d8031cc0..4c6243427 100644 --- a/refact-agent/gui/src/app/middleware.ts +++ b/refact-agent/gui/src/app/middleware.ts @@ -115,7 +115,7 @@ startListening({ listenerApi.dispatch(setIsAuthError(isAuthError)); } if ( - toolsApi.endpoints.getTools.matchRejected(action) && + toolsApi.endpoints.getToolGroups.matchRejected(action) && !action.meta.condition ) { const errorStatus = action.payload?.status; @@ -124,7 +124,7 @@ startListening({ ? AUTH_ERROR_MESSAGE : isDetailMessage(action.payload?.data) ? action.payload.data.detail - : `fetching tools from lsp`; + : `fetching tool groups from lsp`; listenerApi.dispatch(setError(message)); listenerApi.dispatch(setIsAuthError(isAuthError)); @@ -379,10 +379,10 @@ startListening({ const state = listenerApi.getState(); // TBD: should the endpoint need tools? - const toolsRequest = listenerApi.dispatch( - toolsApi.endpoints.getTools.initiate(undefined), + const toolGroupsRequest = listenerApi.dispatch( + toolsApi.endpoints.getToolGroups.initiate(undefined), ); - toolsRequest.unsubscribe(); + toolGroupsRequest.unsubscribe(); // const toolResult = await toolsRequest.unwrap(); // TODO: set mode to configure ? or infer it later // TODO: create a dedicated thunk for this. diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx index 02c2bb3ec..0aa65f34d 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -8,23 +8,70 @@ import { Text, } from "@radix-ui/themes"; import { AnimatePresence, motion } from "framer-motion"; -import React, { useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { ChevronLeftIcon, QuestionMarkCircledIcon, } from "@radix-ui/react-icons"; import { useGetToolGroupsQuery } from "../../../hooks"; -import { ToolGroup as ToolGroupType } from "../../../services/refact"; +import { + ToolGroup as ToolGroupType, + ToolGroupUpdate, +} from "../../../services/refact"; import { ScrollArea } from "../../ScrollArea"; import { ToolGroup } from "./ToolGroup"; +import { useUpdateToolGroupsMutation } from "../../../hooks/useUpdateToolGroupsMutation"; export const ToolGroups: React.FC = () => { const { data: toolsGroups, isLoading, isSuccess } = useGetToolGroupsQuery(); + const { mutationTrigger: updateToolGroups } = useUpdateToolGroupsMutation(); + const [selectedToolGroup, setSelectedToolGroup] = useState(null); + const someToolsEnabled = useMemo(() => { + if (!selectedToolGroup) return false; + return selectedToolGroup.tools.some((tool) => tool.enabled); + }, [selectedToolGroup]); + + const handleToggleToolGroup = useCallback( + (toolGroup: ToolGroupType) => { + const updatedTools = toolGroup.tools.map((tool) => ({ + ...tool, + enabled: someToolsEnabled ? false : true, + })); + + const updatedGroup = { ...toolGroup, tools: updatedTools }; + + const dataToSend: ToolGroupUpdate[] = updatedTools.map((tool) => ({ + enabled: tool.enabled, + source: tool.spec.source, + name: tool.spec.name, + })); + console.log(`[DEBUG]: updating data: `, dataToSend); + + updateToolGroups(dataToSend) + .then((result) => { + console.log(`[DEBUG]: result: `, result); + if (result.data) { + setSelectedToolGroup((prev) => { + console.log( + "[DEBUG]: Previous group: ", + prev, + "new group: ", + updatedGroup, + ); + return updatedGroup; + }); + } + }) + .catch(alert); + }, + [updateToolGroups, someToolsEnabled], + ); + if (isLoading || !isSuccess) return ; return ( @@ -86,8 +133,14 @@ export const ToolGroups: React.FC = () => { {selectedToolGroup.name} - { const ToolGroupsSkeleton: React.FC = () => { return ( - - - Manage Tool Groups - - + + Manage Tool Groups + {[1, 2].map((idx) => ( diff --git a/refact-agent/gui/src/events/index.ts b/refact-agent/gui/src/events/index.ts index d06171346..a8a100c6e 100644 --- a/refact-agent/gui/src/events/index.ts +++ b/refact-agent/gui/src/events/index.ts @@ -36,7 +36,6 @@ export { export type { TextDocToolCall } from "../components/Tools/types"; export type { - ToolCommand, CustomPromptsResponse, CapsResponse, UserMessage, diff --git a/refact-agent/gui/src/hooks/useUpdateToolGroupsMutation.ts b/refact-agent/gui/src/hooks/useUpdateToolGroupsMutation.ts new file mode 100644 index 000000000..b0b5832a3 --- /dev/null +++ b/refact-agent/gui/src/hooks/useUpdateToolGroupsMutation.ts @@ -0,0 +1,8 @@ +import { toolsApi } from "../services/refact"; + +export function useUpdateToolGroupsMutation() { + const [mutationTrigger, mutationResult] = + toolsApi.useUpdateToolGroupsMutation(); + + return { mutationTrigger, mutationResult }; +} diff --git a/refact-agent/gui/src/services/refact/tools.ts b/refact-agent/gui/src/services/refact/tools.ts index f3f924768..71073b517 100644 --- a/refact-agent/gui/src/services/refact/tools.ts +++ b/refact-agent/gui/src/services/refact/tools.ts @@ -5,7 +5,13 @@ import { TOOLS, } from "./consts"; import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; -import { ChatMessage, DiffChunk, isDiffChunk, ToolCall } from "./types"; +import { + ChatMessage, + DiffChunk, + isDiffChunk, + isSuccess, + ToolCall, +} from "./types"; import { formatMessagesForLsp } from "../../features/Chat/Thread/utils"; export const toolsApi = createApi({ @@ -49,7 +55,7 @@ export const toolsApi = createApi({ return { data: toolGroups }; }, }), - updateToolGroups: builder.mutation({ + updateToolGroups: builder.mutation<{ success: true }, ToolGroupUpdate[]>({ queryFn: async (newToolGroups, api, _extraOptions, baseQuery) => { const getState = api.getState as () => RootState; const state = getState(); @@ -65,7 +71,7 @@ export const toolsApi = createApi({ redirect: "follow", }); if (result.error) return result; - if (!Array.isArray(result.data)) { + if (!isSuccess(result.data)) { return { error: { error: "Invalid response from tools", @@ -74,11 +80,8 @@ export const toolsApi = createApi({ }, }; } - const updatedToolGroups = result.data.filter((d) => - isToolGroup(d), - ) as ToolGroup[]; - return { data: updatedToolGroups }; + return { data: result.data }; }, }), checkForConfirmation: builder.mutation< From 1a0fd79c419197b1d33e7c09576008710a5e56c4 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 14 May 2025 20:13:18 +0200 Subject: [PATCH 67/90] chore: fixed formatting --- .../src/components/ChatForm/AgentCapabilities/ToolGroups.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx index 0aa65f34d..d88645597 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -8,7 +8,7 @@ import { Text, } from "@radix-ui/themes"; import { AnimatePresence, motion } from "framer-motion"; -import React, { useCallback, useEffect, useMemo, useState } from "react"; +import React, { useCallback, useMemo, useState } from "react"; import { ChevronLeftIcon, QuestionMarkCircledIcon, @@ -172,7 +172,7 @@ export const ToolGroups: React.FC = () => { - + ))} From 8b8f895aaf7f924f4bc78fb9f578f31b6a6688b3 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 15 May 2025 23:29:00 +0200 Subject: [PATCH 68/90] chore: fixed linting & types --- refact-agent/gui/src/__fixtures__/msw.ts | 4 ++-- .../components/ChatForm/AgentCapabilities/ToolGroups.tsx | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/refact-agent/gui/src/__fixtures__/msw.ts b/refact-agent/gui/src/__fixtures__/msw.ts index 91997965f..f773b9362 100644 --- a/refact-agent/gui/src/__fixtures__/msw.ts +++ b/refact-agent/gui/src/__fixtures__/msw.ts @@ -3,7 +3,7 @@ import { EMPTY_CAPS_RESPONSE, STUB_CAPS_RESPONSE } from "./caps"; import { SYSTEM_PROMPTS } from "./prompts"; import { STUB_LINKS_FOR_CHAT_RESPONSE } from "./chat_links_response"; import { - AT_TOOLS_AVAILABLE_URL, + TOOLS, CHAT_LINKS_URL, KNOWLEDGE_CREATE_URL, } from "../services/refact/consts"; @@ -120,7 +120,7 @@ export const noChatLinks: HttpHandler = http.post( ); export const goodTools: HttpHandler = http.get( - `http://127.0.0.1:8001${AT_TOOLS_AVAILABLE_URL}`, + `http://127.0.0.1:8001${TOOLS}`, () => { return HttpResponse.json(STUB_TOOL_RESPONSE); }, diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx index d88645597..311cd36e7 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -23,6 +23,7 @@ import { import { ScrollArea } from "../../ScrollArea"; import { ToolGroup } from "./ToolGroup"; import { useUpdateToolGroupsMutation } from "../../../hooks/useUpdateToolGroupsMutation"; +import { debugApp } from "../../../debugConfig"; export const ToolGroups: React.FC = () => { const { data: toolsGroups, isLoading, isSuccess } = useGetToolGroupsQuery(); @@ -50,14 +51,14 @@ export const ToolGroups: React.FC = () => { source: tool.spec.source, name: tool.spec.name, })); - console.log(`[DEBUG]: updating data: `, dataToSend); + debugApp(`[DEBUG]: updating data: `, dataToSend); updateToolGroups(dataToSend) .then((result) => { - console.log(`[DEBUG]: result: `, result); + debugApp(`[DEBUG]: result: `, result); if (result.data) { setSelectedToolGroup((prev) => { - console.log( + debugApp( "[DEBUG]: Previous group: ", prev, "new group: ", From af4b685d2b3a69aa580781fc16be911d7e2286fa Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 16 May 2025 00:18:21 +0200 Subject: [PATCH 69/90] feat: ability to disable individual tools in the UI --- .../ChatForm/AgentCapabilities/ToolGroups.tsx | 152 ++++++++++++++---- refact-agent/gui/src/services/refact/tools.ts | 2 + 2 files changed, 127 insertions(+), 27 deletions(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx index 311cd36e7..658582de8 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -8,16 +8,19 @@ import { Text, } from "@radix-ui/themes"; import { AnimatePresence, motion } from "framer-motion"; -import React, { useCallback, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { ChevronLeftIcon, QuestionMarkCircledIcon, } from "@radix-ui/react-icons"; -import { useGetToolGroupsQuery } from "../../../hooks"; +import { useAppDispatch, useGetToolGroupsQuery } from "../../../hooks"; import { + Tool, ToolGroup as ToolGroupType, ToolGroupUpdate, + toolsApi, + ToolSpec, } from "../../../services/refact"; import { ScrollArea } from "../../ScrollArea"; @@ -26,26 +29,35 @@ import { useUpdateToolGroupsMutation } from "../../../hooks/useUpdateToolGroupsM import { debugApp } from "../../../debugConfig"; export const ToolGroups: React.FC = () => { + const dispatch = useAppDispatch(); const { data: toolsGroups, isLoading, isSuccess } = useGetToolGroupsQuery(); const { mutationTrigger: updateToolGroups } = useUpdateToolGroupsMutation(); const [selectedToolGroup, setSelectedToolGroup] = useState(null); + const [selectedToolGroupTools, setSelectedToolGroupTools] = useState< + Tool[] | null + >(null); const someToolsEnabled = useMemo(() => { if (!selectedToolGroup) return false; return selectedToolGroup.tools.some((tool) => tool.enabled); }, [selectedToolGroup]); - const handleToggleToolGroup = useCallback( - (toolGroup: ToolGroupType) => { - const updatedTools = toolGroup.tools.map((tool) => ({ - ...tool, - enabled: someToolsEnabled ? false : true, - })); - - const updatedGroup = { ...toolGroup, tools: updatedTools }; + useEffect(() => { + if (selectedToolGroup) { + setSelectedToolGroupTools(selectedToolGroup.tools); + } + }, [selectedToolGroup]); + const handleUpdateToolGroups = useCallback( + ({ + updatedTools, + updatedGroup, + }: { + updatedTools: { enabled: boolean; spec: ToolSpec }[]; + updatedGroup: ToolGroupType; + }) => { const dataToSend: ToolGroupUpdate[] = updatedTools.map((tool) => ({ enabled: tool.enabled, source: tool.spec.source, @@ -57,6 +69,22 @@ export const ToolGroups: React.FC = () => { .then((result) => { debugApp(`[DEBUG]: result: `, result); if (result.data) { + // it means, individual tool update + debugApp(`[DEBUG]: updating individual tool: `, updatedTools[0]); + if (selectedToolGroupTools && updatedTools.length === 1) { + setSelectedToolGroupTools((prev) => { + const tool = updatedTools[0]; + return prev + ? prev.map((t) => { + if (t.spec.name === tool.spec.name) { + return { ...t, enabled: tool.enabled }; + } + return t; + }) + : selectedToolGroupTools; + }); + return; + } setSelectedToolGroup((prev) => { debugApp( "[DEBUG]: Previous group: ", @@ -70,7 +98,65 @@ export const ToolGroups: React.FC = () => { }) .catch(alert); }, - [updateToolGroups, someToolsEnabled], + [updateToolGroups, setSelectedToolGroupTools, selectedToolGroupTools], + ); + + const handleToggleToolsInToolGroup = useCallback( + (toolGroup: ToolGroupType) => { + const updatedTools = toolGroup.tools.map((tool) => ({ + ...tool, + enabled: someToolsEnabled ? false : true, + })); + + const updatedGroup = { ...toolGroup, tools: updatedTools }; + + handleUpdateToolGroups({ + updatedTools, + updatedGroup, + }); + }, + [handleUpdateToolGroups, someToolsEnabled], + ); + + const handleResetSelectedGroup = useCallback(() => { + dispatch(toolsApi.util.invalidateTags(["TOOL_GROUPS"])); + setSelectedToolGroup(null); + }, [dispatch]); + + const handleToggleSpecificToolFromToolGroup = useCallback( + ({ + tool, + parentGroup, + togglingTo, + }: { + tool: ToolGroupType["tools"][number]; + parentGroup: ToolGroupType; + togglingTo: boolean; + }) => { + const updatedTools: Tool[] = [ + { + enabled: togglingTo, + spec: tool.spec, + }, + ]; + + const updatedGroup = { + ...parentGroup, + tools: parentGroup.tools.map((t) => { + if (t.spec.name === tool.spec.name) { + return { ...tool }; + } + + return { ...t }; + }), + }; + + handleUpdateToolGroups({ + updatedTools, + updatedGroup, + }); + }, + [handleUpdateToolGroups], ); if (isLoading || !isSuccess) return ; @@ -124,7 +210,7 @@ export const ToolGroups: React.FC = () => { - - {selectedToolGroup.name} - - - - - - {selectedToolGroupTools?.map((tool) => ( - - - - 🔨 {tool.spec.display_name} - - {tool.spec.description.trim() !== "" && ( - - - - - - - {tool.spec.description} - - - - )} - - - handleToggleSpecificToolFromToolGroup({ - tool, - parentGroup: selectedToolGroup, - togglingTo: newState, - }) - } - /> - - ))} - - - - + <> + {selectedToolGroupTools && ( + + )} + )} diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx new file mode 100644 index 000000000..eb6b560fd --- /dev/null +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx @@ -0,0 +1,120 @@ +import React from "react"; +import { motion } from "framer-motion"; +import { + Button, + Flex, + Heading, + HoverCard, + Switch, + Text, +} from "@radix-ui/themes"; +import { + ChevronLeftIcon, + QuestionMarkCircledIcon, +} from "@radix-ui/react-icons"; + +import { ScrollArea } from "../../ScrollArea"; + +import { ToolGroup } from "../../../services/refact"; + +export type ToolsListProps = { + group: ToolGroup; + tools: ToolGroup["tools"]; + onBack: () => void; + onToggleAll: (group: ToolGroup) => void; + onToggle: ({ + tool, + parentGroup, + togglingTo, + }: { + tool: ToolGroup["tools"][number]; + parentGroup: ToolGroup; + togglingTo: boolean; + }) => void; + someEnabled: boolean; +}; + +export const ToolsList: React.FC = ({ + group, + tools, + onToggle, + onBack, + onToggleAll, + someEnabled, +}) => { + return ( + + + + + + {group.name} + + + + + + {tools.map((tool) => ( + + + + 🔨 {tool.spec.display_name} + + {tool.spec.description.trim() !== "" && ( + + + + + + + {tool.spec.description} + + + + )} + + + onToggle({ + tool, + parentGroup: group, + togglingTo: newState, + }) + } + /> + + ))} + + + + + ); +}; diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/useToolGroups.ts b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/useToolGroups.ts new file mode 100644 index 000000000..5aea4d2f7 --- /dev/null +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/useToolGroups.ts @@ -0,0 +1,161 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useAppDispatch } from "../../../hooks"; +import { useUpdateToolGroupsMutation } from "../../../hooks/useUpdateToolGroupsMutation"; +import { + Tool, + ToolGroup, + ToolGroupUpdate, + toolsApi, + ToolSpec, +} from "../../../services/refact"; +import { debugApp } from "../../../debugConfig"; + +export function useToolGroups() { + const dispatch = useAppDispatch(); + const { mutationTrigger: updateToolGroups } = useUpdateToolGroupsMutation(); + + const [selectedToolGroup, setSelectedToolGroup] = useState( + null, + ); + const [selectedToolGroupTools, setSelectedToolGroupTools] = useState< + Tool[] | null + >(null); + + const selectToolGroup = useCallback( + (group: ToolGroup | null) => { + setSelectedToolGroup(group); + }, + [setSelectedToolGroup], + ); + + const someToolsEnabled = useMemo(() => { + if (!selectedToolGroupTools) return false; + return selectedToolGroupTools.some((tool) => tool.enabled); + }, [selectedToolGroupTools]); + + const handleUpdateToolGroups = useCallback( + ({ + updatedTools, + updatedGroup, + }: { + updatedTools: { enabled: boolean; spec: ToolSpec }[]; + updatedGroup: ToolGroup; + }) => { + const dataToSend: ToolGroupUpdate[] = updatedTools.map((tool) => ({ + enabled: tool.enabled, + source: tool.spec.source, + name: tool.spec.name, + })); + debugApp(`[DEBUG]: updating data: `, dataToSend); + + updateToolGroups(dataToSend) + .then((result) => { + debugApp(`[DEBUG]: result: `, result); + if (result.data) { + // TODO: reduce complexity + // it means, individual tool update + debugApp(`[DEBUG]: updating individual tool: `, updatedTools[0]); + if (selectedToolGroupTools && updatedTools.length === 1) { + setSelectedToolGroupTools((prev) => { + const tool = updatedTools[0]; + return prev + ? prev.map((t) => { + if (t.spec.name === tool.spec.name) { + return { ...t, enabled: tool.enabled }; + } + return t; + }) + : selectedToolGroupTools; + }); + return; + } + setSelectedToolGroup((prev) => { + debugApp( + "[DEBUG]: Previous group: ", + prev, + "new group: ", + updatedGroup, + ); + return updatedGroup; + }); + } + }) + .catch(alert); + }, + [updateToolGroups, setSelectedToolGroupTools, selectedToolGroupTools], + ); + + const toggleAllTools = useCallback( + (toolGroup: ToolGroup) => { + const updatedTools = toolGroup.tools.map((tool) => ({ + ...tool, + enabled: someToolsEnabled ? false : true, + })); + + const updatedGroup = { ...toolGroup, tools: updatedTools }; + + handleUpdateToolGroups({ + updatedTools, + updatedGroup, + }); + }, + [handleUpdateToolGroups, someToolsEnabled], + ); + + const toggleTool = useCallback( + ({ + tool, + parentGroup, + togglingTo, + }: { + tool: ToolGroup["tools"][number]; + parentGroup: ToolGroup; + togglingTo: boolean; + }) => { + const updatedTools: Tool[] = [ + { + enabled: togglingTo, + spec: tool.spec, + }, + ]; + + const updatedGroup = { + ...parentGroup, + tools: parentGroup.tools.map((t) => { + if (t.spec.name === tool.spec.name) { + return { ...tool }; + } + + return { ...t }; + }), + }; + + handleUpdateToolGroups({ + updatedTools, + updatedGroup, + }); + }, + [handleUpdateToolGroups], + ); + + const resetSelection = useCallback(() => { + dispatch(toolsApi.util.invalidateTags(["TOOL_GROUPS"])); + setSelectedToolGroup(null); + }, [dispatch]); + + useEffect(() => { + if (selectedToolGroup) { + setSelectedToolGroupTools(selectedToolGroup.tools); + } + }, [selectedToolGroup]); + + return { + toggleTool, + toggleAllTools, + resetSelection, + selectToolGroup, + selectedToolGroup, + selectedToolGroupTools, + someToolsEnabled, + }; +} From 9165bda9cdde682858363ddb27f914d875553a35 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 16 May 2025 00:45:57 +0200 Subject: [PATCH 71/90] chore: fixed formatting --- .../AgentCapabilities/ToolGroupList.tsx | 92 +++---- .../ChatForm/AgentCapabilities/ToolsList.tsx | 240 +++++++++--------- 2 files changed, 166 insertions(+), 166 deletions(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroupList.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroupList.tsx index 442ea721d..89bfe6b4e 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroupList.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroupList.tsx @@ -1,46 +1,46 @@ -import React from "react"; -import { motion } from "framer-motion"; -import { Flex } from "@radix-ui/themes"; - -import { ToolGroup } from "./ToolGroup"; -import { ScrollArea } from "../../ScrollArea"; - -import { ToolGroup as ToolGroupType } from "../../../services/refact"; - -export type ToolGroupListProps = { - groups: ToolGroupType[]; - onSelect: (group: ToolGroupType | null) => void; -}; - -export const ToolGroupList: React.FC = ({ - groups, - onSelect, -}) => { - return ( - - - - {groups.map((toolGroup) => ( - - ))} - - - - ); -}; +import React from "react"; +import { motion } from "framer-motion"; +import { Flex } from "@radix-ui/themes"; + +import { ToolGroup } from "./ToolGroup"; +import { ScrollArea } from "../../ScrollArea"; + +import { ToolGroup as ToolGroupType } from "../../../services/refact"; + +export type ToolGroupListProps = { + groups: ToolGroupType[]; + onSelect: (group: ToolGroupType | null) => void; +}; + +export const ToolGroupList: React.FC = ({ + groups, + onSelect, +}) => { + return ( + + + + {groups.map((toolGroup) => ( + + ))} + + + + ); +}; diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx index eb6b560fd..e5bc3e188 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx @@ -1,120 +1,120 @@ -import React from "react"; -import { motion } from "framer-motion"; -import { - Button, - Flex, - Heading, - HoverCard, - Switch, - Text, -} from "@radix-ui/themes"; -import { - ChevronLeftIcon, - QuestionMarkCircledIcon, -} from "@radix-ui/react-icons"; - -import { ScrollArea } from "../../ScrollArea"; - -import { ToolGroup } from "../../../services/refact"; - -export type ToolsListProps = { - group: ToolGroup; - tools: ToolGroup["tools"]; - onBack: () => void; - onToggleAll: (group: ToolGroup) => void; - onToggle: ({ - tool, - parentGroup, - togglingTo, - }: { - tool: ToolGroup["tools"][number]; - parentGroup: ToolGroup; - togglingTo: boolean; - }) => void; - someEnabled: boolean; -}; - -export const ToolsList: React.FC = ({ - group, - tools, - onToggle, - onBack, - onToggleAll, - someEnabled, -}) => { - return ( - - - - - - {group.name} - - - - - - {tools.map((tool) => ( - - - - 🔨 {tool.spec.display_name} - - {tool.spec.description.trim() !== "" && ( - - - - - - - {tool.spec.description} - - - - )} - - - onToggle({ - tool, - parentGroup: group, - togglingTo: newState, - }) - } - /> - - ))} - - - - - ); -}; +import React from "react"; +import { motion } from "framer-motion"; +import { + Button, + Flex, + Heading, + HoverCard, + Switch, + Text, +} from "@radix-ui/themes"; +import { + ChevronLeftIcon, + QuestionMarkCircledIcon, +} from "@radix-ui/react-icons"; + +import { ScrollArea } from "../../ScrollArea"; + +import { ToolGroup } from "../../../services/refact"; + +export type ToolsListProps = { + group: ToolGroup; + tools: ToolGroup["tools"]; + onBack: () => void; + onToggleAll: (group: ToolGroup) => void; + onToggle: ({ + tool, + parentGroup, + togglingTo, + }: { + tool: ToolGroup["tools"][number]; + parentGroup: ToolGroup; + togglingTo: boolean; + }) => void; + someEnabled: boolean; +}; + +export const ToolsList: React.FC = ({ + group, + tools, + onToggle, + onBack, + onToggleAll, + someEnabled, +}) => { + return ( + + + + + + {group.name} + + + + + + {tools.map((tool) => ( + + + + 🔨 {tool.spec.display_name} + + {tool.spec.description.trim() !== "" && ( + + + + + + + {tool.spec.description} + + + + )} + + + onToggle({ + tool, + parentGroup: group, + togglingTo: newState, + }) + } + /> + + ))} + + + + + ); +}; From ad62a6782781cbe3f08cb57eba4104cf8f9e3414 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 16 May 2025 00:52:54 +0200 Subject: [PATCH 72/90] chore: removed useless comments --- refact-agent/gui/src/features/Chat/Thread/actions.ts | 10 +--------- refact-agent/gui/src/services/refact/chat.ts | 3 --- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/refact-agent/gui/src/features/Chat/Thread/actions.ts b/refact-agent/gui/src/features/Chat/Thread/actions.ts index ea926f6fb..e0a7ecf9d 100644 --- a/refact-agent/gui/src/features/Chat/Thread/actions.ts +++ b/refact-agent/gui/src/features/Chat/Thread/actions.ts @@ -305,25 +305,18 @@ function checkForToolLoop(message: ChatMessages): boolean { } // TODO: add props for config chat -// export function chatModeToLspMode(mode?: ToolUse) { -// if (mode === "agent") return "AGENT"; -// if (mode === "quick") return "NO_TOOLS"; -// return "EXPLORE"; -// } - export const chatAskQuestionThunk = createAppAsyncThunk< unknown, { messages: ChatMessages; chatId: string; - // tools: ToolCommand[] | null; checkpointsEnabled?: boolean; mode?: LspChatMode; // used once for actions // TODO: make a separate function for this... and it'll need to be saved. } >( "chatThread/sendChat", - ({ messages, chatId, /*tools,*/ mode, checkpointsEnabled }, thunkAPI) => { + ({ messages, chatId, mode, checkpointsEnabled }, thunkAPI) => { const state = thunkAPI.getState(); const thread = @@ -346,7 +339,6 @@ export const chatAskQuestionThunk = createAppAsyncThunk< messages: messagesForLsp, last_user_message_id: maybeLastUserMessageId, model: state.chat.thread.model, - // tools, stream: true, abortSignal: thunkAPI.signal, increase_max_tokens: increaseMaxTokens, diff --git a/refact-agent/gui/src/services/refact/chat.ts b/refact-agent/gui/src/services/refact/chat.ts index e6b1751a5..c07316869 100644 --- a/refact-agent/gui/src/services/refact/chat.ts +++ b/refact-agent/gui/src/services/refact/chat.ts @@ -59,7 +59,6 @@ type SendChatArgs = { takeNote?: boolean; onlyDeterministicMessages?: boolean; chatId?: string; - // tools: ToolCommand[] | null; port?: number; apiKey?: string | null; // isConfig?: boolean; @@ -150,7 +149,6 @@ export async function sendChat({ // takeNote = false, onlyDeterministicMessages: only_deterministic_messages, chatId: chat_id, - // tools, port = 8001, apiKey, checkpointsEnabled = true, @@ -175,7 +173,6 @@ export async function sendChat({ messages, model: model, stream, - // tools, only_deterministic_messages, checkpoints_enabled: checkpointsEnabled, // chat_id, From 456447410efa2cfbd366415bbb25e8a08782dd11 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 16 May 2025 11:02:25 +0200 Subject: [PATCH 73/90] fix: removed alert on catch of tool groups update & removed excessive logs --- .../AgentCapabilities/useToolGroups.ts | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/useToolGroups.ts b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/useToolGroups.ts index 5aea4d2f7..8a7cff505 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/useToolGroups.ts +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/useToolGroups.ts @@ -8,7 +8,6 @@ import { toolsApi, ToolSpec, } from "../../../services/refact"; -import { debugApp } from "../../../debugConfig"; export function useToolGroups() { const dispatch = useAppDispatch(); @@ -46,15 +45,12 @@ export function useToolGroups() { source: tool.spec.source, name: tool.spec.name, })); - debugApp(`[DEBUG]: updating data: `, dataToSend); updateToolGroups(dataToSend) .then((result) => { - debugApp(`[DEBUG]: result: `, result); if (result.data) { // TODO: reduce complexity // it means, individual tool update - debugApp(`[DEBUG]: updating individual tool: `, updatedTools[0]); if (selectedToolGroupTools && updatedTools.length === 1) { setSelectedToolGroupTools((prev) => { const tool = updatedTools[0]; @@ -69,18 +65,13 @@ export function useToolGroups() { }); return; } - setSelectedToolGroup((prev) => { - debugApp( - "[DEBUG]: Previous group: ", - prev, - "new group: ", - updatedGroup, - ); - return updatedGroup; - }); + setSelectedToolGroup(updatedGroup); } }) - .catch(alert); + .catch((error: unknown) => { + // eslint-disable-next-line no-console + console.log(error); + }); }, [updateToolGroups, setSelectedToolGroupTools, selectedToolGroupTools], ); From 393d3210d657c5aaf21f00f5e7addb06e95f0c12 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 16 May 2025 13:26:05 +0200 Subject: [PATCH 74/90] refactor: removed old tools handler's code --- refact-agent/gui/src/app/middleware.ts | 8 ------- .../gui/src/features/Chat/Thread/actions.ts | 16 ------------- .../gui/src/hooks/useSendChatRequest.ts | 24 +------------------ 3 files changed, 1 insertion(+), 47 deletions(-) diff --git a/refact-agent/gui/src/app/middleware.ts b/refact-agent/gui/src/app/middleware.ts index 4c6243427..e8a4b8fe3 100644 --- a/refact-agent/gui/src/app/middleware.ts +++ b/refact-agent/gui/src/app/middleware.ts @@ -377,20 +377,12 @@ startListening({ actionCreator: newIntegrationChat, effect: async (_action, listenerApi) => { const state = listenerApi.getState(); - - // TBD: should the endpoint need tools? - const toolGroupsRequest = listenerApi.dispatch( - toolsApi.endpoints.getToolGroups.initiate(undefined), - ); - toolGroupsRequest.unsubscribe(); - // const toolResult = await toolsRequest.unwrap(); // TODO: set mode to configure ? or infer it later // TODO: create a dedicated thunk for this. await listenerApi.dispatch( chatAskQuestionThunk({ messages: state.chat.thread.messages, chatId: state.chat.thread.id, - // tools: toolResult, }), ); }, diff --git a/refact-agent/gui/src/features/Chat/Thread/actions.ts b/refact-agent/gui/src/features/Chat/Thread/actions.ts index e0a7ecf9d..b7c1e98cb 100644 --- a/refact-agent/gui/src/features/Chat/Thread/actions.ts +++ b/refact-agent/gui/src/features/Chat/Thread/actions.ts @@ -402,7 +402,6 @@ export const sendCurrentChatToLspAfterToolCallUpdate = createAppAsyncThunk< "chatThread/sendCurrentChatToLspAfterToolCallUpdate", async ({ chatId, toolCallId }, thunkApi) => { const state = thunkApi.getState(); - // const toolUse = state.chat.thread.tool_use; if (state.chat.thread.id !== chatId) return; if ( state.chat.streaming || @@ -423,25 +422,10 @@ export const sendCurrentChatToLspAfterToolCallUpdate = createAppAsyncThunk< if (!toolUseInThisSet) return; thunkApi.dispatch(setIsWaitingForResponse(true)); - // duplicate in sendChat - // let tools = await thunkApi - // .dispatch(toolsApi.endpoints.getTools.initiate(undefined)) - // .unwrap(); - - // if (toolUse === "quick") { - // tools = []; - // } else if (toolUse === "explore") { - // tools = tools.filter((t) => !t.function.agentic); - // } - // tools = tools.map((t) => { - // const { agentic: _, ...remaining } = t.function; - // return { ...t, function: { ...remaining } }; - // }); return thunkApi.dispatch( chatAskQuestionThunk({ messages: state.chat.thread.messages, - // tools, chatId, mode: state.chat.thread.mode, checkpointsEnabled: state.chat.checkpoints_enabled, diff --git a/refact-agent/gui/src/hooks/useSendChatRequest.ts b/refact-agent/gui/src/hooks/useSendChatRequest.ts index 92e3e534c..aaed6c8f6 100644 --- a/refact-agent/gui/src/hooks/useSendChatRequest.ts +++ b/refact-agent/gui/src/hooks/useSendChatRequest.ts @@ -17,10 +17,7 @@ import { selectThreadMode, selectThreadToolUse, } from "../features/Chat/Thread/selectors"; -import { - useCheckForConfirmationMutation, - // useGetToolsLazyQuery, -} from "./useGetToolGroupsQuery"; +import { useCheckForConfirmationMutation } from "./useGetToolGroupsQuery"; import { ChatMessage, ChatMessages, @@ -129,23 +126,6 @@ export const useSendChatRequest = () => { const sendMessages = useCallback( async (messages: ChatMessages, maybeMode?: LspChatMode) => { dispatch(setIsWaitingForResponse(true)); - // TODO: should be safe to remove - - // let tools = await triggerGetTools(undefined).unwrap(); - // // TODO: save tool use to state.chat - // // if (toolUse && isToolUse(toolUse)) { - // // dispatch(setToolUse(toolUse)); - // // } - // if (toolUse === "quick") { - // tools = []; - // } else if (toolUse === "explore") { - // tools = tools.filter((t) => !t.function.agentic); - // } - // tools = tools.map((t) => { - // const { agentic: _, ...remaining } = t.function; - // return { ...t, function: { ...remaining } }; - // }); - const lastMessage = messages.slice(-1)[0]; if ( @@ -186,7 +166,6 @@ export const useSendChatRequest = () => { const action = chatAskQuestionThunk({ messages, - // tools checkpointsEnabled, chatId, mode, @@ -196,7 +175,6 @@ export const useSendChatRequest = () => { abortControllers.addAbortController(chatId, dispatchedAction.abort); }, [ - // triggerGetTools, toolUse, isWaiting, dispatch, From 3252783fd76434e06c6ab9b5cdf265c1f7e495b7 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 16 May 2025 14:43:03 +0200 Subject: [PATCH 75/90] fix: preserving keys for tool groups to repair animation --- .../components/ChatForm/AgentCapabilities/ToolGroups.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx index 5abf72c0f..db815c105 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroups.tsx @@ -29,11 +29,16 @@ export const ToolGroups: React.FC = () => { {!selectedToolGroup ? ( - + ) : ( <> {selectedToolGroupTools && ( Date: Fri, 16 May 2025 15:50:03 +0200 Subject: [PATCH 76/90] change builtin groups --- .../engine/src/http/routers/v1/at_tools.rs | 2 + .../engine/src/tools/tools_description.rs | 1 + refact-agent/engine/src/tools/tools_list.rs | 85 ++++++++++++++----- 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index 23b54adc2..f548b860e 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -77,6 +77,7 @@ pub struct ToolGroupResponse { pub name: String, pub description: String, pub category: ToolGroupCategory, + pub allow_per_tool_toggle: bool, pub tools: Vec, } @@ -102,6 +103,7 @@ pub async fn handle_v1_get_tools( name: tool_group.name, description: tool_group.description, category: tool_group.category, + allow_per_tool_toggle: tool_group.allow_per_tool_toggle, tools, }) }).collect(); diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 7a99af36f..b753354ba 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -39,6 +39,7 @@ pub struct ToolGroup { pub description: String, pub category: ToolGroupCategory, pub tools: Vec>, + pub allow_per_tool_toggle: bool, } #[derive(Clone, Serialize, Deserialize, Debug)] diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index e16a59229..df1944d21 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -79,32 +79,73 @@ async fn get_builtin_tools( let config_dir = gcx.read().await.config_dir.clone(); let config_path = config_dir.join("builtin_tools.yaml").to_string_lossy().to_string(); - let tools = vec![ - Box::new(crate::tools::tool_ast_definition::ToolAstDefinition{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_ast_reference::ToolAstReference{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_tree::ToolTree{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::file_edit::tool_create_textdoc::ToolCreateTextDoc{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::file_edit::tool_update_textdoc::ToolUpdateTextDoc {config_path: config_path.clone()}) as Box, - Box::new(crate::tools::file_edit::tool_update_textdoc_regex::ToolUpdateTextDocRegex {config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_web::ToolWeb{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_cat::ToolCat{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_rm::ToolRm{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_mv::ToolMv{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_strategic_planning::ToolStrategicPlanning{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_regex_search::ToolRegexSearch{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_knowledge::ToolGetKnowledge{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_create_knowledge::ToolCreateKnowledge{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_create_memory_bank::ToolCreateMemoryBank{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_search::ToolSearch{config_path: config_path.clone()}) as Box, - Box::new(crate::tools::tool_locate_search::ToolLocateSearch{config_path: config_path.clone()}) as Box, + let mut codebase_search_tools: Vec> = vec![ + Box::new(crate::tools::tool_ast_definition::ToolAstDefinition{config_path: config_path.clone()}), + Box::new(crate::tools::tool_ast_reference::ToolAstReference{config_path: config_path.clone()}), + Box::new(crate::tools::tool_tree::ToolTree{config_path: config_path.clone()}), + Box::new(crate::tools::tool_cat::ToolCat{config_path: config_path.clone()}), + Box::new(crate::tools::tool_regex_search::ToolRegexSearch{config_path: config_path.clone()}), + Box::new(crate::tools::tool_search::ToolSearch{config_path: config_path.clone()}), + Box::new(crate::tools::tool_locate_search::ToolLocateSearch{config_path: config_path.clone()}), + ]; + + let codebase_change_tools: Vec> = vec![ + Box::new(crate::tools::file_edit::tool_create_textdoc::ToolCreateTextDoc{config_path: config_path.clone()}), + Box::new(crate::tools::file_edit::tool_update_textdoc::ToolUpdateTextDoc{config_path: config_path.clone()}), + Box::new(crate::tools::file_edit::tool_update_textdoc_regex::ToolUpdateTextDocRegex{config_path: config_path.clone()}), + Box::new(crate::tools::tool_rm::ToolRm{config_path: config_path.clone()}), + Box::new(crate::tools::tool_mv::ToolMv{config_path: config_path.clone()}), + ]; + + let web_tools: Vec> = vec![ + Box::new(crate::tools::tool_web::ToolWeb{config_path: config_path.clone()}), + ]; + + let deep_analysis_tools: Vec> = vec![ + Box::new(crate::tools::tool_strategic_planning::ToolStrategicPlanning{config_path: config_path.clone()}), + ]; + + let knowledge_tools: Vec> = vec![ + Box::new(crate::tools::tool_knowledge::ToolGetKnowledge{config_path: config_path.clone()}), + Box::new(crate::tools::tool_create_knowledge::ToolCreateKnowledge{config_path: config_path.clone()}), + Box::new(crate::tools::tool_create_memory_bank::ToolCreateMemoryBank{config_path: config_path.clone()}), ]; let mut tool_groups = vec![ ToolGroup { - name: "builtin".to_string(), - description: "Builtin tools".to_string(), + name: "codebase_search".to_string(), + description: "Codebase search tools".to_string(), + category: ToolGroupCategory::Builtin, + tools: codebase_search_tools, + allow_per_tool_toggle: false + }, + ToolGroup { + name: "codebase_change".to_string(), + description: "Codebase modification tools".to_string(), + category: ToolGroupCategory::Builtin, + tools: codebase_change_tools, + allow_per_tool_toggle: false + }, + ToolGroup { + name: "web".to_string(), + description: "Web tools".to_string(), + category: ToolGroupCategory::Builtin, + tools: web_tools, + allow_per_tool_toggle: false + }, + ToolGroup { + name: "strategic_planning".to_string(), + description: "Strategic planning tools".to_string(), + category: ToolGroupCategory::Builtin, + tools: deep_analysis_tools, + allow_per_tool_toggle: false + }, + ToolGroup { + name: "knowledge".to_string(), + description: "Knowledge tools".to_string(), category: ToolGroupCategory::Builtin, - tools, + tools: knowledge_tools, + allow_per_tool_toggle: false }, ]; @@ -124,12 +165,14 @@ async fn get_integration_tools( description: "Integration tools".to_string(), category: ToolGroupCategory::Integration, tools: vec![], + allow_per_tool_toggle: true }, ToolGroup { name: "mcp".to_string(), description: "MCP tools".to_string(), category: ToolGroupCategory::MCP, tools: vec![], + allow_per_tool_toggle: true }, ]; let (integrations_map, _yaml_errors) = load_integrations(gcx.clone(), &["**/*".to_string()]).await; From cf69ce4b019f9cfa123f27afa2385268088cc8be Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 15 May 2025 16:15:29 +0200 Subject: [PATCH 77/90] refactor: apply_model_adaptation_patch remove never used parameters --- .../engine/src/scratchpad_abstract.rs | 2 -- .../engine/src/scratchpads/chat_generic.rs | 2 -- .../src/scratchpads/chat_passthrough.rs | 2 -- .../src/scratchpads/code_completion_fim.rs | 2 -- .../scratchpads/code_completion_replace.rs | 4 ---- refact-agent/engine/src/scratchpads/mod.rs | 21 ++----------------- 6 files changed, 2 insertions(+), 31 deletions(-) diff --git a/refact-agent/engine/src/scratchpad_abstract.rs b/refact-agent/engine/src/scratchpad_abstract.rs index cfa463d3c..be6bbadc4 100644 --- a/refact-agent/engine/src/scratchpad_abstract.rs +++ b/refact-agent/engine/src/scratchpad_abstract.rs @@ -74,8 +74,6 @@ pub trait ScratchpadAbstract: Send { async fn apply_model_adaptation_patch( &mut self, patch: &Value, - exploration_tools: bool, - agentic_tools: bool, ) -> Result<(), String>; async fn prompt( diff --git a/refact-agent/engine/src/scratchpads/chat_generic.rs b/refact-agent/engine/src/scratchpads/chat_generic.rs index 40c941471..88ec17253 100644 --- a/refact-agent/engine/src/scratchpads/chat_generic.rs +++ b/refact-agent/engine/src/scratchpads/chat_generic.rs @@ -67,8 +67,6 @@ impl ScratchpadAbstract for GenericChatScratchpad { async fn apply_model_adaptation_patch( &mut self, patch: &Value, - _exploration_tools: bool, - _agentic_tools: bool, ) -> Result<(), String> { self.token_bos = patch.get("token_bos").and_then(|x| x.as_str()).unwrap_or("").to_string(); self.token_esc = patch.get("token_esc").and_then(|x| x.as_str()).unwrap_or("").to_string(); diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index 48ea338e5..566adc251 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -96,8 +96,6 @@ impl ScratchpadAbstract for ChatPassthrough { async fn apply_model_adaptation_patch( &mut self, _patch: &Value, - _exploration_tools: bool, - _agentic_tools: bool, ) -> Result<(), String> { Ok(()) } diff --git a/refact-agent/engine/src/scratchpads/code_completion_fim.rs b/refact-agent/engine/src/scratchpads/code_completion_fim.rs index 90f46367f..827dd372a 100644 --- a/refact-agent/engine/src/scratchpads/code_completion_fim.rs +++ b/refact-agent/engine/src/scratchpads/code_completion_fim.rs @@ -79,8 +79,6 @@ impl ScratchpadAbstract for FillInTheMiddleScratchpad { async fn apply_model_adaptation_patch( &mut self, patch: &Value, - _exploration_tools: bool, - _agentic_tools: bool, ) -> Result<(), String> { // That will work for some models (starcoder) without patching self.fim_prefix = patch.get("fim_prefix").and_then(|x| x.as_str()).unwrap_or("").to_string(); diff --git a/refact-agent/engine/src/scratchpads/code_completion_replace.rs b/refact-agent/engine/src/scratchpads/code_completion_replace.rs index 7acf0ab2f..4e7f22790 100644 --- a/refact-agent/engine/src/scratchpads/code_completion_replace.rs +++ b/refact-agent/engine/src/scratchpads/code_completion_replace.rs @@ -602,8 +602,6 @@ impl ScratchpadAbstract for CodeCompletionReplaceScratchpad { async fn apply_model_adaptation_patch( &mut self, patch: &Value, - _exploration_tools: bool, - _agentic_tools: bool, ) -> Result<(), String> { self.token_bos = patch .get("token_bos") @@ -876,8 +874,6 @@ impl ScratchpadAbstract for CodeCompletionReplacePassthroughScratchpad { async fn apply_model_adaptation_patch( &mut self, patch: &Value, - _exploration_tools: bool, - _agentic_tools: bool, ) -> Result<(), String> { self.t.context_format = patch .get("context_format") diff --git a/refact-agent/engine/src/scratchpads/mod.rs b/refact-agent/engine/src/scratchpads/mod.rs index 7ac1b0831..a85d123bc 100644 --- a/refact-agent/engine/src/scratchpads/mod.rs +++ b/refact-agent/engine/src/scratchpads/mod.rs @@ -60,7 +60,7 @@ pub async fn create_code_completion_scratchpad( } else { return Err(format!("This rust binary doesn't have code completion scratchpad \"{}\" compiled in", model_rec.scratchpad)); } - result.apply_model_adaptation_patch(&model_rec.scratchpad_patch, false, false).await?; + result.apply_model_adaptation_patch(&model_rec.scratchpad_patch).await?; verify_has_send(&result); Ok(result) } @@ -86,24 +86,7 @@ pub async fn create_chat_scratchpad( } else { return Err(format!("This rust binary doesn't have chat scratchpad \"{}\" compiled in", model_rec.scratchpad)); } - let mut exploration_tools: bool = false; - let mut agentic_tools: bool = false; - if post.tools.is_some() { - for t in post.tools.as_ref().unwrap() { - let tobj = t.as_object().unwrap(); - if let Some(function) = tobj.get("function") { - if let Some(name) = function.get("name") { - if name.as_str() == Some("web") { // anything that will still be on without ast and vecdb - exploration_tools = true; - } - if name.as_str() == Some("apply_edit") { - agentic_tools = true; - } - } - } - } - } - result.apply_model_adaptation_patch(&model_rec.scratchpad_patch, exploration_tools, agentic_tools).await?; + result.apply_model_adaptation_patch(&model_rec.scratchpad_patch).await?; verify_has_send(&result); Ok(result) } From f27847b902e093947696d0ee33ad10cf07b33e30 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Fri, 16 May 2025 16:51:20 +0200 Subject: [PATCH 78/90] refactor: internally use ToolDesc instead of openai repr --- refact-agent/engine/src/call_validation.rs | 2 - .../engine/src/http/routers/v1/at_tools.rs | 4 +- .../engine/src/http/routers/v1/chat.rs | 76 +++---------------- .../src/scratchpads/chat_passthrough.rs | 64 ++++++---------- refact-agent/engine/src/scratchpads/mod.rs | 11 ++- refact-agent/engine/src/subchat.rs | 8 +- .../engine/src/tools/tools_description.rs | 27 +------ refact-agent/engine/src/tools/tools_list.rs | 35 +++++++++ 8 files changed, 85 insertions(+), 142 deletions(-) diff --git a/refact-agent/engine/src/call_validation.rs b/refact-agent/engine/src/call_validation.rs index 2ba2e2cf8..4af2cca32 100644 --- a/refact-agent/engine/src/call_validation.rs +++ b/refact-agent/engine/src/call_validation.rs @@ -239,8 +239,6 @@ pub struct ChatPost { #[serde(default)] pub n: Option, #[serde(default)] - pub tools: Option>, - #[serde(default)] pub tool_choice: Option, #[serde(default)] pub checkpoints_enabled: bool, diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index f548b860e..bd5e9846f 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -66,13 +66,13 @@ pub struct ToolExecuteResponse { pub tools_ran: bool, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] pub struct ToolResponse { pub spec: ToolDesc, pub enabled: bool, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] pub struct ToolGroupResponse { pub name: String, pub description: String, diff --git a/refact-agent/engine/src/http/routers/v1/chat.rs b/refact-agent/engine/src/http/routers/v1/chat.rs index b85a6ec0c..67fb74ce8 100644 --- a/refact-agent/engine/src/http/routers/v1/chat.rs +++ b/refact-agent/engine/src/http/routers/v1/chat.rs @@ -5,9 +5,8 @@ use tokio::sync::RwLock as ARwLock; use axum::Extension; use axum::response::Result; use hyper::{Body, Response, StatusCode}; -use serde_json::Value; -use crate::call_validation::{ChatContent, ChatMessage, ChatPost, ChatMode}; +use crate::call_validation::{ChatContent, ChatMessage, ChatPost}; use crate::caps::resolve_chat_model; use crate::custom_error::ScratchError; use crate::at_commands::at_commands::AtCommandsContext; @@ -15,46 +14,8 @@ use crate::git::checkpoints::create_workspace_checkpoint; use crate::global_context::{GlobalContext, SharedGlobalContext}; use crate::indexing_utils::wait_for_indexing_if_needed; use crate::integrations::docker::docker_container_manager::docker_container_check_status_or_start; -use crate::tools::tools_list::get_available_tools; - - -pub fn available_tools_by_chat_mode(current_tools: Vec, chat_mode: &ChatMode) -> Vec { - fn filter_out_tools(current_tools: &Vec, blacklist: &Vec<&str>) -> Vec { - current_tools - .into_iter() - .filter(|x| { - x.get("function") - .and_then(|x| x.get("name")) - .and_then(|tool_name| tool_name.as_str()) - .map(|tool_name_str| !blacklist.contains(&tool_name_str)) - .unwrap_or(true) - }) - .cloned() - .collect() - } - fn keep_tools(current_tools: &Vec, whitelist: &Vec<&str>) -> Vec { - current_tools - .into_iter() - .filter(|x| { - x.get("function") - .and_then(|x| x.get("name")) - .and_then(|tool_name| tool_name.as_str()) - .map(|tool_name_str| whitelist.contains(&tool_name_str)) - .unwrap_or(false) - }) - .cloned() - .collect() - } - match chat_mode { - ChatMode::NO_TOOLS => { - vec![] - }, - ChatMode::AGENT => filter_out_tools(¤t_tools, &vec!["search_symbol_definition", "search_symbol_usages", "search_pattern", "search_semantic"]), - ChatMode::EXPLORE => current_tools, - ChatMode::CONFIGURE => filter_out_tools(¤t_tools, &vec!["tree", "locate", "knowledge", "search"]), - ChatMode::PROJECT_SUMMARY => keep_tools(¤t_tools, &vec!["cat", "tree", "bash"]), - } -} +use crate::tools::tools_description::ToolDesc; +use crate::tools::tools_list::get_available_tools_by_chat_mode; pub const CHAT_TOP_N: usize = 12; @@ -122,32 +83,12 @@ async fn _chat( tracing::info!("chat_mode {:?}", chat_post.meta.chat_mode); + let tools: Vec = get_available_tools_by_chat_mode(gcx.clone(), chat_post.meta.chat_mode).await + .into_iter() + .map(|tool| tool.tool_description()) + .collect(); - chat_post.tools = if chat_post.meta.chat_mode == ChatMode::NO_TOOLS { - None - } else { - // All available tools for the current lsp config in openai style - let tools: Vec = get_available_tools(gcx.clone()).await.into_iter().filter_map(|tool| { - let tool_desc = tool.tool_description(); - if tool.config().unwrap_or_default().enabled && tool_desc.agentic == (chat_post.meta.chat_mode == ChatMode::AGENT) { - let mut openai_style_tool = tool_desc.into_openai_style(); - openai_style_tool["function"].as_object_mut().unwrap().remove("agentic"); - Some(openai_style_tool) - } else { - None - } - }).collect(); - - tracing::info!( - "tools [{}]", - tools.iter() - .filter_map(|tool| tool.get("function").and_then(|f| f.get("name")).and_then(|n| n.as_str())) - .collect::>() - .join(", ") - ); - - Some(available_tools_by_chat_mode(tools, &chat_post.meta.chat_mode)) - }; + tracing::info!("tools: {:?}", tools.iter().map(|t| &t.name).collect::>()); let caps = crate::global_context::try_load_caps_quickly_if_not_present(gcx.clone(), 0).await?; let model_rec = resolve_chat_model(caps, &chat_post.model) @@ -235,6 +176,7 @@ async fn _chat( let mut scratchpad = crate::scratchpads::create_chat_scratchpad( gcx.clone(), &mut chat_post, + tools, &messages, true, &model_rec, diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index 566adc251..bd8f238be 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -11,13 +11,14 @@ use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ChatMessage, ChatPost, ReasoningEffort, SamplingParameters}; use crate::caps::resolve_chat_model; use crate::http::http_get_json; +use crate::http::routers::v1::at_tools::ToolGroupResponse; use crate::integrations::docker::docker_container_manager::docker_container_get_host_lsp_port_to_connect; use crate::scratchpad_abstract::{FinishReason, HasTokenizerAndEot, ScratchpadAbstract}; use crate::scratchpads::chat_utils_limit_history::fix_and_limit_messages_history; use crate::scratchpads::scratchpad_utils::HasRagResults; use crate::scratchpads::chat_utils_prompts::prepend_the_right_system_prompt_and_maybe_more_initial_messages; use crate::scratchpads::passthrough_convert_messages::convert_messages_to_openai_format; -use crate::tools::tools_description::tool_description_list_from_yaml; +use crate::tools::tools_description::ToolDesc; use crate::tools::tools_list::get_available_tools; use crate::tools::tools_execute::{run_tools_locally, run_tools_remotely}; @@ -58,6 +59,7 @@ impl DeltaSender { pub struct ChatPassthrough { pub t: HasTokenizerAndEot, pub post: ChatPost, + pub tools: Vec, pub messages: Vec, pub prepend_system_prompt: bool, pub has_rag_results: HasRagResults, @@ -71,6 +73,7 @@ impl ChatPassthrough { pub fn new( tokenizer: Option>, post: &ChatPost, + tools: Vec, messages: &Vec, prepend_system_prompt: bool, allow_at: bool, @@ -80,6 +83,7 @@ impl ChatPassthrough { ChatPassthrough { t: HasTokenizerAndEot::new(tokenizer), post: post.clone(), + tools, messages: messages.clone(), prepend_system_prompt, has_rag_results: HasRagResults::new(), @@ -141,48 +145,28 @@ impl ScratchpadAbstract for ChatPassthrough { let mut big_json = serde_json::json!({}); if self.supports_tools { - let post_tools = self.post.tools.as_ref().and_then(|tools| { - if tools.is_empty() { - None - } else { - Some(tools.clone()) - } - }); - - let mut tools = if let Some(t) = post_tools { - // here we only use names from the tools in `post` - let turned_on = t.iter().filter_map(|x| { - if let Value::Object(map) = x { - map.get("function").and_then(|f| f.get("name")).and_then(|name| name.as_str().map(|s| s.to_string())) - } else { - None - } - }).collect::>(); - // and take descriptions of tools from the official source - if should_execute_remotely { - let port = docker_container_get_host_lsp_port_to_connect(gcx.clone(), &self.post.meta.chat_id).await?; - tracing::info!("Calling tools on port: {}", port); - let tool_desclist: Vec = http_get_json(&format!("http://localhost:{port}/v1/tools")).await?; - Some(tool_desclist.into_iter().filter(|tool_desc| { - tool_desc.get("function").and_then(|f| f.get("name")).and_then(|n| n.as_str()).map_or(false, |n| turned_on.contains(&n.to_string())) - }).collect::>()) - } else { - let allow_experimental = gcx.read().await.cmdline.experimental; - let tool_descriptions = tool_description_list_from_yaml(at_tools, Some(&turned_on), allow_experimental).await?; - Some(tool_descriptions.into_iter().filter(|x| x.is_supported_by(&self.post.model)).map(|x| x.into_openai_style()).collect::>()) - } + let tools: Vec = if should_execute_remotely { + let port = docker_container_get_host_lsp_port_to_connect(gcx.clone(), &self.post.meta.chat_id).await?; + tracing::info!("Calling tools on port: {}", port); + let tool_desclist: Vec = http_get_json(&format!("http://localhost:{port}/v1/tools")).await?; + tool_desclist.into_iter() + .flat_map(|tool_group| tool_group.tools) + .map(|tool| tool.spec) + .collect() } else { - None + self.tools.iter() + .filter(|x| x.is_supported_by(&self.post.model)) + .cloned() + .collect() }; - // remove "agentic" - if let Some(tools) = &mut tools { - for tool in tools { - if let Some(function) = tool.get_mut("function") { - function.as_object_mut().unwrap().remove("agentic"); - } - } - } + let tools = tools.into_iter().map(|tool| tool.into_openai_style()).collect::>(); + + let tools = if tools.is_empty() { + None + } else { + Some(tools) + }; big_json["tools"] = json!(tools); big_json["tool_choice"] = json!(self.post.tool_choice); diff --git a/refact-agent/engine/src/scratchpads/mod.rs b/refact-agent/engine/src/scratchpads/mod.rs index a85d123bc..c1cf214a2 100644 --- a/refact-agent/engine/src/scratchpads/mod.rs +++ b/refact-agent/engine/src/scratchpads/mod.rs @@ -26,6 +26,7 @@ use crate::scratchpad_abstract::ScratchpadAbstract; use crate::completion_cache; use crate::telemetry::telemetry_structs; use crate::tokens; +use crate::tools::tools_description::ToolDesc; fn verify_has_send(_x: &T) {} @@ -68,6 +69,7 @@ pub async fn create_code_completion_scratchpad( pub async fn create_chat_scratchpad( global_context: Arc>, post: &mut ChatPost, + tools: Vec, messages: &Vec, prepend_system_prompt: bool, model_rec: &ChatModelRecord, @@ -81,7 +83,14 @@ pub async fn create_chat_scratchpad( )); } else if model_rec.scratchpad == "PASSTHROUGH" { result = Box::new(chat_passthrough::ChatPassthrough::new( - tokenizer_arc.clone(), post, messages, prepend_system_prompt, allow_at, model_rec.supports_tools, model_rec.supports_clicks + tokenizer_arc.clone(), + post, + tools, + messages, + prepend_system_prompt, + allow_at, + model_rec.supports_tools, + model_rec.supports_clicks, )); } else { return Err(format!("This rust binary doesn't have chat scratchpad \"{}\" compiled in", model_rec.scratchpad)); diff --git a/refact-agent/engine/src/subchat.rs b/refact-agent/engine/src/subchat.rs index 4055eb6ff..7193accc8 100644 --- a/refact-agent/engine/src/subchat.rs +++ b/refact-agent/engine/src/subchat.rs @@ -29,7 +29,7 @@ pub async fn create_chat_post_and_scratchpad( n: usize, reasoning_effort: Option, prepend_system_prompt: bool, - tools: Option>, + tools: Vec, tool_choice: Option, only_deterministic_messages: bool, _should_execute_remotely: bool, @@ -61,7 +61,6 @@ pub async fn create_chat_post_and_scratchpad( stream: Some(false), temperature, n: Some(n), - tools, tool_choice, only_deterministic_messages, subchat_tool_parameters: tconfig.subchat_tool_parameters.clone(), @@ -85,6 +84,7 @@ pub async fn create_chat_post_and_scratchpad( let scratchpad = crate::scratchpads::create_chat_scratchpad( global_context.clone(), &mut chat_post, + tools, &messages.into_iter().cloned().collect::>(), prepend_system_prompt, &model_rec, @@ -294,7 +294,7 @@ pub async fn subchat_single( &tool.name }).collect::>()); - let tools = tools_desclist.into_iter().filter(|x| x.is_supported_by(model_id)).map(|x|x.into_openai_style()).collect::>(); + let tools = tools_desclist.into_iter().filter(|x| x.is_supported_by(model_id)).collect::>(); let max_new_tokens = max_new_tokens.unwrap_or(MAX_NEW_TOKENS); let (mut chat_post, spad, model_rec) = create_chat_post_and_scratchpad( @@ -307,7 +307,7 @@ pub async fn subchat_single( n, reasoning_effort, prepend_system_prompt, - Some(tools), + tools, tool_choice.clone(), only_deterministic_messages, should_execute_remotely, diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index b753354ba..7668ae493 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -26,7 +26,7 @@ pub struct MatchConfirmDeny { pub rule: String, } -#[derive(Clone, Copy, Serialize, Debug)] +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] pub enum ToolGroupCategory { Builtin, @@ -258,7 +258,6 @@ pub fn model_supports_array_param_type(model_id: &str) -> bool { pub fn make_openai_tool_value( name: String, - agentic: bool, description: String, parameters_required: Vec, parameters: Vec, @@ -277,7 +276,6 @@ pub fn make_openai_tool_value( "type": "function", "function": { "name": name, - "agentic": agentic, // this field is not OpenAI's "description": description, "parameters": { "type": "object", @@ -293,7 +291,6 @@ impl ToolDesc { pub fn into_openai_style(self) -> Value { make_openai_tool_value( self.name, - self.agentic, self.description, self.parameters_required, self.parameters, @@ -313,28 +310,6 @@ impl ToolDesc { } } -pub async fn tool_description_list_from_yaml( - tools: IndexMap>, - turned_on: Option<&Vec>, - allow_experimental: bool, -) -> Result, String> { - let mut tool_desc_vec: Vec = vec![]; - - for (tool_name, tool) in tools { - if !tool_desc_vec.iter().any(|desc| desc.name == tool_name) { - tool_desc_vec.push(tool.tool_description()); - } - } - - Ok(tool_desc_vec.iter() - .filter(|x| { - turned_on.map_or(true, |turned_on_vec| turned_on_vec.contains(&x.name)) && - (allow_experimental || !x.experimental) - }) - .cloned() - .collect::>()) -} - #[allow(dead_code)] const NOT_READY_TOOLS: &str = r####" - name: "diff" diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index df1944d21..0dbe4e569 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use tokio::sync::RwLock as ARwLock; +use crate::call_validation::ChatMode; use crate::global_context::{try_load_caps_quickly_if_not_present, GlobalContext}; use crate::integrations::running_integrations::load_integrations; @@ -208,3 +209,37 @@ pub async fn get_available_tools( ) -> Vec> { get_available_tool_groups(gcx).await.into_iter().flat_map(|g| g.tools).collect() } + +pub async fn get_available_tools_by_chat_mode( + gcx: Arc>, + chat_mode: ChatMode, +) -> Vec> { + if chat_mode == ChatMode::NO_TOOLS { + return vec![]; + } + + let tools = get_available_tool_groups(gcx).await.into_iter() + .flat_map(|g| g.tools) + .filter(|tool| tool.config().unwrap_or_default().enabled); + + + match chat_mode { + ChatMode::NO_TOOLS => unreachable!("Condition handled at the beginning of the function."), + ChatMode::EXPLORE => { + tools.filter(|tool| !tool.tool_description().agentic).collect() + }, + ChatMode::AGENT => { + let blacklist = ["search_symbol_definition", "search_symbol_usages", "search_pattern", "search_semantic"]; + tools.filter(|tool| !blacklist.contains(&tool.tool_description().name.as_str())).collect() + } + ChatMode::CONFIGURE => { + let blacklist = ["tree", "locate", "knowledge", "search"]; + tools.filter(|tool| !blacklist.contains(&tool.tool_description().name.as_str())).collect() + }, + ChatMode::PROJECT_SUMMARY => { + let whitelist = ["cat", "tree"]; + tools.filter(|tool| whitelist.contains(&tool.tool_description().name.as_str())).collect() + }, + } +} + From 2021441dedcb6a9cddb53541a0bcc76ef95f6097 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 21 May 2025 10:02:04 +0200 Subject: [PATCH 79/90] don't limit groups to enable all or disable all at once --- .../engine/src/tools/tools_description.rs | 2 -- refact-agent/engine/src/tools/tools_list.rs | 17 +++++------------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_description.rs b/refact-agent/engine/src/tools/tools_description.rs index 7668ae493..ac6dc8802 100644 --- a/refact-agent/engine/src/tools/tools_description.rs +++ b/refact-agent/engine/src/tools/tools_description.rs @@ -4,7 +4,6 @@ use serde_json::{Value, json}; use serde::{Deserialize, Serialize}; use async_trait::async_trait; use tokio::sync::Mutex as AMutex; -use indexmap::IndexMap; use crate::at_commands::at_commands::AtCommandsContext; use crate::call_validation::{ChatUsage, ContextEnum}; @@ -39,7 +38,6 @@ pub struct ToolGroup { pub description: String, pub category: ToolGroupCategory, pub tools: Vec>, - pub allow_per_tool_toggle: bool, } #[derive(Clone, Serialize, Deserialize, Debug)] diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index 0dbe4e569..d222ad23b 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -114,39 +114,34 @@ async fn get_builtin_tools( let mut tool_groups = vec![ ToolGroup { - name: "codebase_search".to_string(), + name: "Codebase Search".to_string(), description: "Codebase search tools".to_string(), category: ToolGroupCategory::Builtin, tools: codebase_search_tools, - allow_per_tool_toggle: false }, ToolGroup { - name: "codebase_change".to_string(), + name: "Codebase Change".to_string(), description: "Codebase modification tools".to_string(), category: ToolGroupCategory::Builtin, tools: codebase_change_tools, - allow_per_tool_toggle: false }, ToolGroup { - name: "web".to_string(), + name: "Web".to_string(), description: "Web tools".to_string(), category: ToolGroupCategory::Builtin, tools: web_tools, - allow_per_tool_toggle: false }, ToolGroup { - name: "strategic_planning".to_string(), + name: "Strategic Planning".to_string(), description: "Strategic planning tools".to_string(), category: ToolGroupCategory::Builtin, tools: deep_analysis_tools, - allow_per_tool_toggle: false }, ToolGroup { - name: "knowledge".to_string(), + name: "Knowledge".to_string(), description: "Knowledge tools".to_string(), category: ToolGroupCategory::Builtin, tools: knowledge_tools, - allow_per_tool_toggle: false }, ]; @@ -166,14 +161,12 @@ async fn get_integration_tools( description: "Integration tools".to_string(), category: ToolGroupCategory::Integration, tools: vec![], - allow_per_tool_toggle: true }, ToolGroup { name: "mcp".to_string(), description: "MCP tools".to_string(), category: ToolGroupCategory::MCP, tools: vec![], - allow_per_tool_toggle: true }, ]; let (integrations_map, _yaml_errors) = load_integrations(gcx.clone(), &["**/*".to_string()]).await; From 36d8603eb6ed57d8136230d60e0ac36e743404da Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 21 May 2025 14:03:26 +0200 Subject: [PATCH 80/90] fully remove allow per tool toggle --- refact-agent/engine/src/http/routers/v1/at_tools.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index bd5e9846f..7f9f0ccf3 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -77,7 +77,6 @@ pub struct ToolGroupResponse { pub name: String, pub description: String, pub category: ToolGroupCategory, - pub allow_per_tool_toggle: bool, pub tools: Vec, } @@ -103,7 +102,6 @@ pub async fn handle_v1_get_tools( name: tool_group.name, description: tool_group.description, category: tool_group.category, - allow_per_tool_toggle: tool_group.allow_per_tool_toggle, tools, }) }).collect(); From 1e980ed860cf3254b46602bdd83e2afd2af4e11e Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 21 May 2025 17:08:24 +0200 Subject: [PATCH 81/90] fix: improved positioning and sizing of tooltip hover cards --- .../ChatForm/AgentCapabilities/ToolGroup.tsx | 4 +-- .../ChatForm/AgentCapabilities/ToolsList.tsx | 9 ++++-- .../src/components/ChatForm/ChatControls.tsx | 28 ++++++++++--------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx index ed51fc1a8..6fe7ecdca 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolGroup.tsx @@ -70,8 +70,8 @@ export const ToolGroup: React.FC = ({ - - + + {group.description} diff --git a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx index e5bc3e188..50f44859a 100644 --- a/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx +++ b/refact-agent/gui/src/components/ChatForm/AgentCapabilities/ToolsList.tsx @@ -91,8 +91,13 @@ export const ToolsList: React.FC = ({ - - + + {tool.spec.description} diff --git a/refact-agent/gui/src/components/ChatForm/ChatControls.tsx b/refact-agent/gui/src/components/ChatForm/ChatControls.tsx index cb8f7ba0c..fe0a1dcf0 100644 --- a/refact-agent/gui/src/components/ChatForm/ChatControls.tsx +++ b/refact-agent/gui/src/components/ChatForm/ChatControls.tsx @@ -82,16 +82,18 @@ export const ApplyPatchSwitch: React.FC = () => { - - Enabled - + + + Enabled + + When enabled, Refact Agent will automatically apply changes to files without asking for your confirmation. - + Disabled - + When disabled, Refact Agent will ask for your confirmation before applying any unsaved changes. @@ -133,13 +135,13 @@ export const AgentRollbackSwitch: React.FC = () => { - + - + When enabled, Refact Agent will automatically make snapshots of files between your messages - + You can rollback file changes to checkpoints taken when you sent messages to Agent @@ -150,7 +152,7 @@ export const AgentRollbackSwitch: React.FC = () => { whiteSpace: "pre-wrap", }} > - + { - + - + When enabled, Refact Agent will automatically generate related follow-ups to the conversation @@ -267,9 +269,9 @@ export const TitleGenerationSwitch: React.FC = () => { - + - + When enabled, Refact Agent will automatically generate summarized chat title for the conversation From 17421416613665b55e2cf68c8543577637c759aa Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 22 May 2025 10:44:01 +0200 Subject: [PATCH 82/90] doc: doc comment for address param really all should have, but one at a time --- refact-agent/engine/src/global_context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/refact-agent/engine/src/global_context.rs b/refact-agent/engine/src/global_context.rs index 7442656e5..745310c06 100644 --- a/refact-agent/engine/src/global_context.rs +++ b/refact-agent/engine/src/global_context.rs @@ -35,6 +35,7 @@ pub struct CommandLine { #[structopt(long, default_value="", help="Send logs to a file.")] pub logs_to_file: String, #[structopt(long, short="u", default_value="", help="URL to use: \"Refact\" for Cloud, or your Self-Hosted Server URL. To bring your own keys, use \"Refact\" and set up providers.")] + /// URL to use: "Refact" for Cloud, or your Self-Hosted Server URL. To bring your own keys, use "Refact" and set up providers. pub address_url: String, #[structopt(long, short="k", default_value="", help="The API key to authenticate your requests, will appear in HTTP requests this binary makes.")] pub api_key: String, From 2361b3ce956aaeb966f4262cb0bdbc8f1fa4f012 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 22 May 2025 13:51:54 +0200 Subject: [PATCH 83/90] feat: handle system prompt based on present tools --- .../src/http/routers/v1/system_prompt.rs | 12 +++- .../engine/src/integrations/config_chat.rs | 11 +++- .../engine/src/integrations/mcp/tool_mcp.rs | 2 +- .../src/integrations/project_summary_chat.rs | 13 +++- .../engine/src/scratchpads/chat_generic.rs | 13 +++- .../src/scratchpads/chat_passthrough.rs | 8 ++- .../src/scratchpads/chat_utils_prompts.rs | 61 ++++++++++++++++++- .../customization_compiled_in.yaml | 22 ++----- 8 files changed, 114 insertions(+), 28 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/system_prompt.rs b/refact-agent/engine/src/http/routers/v1/system_prompt.rs index 0f2992f80..2298c9925 100644 --- a/refact-agent/engine/src/http/routers/v1/system_prompt.rs +++ b/refact-agent/engine/src/http/routers/v1/system_prompt.rs @@ -11,6 +11,7 @@ use crate::global_context::GlobalContext; use crate::indexing_utils::wait_for_indexing_if_needed; use crate::scratchpads::chat_utils_prompts::prepend_the_right_system_prompt_and_maybe_more_initial_messages; use crate::scratchpads::scratchpad_utils::HasRagResults; +use crate::tools::tools_list::get_available_tools_by_chat_mode; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PrependSystemPromptPost { @@ -35,7 +36,16 @@ pub async fn handle_v1_prepend_system_prompt_and_maybe_more_initial_messages( let mut has_rag_results = HasRagResults::new(); let messages = prepend_the_right_system_prompt_and_maybe_more_initial_messages( - gcx.clone(), post.messages, &post.chat_meta, &mut has_rag_results).await; + gcx.clone(), + post.messages, + &post.chat_meta, + &mut has_rag_results, + get_available_tools_by_chat_mode(gcx.clone(), post.chat_meta.chat_mode) + .await + .into_iter() + .map(|t| t.tool_description().name) + .collect(), + ).await; let messages_to_stream_back = has_rag_results.in_json; Ok(Response::builder() diff --git a/refact-agent/engine/src/integrations/config_chat.rs b/refact-agent/engine/src/integrations/config_chat.rs index b982544e0..e6f227838 100644 --- a/refact-agent/engine/src/integrations/config_chat.rs +++ b/refact-agent/engine/src/integrations/config_chat.rs @@ -8,6 +8,7 @@ use crate::global_context::GlobalContext; use crate::call_validation::{ChatContent, ChatMessage, ContextFile, ChatMeta}; use crate::scratchpads::scratchpad_utils::HasRagResults; use crate::integrations::yaml_schema::ISchema; +use crate::tools::tools_list::get_available_tools_by_chat_mode; pub async fn mix_config_messages( @@ -148,7 +149,15 @@ pub async fn mix_config_messages( let system_message = ChatMessage { role: "system".to_string(), content: ChatContent::SimpleText( - crate::scratchpads::chat_utils_prompts::system_prompt_add_extra_instructions(gcx.clone(), &sp.text).await + crate::scratchpads::chat_utils_prompts::system_prompt_add_extra_instructions( + gcx.clone(), + sp.text.clone(), + get_available_tools_by_chat_mode(gcx.clone(), chat_meta.chat_mode) + .await + .into_iter() + .map(|t| t.tool_description().name) + .collect(), + ).await ), ..Default::default() }; diff --git a/refact-agent/engine/src/integrations/mcp/tool_mcp.rs b/refact-agent/engine/src/integrations/mcp/tool_mcp.rs index 808ec95c5..0776f6485 100644 --- a/refact-agent/engine/src/integrations/mcp/tool_mcp.rs +++ b/refact-agent/engine/src/integrations/mcp/tool_mcp.rs @@ -233,7 +233,7 @@ impl Tool for ToolMCP { ToolDesc { name: tool_name.clone(), - display_name: self.mcp_tool.name, + display_name: self.mcp_tool.name.to_string(), source: ToolSource { source_type: ToolSourceType::Integration, config_path: self.config_path.clone(), diff --git a/refact-agent/engine/src/integrations/project_summary_chat.rs b/refact-agent/engine/src/integrations/project_summary_chat.rs index 33dd56989..41c06342b 100644 --- a/refact-agent/engine/src/integrations/project_summary_chat.rs +++ b/refact-agent/engine/src/integrations/project_summary_chat.rs @@ -5,11 +5,12 @@ use crate::call_validation::{ChatContent, ChatMessage, ChatMeta}; use crate::integrations::setting_up_integrations::integrations_all; use crate::scratchpads::chat_utils_prompts::system_prompt_add_extra_instructions; use crate::scratchpads::scratchpad_utils::HasRagResults; +use crate::tools::tools_list::get_available_tools_by_chat_mode; pub async fn mix_project_summary_messages( gcx: Arc>, - _chat_meta: &ChatMeta, + chat_meta: &ChatMeta, messages: &mut Vec, stream_back_to_user: &mut HasRagResults, ) { @@ -37,7 +38,15 @@ pub async fn mix_project_summary_messages( sp_text = sp_text.replace("%AVAILABLE_INTEGRATIONS%", &integrations.iter().map(|x|x.integr_name.clone()).collect::>().join(", ")); } - sp_text = system_prompt_add_extra_instructions(gcx.clone(), &sp_text).await; // print inside + sp_text = system_prompt_add_extra_instructions( + gcx.clone(), + sp_text, + get_available_tools_by_chat_mode(gcx.clone(), chat_meta.chat_mode) + .await + .into_iter() + .map(|t| t.tool_description().name) + .collect(), + ).await; // print inside let system_message = ChatMessage { role: "system".to_string(), diff --git a/refact-agent/engine/src/scratchpads/chat_generic.rs b/refact-agent/engine/src/scratchpads/chat_generic.rs index 88ec17253..cf37d3a98 100644 --- a/refact-agent/engine/src/scratchpads/chat_generic.rs +++ b/refact-agent/engine/src/scratchpads/chat_generic.rs @@ -14,6 +14,7 @@ use crate::scratchpads::chat_utils_deltadelta::DeltaDeltaChatStreamer; use crate::scratchpads::chat_utils_limit_history::fix_and_limit_messages_history; use crate::scratchpads::chat_utils_prompts::prepend_the_right_system_prompt_and_maybe_more_initial_messages; use crate::scratchpads::scratchpad_utils::HasRagResults; +use crate::tools::tools_list::get_available_tools_by_chat_mode; const DEBUG: bool = true; @@ -104,7 +105,17 @@ impl ScratchpadAbstract for GenericChatScratchpad { }; let messages = if self.prepend_system_prompt && self.allow_at { - prepend_the_right_system_prompt_and_maybe_more_initial_messages(gcx.clone(), self.messages.clone(), &self.post.meta, &mut self.has_rag_results).await + prepend_the_right_system_prompt_and_maybe_more_initial_messages( + gcx.clone(), + self.messages.clone(), + &self.post.meta, + &mut self.has_rag_results, + get_available_tools_by_chat_mode(gcx.clone(), self.post.meta.chat_mode) + .await + .into_iter() + .map(|t| t.tool_description().name) + .collect(), + ).await } else { self.messages.clone() }; diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index bd8f238be..a79c1323e 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -123,7 +123,13 @@ impl ScratchpadAbstract for ChatPassthrough { }; let messages = if self.prepend_system_prompt && self.allow_at { - prepend_the_right_system_prompt_and_maybe_more_initial_messages(gcx.clone(), self.messages.clone(), &self.post.meta, &mut self.has_rag_results).await + prepend_the_right_system_prompt_and_maybe_more_initial_messages( + gcx.clone(), + self.messages.clone(), + &self.post.meta, + &mut self.has_rag_results, + self.tools.iter().map(|x| x.name.clone()).collect(), + ).await } else { self.messages.clone() }; diff --git a/refact-agent/engine/src/scratchpads/chat_utils_prompts.rs b/refact-agent/engine/src/scratchpads/chat_utils_prompts.rs index 03da191ac..d679340e0 100644 --- a/refact-agent/engine/src/scratchpads/chat_utils_prompts.rs +++ b/refact-agent/engine/src/scratchpads/chat_utils_prompts.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::fs; use std::sync::Arc; use std::path::PathBuf; @@ -117,7 +118,8 @@ async fn _read_project_summary( pub async fn system_prompt_add_extra_instructions( gcx: Arc>, - system_prompt: &String, + system_prompt: String, + tool_names: HashSet, ) -> String { async fn workspace_files_info(gcx: &Arc>) -> (Vec, Option) { let gcx_locked = gcx.read().await; @@ -174,6 +176,56 @@ pub async fn system_prompt_add_extra_instructions( } } + if system_prompt.contains("%EXPLORE_FILE_EDIT_INSTRUCTIONS%") { + let replacement = if tool_names.contains("create_textdoc") || tool_names.contains("update_textdoc") { + "- Then use `*_textdoc()` tools to make changes.\n" + } else { + "" + }; + + system_prompt = system_prompt.replace("%EXPLORE_FILE_EDIT_INSTRUCTIONS%", replacement); + } + + if system_prompt.contains("%AGENT_EXPLORATION_INSTRUCTIONS%") { + let replacement = if tool_names.contains("locate") { + "- Call `locate()` tool to find relevant files.\n" + } else { + "- Call available tools to find relevant files.\n" + }; + + system_prompt = system_prompt.replace("%AGENT_EXPLORATION_INSTRUCTIONS%", replacement); + } + + if system_prompt.contains("%AGENT_EXECUTION_INSTRUCTIONS%") { + let replacement = if tool_names.contains("create_textdoc") || tool_names.contains("update_textdoc") { +"3. Confirm the Plan with the User — No Coding Until Approved + - Post a concise, bullet-point summary that includes + • the suspected root cause + • the exact files/functions you will modify or create + • the new or updated tests you will add + • the expected outcome and success criteria + - Explicitly ask “Does this align with your vision? + - Wait for the user’s approval or revisions before proceeding. +4. Implement the Fix + - Apply the approved changes directly to project files using `update_textdoc()` and `create_textdoc()` tools. +5. Validate and Improve + - Run all available tooling to ensure the project compiles and your fix works. + - Add or update tests that reproduce the original bug and verify they pass. + - Execute the full test suite to guard against regressions. + - Iterate until everything is green. +" + } else { +" - Propose the changes to the user + • the suspected root cause + • the exact files/functions to modify or create + • the new or updated tests to add + • the expected outcome and success criteria +" + }; + + system_prompt = system_prompt.replace("%AGENT_EXECUTION_INSTRUCTIONS%", replacement); + } + system_prompt } @@ -182,6 +234,7 @@ pub async fn prepend_the_right_system_prompt_and_maybe_more_initial_messages( mut messages: Vec, chat_meta: &call_validation::ChatMeta, stream_back_to_user: &mut HasRagResults, + tool_names: HashSet, ) -> Vec { let have_system = !messages.is_empty() && messages[0].role == "system"; if have_system { @@ -206,8 +259,10 @@ pub async fn prepend_the_right_system_prompt_and_maybe_more_initial_messages( match chat_meta.chat_mode { ChatMode::EXPLORE | ChatMode::AGENT | ChatMode::NO_TOOLS => { - let system_message_content = system_prompt_add_extra_instructions(gcx.clone(), - &get_default_system_prompt(gcx.clone(), chat_meta.chat_mode.clone()).await + let system_message_content = system_prompt_add_extra_instructions( + gcx.clone(), + get_default_system_prompt(gcx.clone(), chat_meta.chat_mode.clone()).await, + tool_names, ).await; let msg = ChatMessage { role: "system".to_string(), diff --git a/refact-agent/engine/src/yaml_configs/customization_compiled_in.yaml b/refact-agent/engine/src/yaml_configs/customization_compiled_in.yaml index a97444a55..a2937cc8b 100644 --- a/refact-agent/engine/src/yaml_configs/customization_compiled_in.yaml +++ b/refact-agent/engine/src/yaml_configs/customization_compiled_in.yaml @@ -62,7 +62,7 @@ PROMPT_EXPLORATION_TOOLS: | - Gather the necessary context using available exploration tools, or follow the user’s instructions. - Ask clarifying questions if needed, making as many iterations as necessary to refine the context. - After gathering context, propose required project changes. - - Then use `*_textdoc()` tools to make changes. + %EXPLORE_FILE_EDIT_INSTRUCTIONS% - **If no**: - Answer the question directly without calling any tools. @@ -75,29 +75,15 @@ PROMPT_EXPLORATION_TOOLS: | PROMPT_AGENTIC_TOOLS: | [mode3] You are a fully autonomous agent for coding tasks. - Your task is to identify and solve the problem from the given PR by directly changing files in the given project. + Your task is to identify and solve the problem by directly changing files in the given project. You must follow the strategy, step by step in the given order without skipping. You must confirm the plan with the user before proceeding! 1. Explore the Problem - - Call `locate()` tool to find relevant files. + %AGENT_EXPLORATION_INSTRUCTIONS% 2. Draft the Solution Plan - Identify the root cause and sketch the required code changes (files to touch, functions to edit, tests to add). - 3. Confirm the Plan with the User — No Coding Until Approved - - Post a concise, bullet-point summary that includes - • the suspected root cause - • the exact files/functions you will modify or create - • the new or updated tests you will add - • the expected outcome and success criteria - - Explicitly ask “Does this align with your vision? - - Wait for the user’s approval or revisions before proceeding. - 4. Implement the Fix - - Apply the approved changes directly to project files using `update_textdoc()` and `create_textdoc()` tools. - 5. Validate and Improve - - Run all available tooling to ensure the project compiles and your fix works. - - Add or update tests that reproduce the original bug and verify they pass. - - Execute the full test suite to guard against regressions. - - Iterate until everything is green. + %AGENT_EXECUTION_INSTRUCTIONS% **BEST PRACTICES** - %CD_INSTRUCTIONS% From be6e41e48c995de49e843c464ce938e8b20f19e0 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 22 May 2025 15:40:35 +0200 Subject: [PATCH 84/90] feat: mcp tools partition by server --- refact-agent/engine/src/tools/tools_list.rs | 52 ++++++++++++++------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index d222ad23b..18f5a2d92 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock as ARwLock; @@ -155,30 +156,47 @@ async fn get_builtin_tools( async fn get_integration_tools( gcx: Arc>, ) -> Vec { - let mut tool_groups = vec![ - ToolGroup { - name: "integrations".to_string(), - description: "Integration tools".to_string(), - category: ToolGroupCategory::Integration, - tools: vec![], - }, - ToolGroup { - name: "mcp".to_string(), - description: "MCP tools".to_string(), - category: ToolGroupCategory::MCP, - tools: vec![], - }, - ]; + let mut integrations_group = ToolGroup { + name: "Integrations".to_string(), + description: "Integration tools".to_string(), + category: ToolGroupCategory::Integration, + tools: vec![], + }; + + let mut mcp_groups = HashMap::new(); + let (integrations_map, _yaml_errors) = load_integrations(gcx.clone(), &["**/*".to_string()]).await; for (name, integr) in integrations_map { for tool in integr.integr_tools(&name).await { - if tool.tool_description().name.starts_with("mcp") { - tool_groups[1].tools.push(tool); + let tool_desc = tool.tool_description(); + if tool_desc.name.starts_with("mcp") { + let mcp_server_name = std::path::Path::new(&tool_desc.source.config_path) + .file_stem() + .and_then(|name| name.to_str()) + .unwrap_or("unknown"); + + if !mcp_groups.contains_key(mcp_server_name) { + mcp_groups.insert( + mcp_server_name.to_string(), + ToolGroup { + name: format!("MCP {}", mcp_server_name), + description: format!("MCP tools for {}", mcp_server_name), + category: ToolGroupCategory::Integration, + tools: vec![], + }, + ); + } + mcp_groups.entry(mcp_server_name.to_string()) + .and_modify(|group| group.tools.push(tool)); } else { - tool_groups[0].tools.push(tool); + integrations_group.tools.push(tool); } } } + + let mut tool_groups = vec![integrations_group]; + tool_groups.extend(mcp_groups.into_values()); + for tool_group in tool_groups.iter_mut() { tool_group.retain_available_tools(gcx.clone()).await; } From 8b1a650df74be6f105e6ccb032bc69c989317c18 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 22 May 2025 15:48:50 +0200 Subject: [PATCH 85/90] doc: better doc comment --- refact-agent/engine/src/global_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refact-agent/engine/src/global_context.rs b/refact-agent/engine/src/global_context.rs index 745310c06..538625787 100644 --- a/refact-agent/engine/src/global_context.rs +++ b/refact-agent/engine/src/global_context.rs @@ -35,7 +35,7 @@ pub struct CommandLine { #[structopt(long, default_value="", help="Send logs to a file.")] pub logs_to_file: String, #[structopt(long, short="u", default_value="", help="URL to use: \"Refact\" for Cloud, or your Self-Hosted Server URL. To bring your own keys, use \"Refact\" and set up providers.")] - /// URL to use: "Refact" for Cloud, or your Self-Hosted Server URL. To bring your own keys, use "Refact" and set up providers. + /// Inference server URL, or "Refact" for cloud pub address_url: String, #[structopt(long, short="k", default_value="", help="The API key to authenticate your requests, will appear in HTTP requests this binary makes.")] pub api_key: String, From 13e08df1359d6ec54118001a4d32d2f66f59ac58 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Thu, 22 May 2025 16:29:45 +0200 Subject: [PATCH 86/90] refactor: chat passthrough ask for new tools only if needed --- .../engine/src/scratchpads/chat_passthrough.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index a79c1323e..19993e8a4 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -114,13 +114,6 @@ impl ScratchpadAbstract for ChatPassthrough { (ccx_locked.global_context.clone(), ccx_locked.n_ctx, ccx_locked.should_execute_remotely) }; let style = self.post.style.clone(); - let mut at_tools = if !should_execute_remotely { - get_available_tools(gcx.clone()).await.into_iter().map(|x| { - (x.tool_description().name, x) - }).collect() - } else { - IndexMap::new() - }; let messages = if self.prepend_system_prompt && self.allow_at { prepend_the_right_system_prompt_and_maybe_more_initial_messages( @@ -144,7 +137,9 @@ impl ScratchpadAbstract for ChatPassthrough { (messages, _) = if should_execute_remotely { run_tools_remotely(ccx.clone(), &self.post.model, sampling_parameters_to_patch.max_new_tokens, &messages, &mut self.has_rag_results, &style).await? } else { - run_tools_locally(ccx.clone(), &mut at_tools, self.t.tokenizer.clone(), sampling_parameters_to_patch.max_new_tokens, &messages, &mut self.has_rag_results, &style).await? + let mut tools = get_available_tools(gcx.clone()).await.into_iter() + .map(|x| (x.tool_description().name, x)).collect(); + run_tools_locally(ccx.clone(), &mut tools, self.t.tokenizer.clone(), sampling_parameters_to_patch.max_new_tokens, &messages, &mut self.has_rag_results, &style).await? } }; From b430552b6877556c58b127c3ba470894c5058b7f Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Tue, 27 May 2025 22:02:06 +0200 Subject: [PATCH 87/90] chore: MCP tool group category for MCP tool groups instead of integrations --- refact-agent/engine/src/tools/tools_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refact-agent/engine/src/tools/tools_list.rs b/refact-agent/engine/src/tools/tools_list.rs index 18f5a2d92..185db2bdf 100644 --- a/refact-agent/engine/src/tools/tools_list.rs +++ b/refact-agent/engine/src/tools/tools_list.rs @@ -181,7 +181,7 @@ async fn get_integration_tools( ToolGroup { name: format!("MCP {}", mcp_server_name), description: format!("MCP tools for {}", mcp_server_name), - category: ToolGroupCategory::Integration, + category: ToolGroupCategory::MCP, tools: vec![], }, ); From f08e8ab3d0c4998c5ded0411b2d7700537ff7b98 Mon Sep 17 00:00:00 2001 From: MDario123 Date: Wed, 28 May 2025 15:33:11 +0200 Subject: [PATCH 88/90] fix: remove non existent param from function call artifact from rebase --- refact-agent/engine/src/files_in_workspace.rs | 4 +--- .../engine/src/integrations/setting_up_integrations.rs | 3 +-- refact-agent/engine/src/main.rs | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/refact-agent/engine/src/files_in_workspace.rs b/refact-agent/engine/src/files_in_workspace.rs index b4896aa36..b10beb11e 100644 --- a/refact-agent/engine/src/files_in_workspace.rs +++ b/refact-agent/engine/src/files_in_workspace.rs @@ -652,8 +652,6 @@ pub async fn on_workspaces_init(gcx: Arc>) -> i32 { // Called from lsp and lsp_like // Not called from main.rs as part of initialization - let allow_experimental = gcx.read().await.cmdline.experimental; - watcher_init(gcx.clone()).await; let files_enqueued = enqueue_all_files_from_workspace_folders(gcx.clone(), false, false).await; @@ -663,7 +661,7 @@ pub async fn on_workspaces_init(gcx: Arc>) -> i32 }); // Start or connect to mcp servers - let _ = load_integrations(gcx.clone(), allow_experimental, &["**/mcp_*".to_string()]).await; + let _ = load_integrations(gcx.clone(), &["**/mcp_*".to_string()]).await; files_enqueued } diff --git a/refact-agent/engine/src/integrations/setting_up_integrations.rs b/refact-agent/engine/src/integrations/setting_up_integrations.rs index 10e4d4a45..956bd63a3 100644 --- a/refact-agent/engine/src/integrations/setting_up_integrations.rs +++ b/refact-agent/engine/src/integrations/setting_up_integrations.rs @@ -538,7 +538,6 @@ pub async fn integration_config_save( integr_config_path: &String, integr_values: &serde_json::Value, ) -> Result<(), String> { - let allow_experimental = gcx.read().await.cmdline.experimental; let config_path = crate::files_correction::canonical_path(integr_config_path); let (integr_name, _project_path) = crate::integrations::setting_up_integrations::split_path_into_project_and_integration(&config_path) .map_err(|e| format!("Failed to split path: {}", e))?; @@ -574,7 +573,7 @@ pub async fn integration_config_save( // If it is an mcp integration, ensure we restart or reconnect to the server if config_path.file_name().and_then(|f| f.to_str()).is_some_and(|f| f.starts_with("mcp_")) { - let _ = load_integrations(gcx.clone(), allow_experimental, &["**/mcp_*".to_string()]).await; + let _ = load_integrations(gcx.clone(), &["**/mcp_*".to_string()]).await; } Ok(()) diff --git a/refact-agent/engine/src/main.rs b/refact-agent/engine/src/main.rs index 39faa584e..1ca170307 100644 --- a/refact-agent/engine/src/main.rs +++ b/refact-agent/engine/src/main.rs @@ -179,7 +179,7 @@ async fn main() { }); // Start or connect to mcp servers - let _ = running_integrations::load_integrations(gcx.clone(), cmdline.experimental, &["**/mcp_*".to_string()]).await; + let _ = running_integrations::load_integrations(gcx.clone(), &["**/mcp_*".to_string()]).await; // not really needed, but it's nice to have an error message sooner if there's one let _caps = crate::global_context::try_load_caps_quickly_if_not_present(gcx.clone(), 0).await; From ef45eaa3f458e0e3e2ea4e992ec182af550e345e Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 28 May 2025 17:09:25 +0200 Subject: [PATCH 89/90] chore: updated package-lock.json --- refact-agent/gui/package-lock.json | 59 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/refact-agent/gui/package-lock.json b/refact-agent/gui/package-lock.json index bc7bd89e0..608e0f8aa 100644 --- a/refact-agent/gui/package-lock.json +++ b/refact-agent/gui/package-lock.json @@ -21,7 +21,6 @@ "react-redux": "^9.1.2", "urql": "^4.2.2", "zod": "^3.25.20" - "react-redux": "^9.1.2" }, "devDependencies": { "@0no-co/graphqlsp": "^1.12.16", @@ -4879,6 +4878,26 @@ "@parcel/watcher-win32-x64": "2.5.1" } }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@parcel/watcher-android-arm64": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", @@ -16242,13 +16261,13 @@ } }, "node_modules/framer-motion": { - "version": "12.10.4", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.10.4.tgz", - "integrity": "sha512-gGkavMW3QFDCxI+adVu5sn2NtIRHYPGVLDSJ0S/6B0ZoxPaql2F9foWdg9sGIP6sPA8cbNDfxYf9VlhD3+FkVQ==", + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.15.0.tgz", + "integrity": "sha512-XKg/LnKExdLGugZrDILV7jZjI599785lDIJZLxMiiIFidCsy0a4R2ZEf+Izm67zyOuJgQYTHOmodi7igQsw3vg==", "license": "MIT", "dependencies": { - "motion-dom": "^12.10.4", - "motion-utils": "^12.9.4", + "motion-dom": "^12.15.0", + "motion-utils": "^12.12.1", "tslib": "^2.4.0" }, "peerDependencies": { @@ -16333,20 +16352,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -21288,18 +21293,18 @@ "dev": true }, "node_modules/motion-dom": { - "version": "12.10.4", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.10.4.tgz", - "integrity": "sha512-GSv8kz0ANFfeGrFKi99s3GQjLiL1IKH3KtSNEqrPiVbloHVRiNbNtpsYQq9rkV2AV+7jxvd1X1ObUMVDnAEnXA==", + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.15.0.tgz", + "integrity": "sha512-D2ldJgor+2vdcrDtKJw48k3OddXiZN1dDLLWrS8kiHzQdYVruh0IoTwbJBslrnTXIPgFED7PBN2Zbwl7rNqnhA==", "license": "MIT", "dependencies": { - "motion-utils": "^12.9.4" + "motion-utils": "^12.12.1" } }, "node_modules/motion-utils": { - "version": "12.9.4", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.9.4.tgz", - "integrity": "sha512-BW3I65zeM76CMsfh3kHid9ansEJk9Qvl+K5cu4DVHKGsI52n76OJ4z2CUJUV+Mn3uEP9k1JJA3tClG0ggSrRcg==", + "version": "12.12.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.12.1.tgz", + "integrity": "sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w==", "license": "MIT" }, "node_modules/mrmime": { From 605764bfde3d07e6f31d7a5a412929a48706788d Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 28 May 2025 17:15:10 +0200 Subject: [PATCH 90/90] chore: fixed linting --- refact-agent/gui/package-lock.json | 20 ------------------- refact-agent/gui/src/services/refact/tools.ts | 4 +--- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/refact-agent/gui/package-lock.json b/refact-agent/gui/package-lock.json index 608e0f8aa..251cac3ab 100644 --- a/refact-agent/gui/package-lock.json +++ b/refact-agent/gui/package-lock.json @@ -4878,26 +4878,6 @@ "@parcel/watcher-win32-x64": "2.5.1" } }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@parcel/watcher-android-arm64": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", diff --git a/refact-agent/gui/src/services/refact/tools.ts b/refact-agent/gui/src/services/refact/tools.ts index 50e1e4b55..1411b06e0 100644 --- a/refact-agent/gui/src/services/refact/tools.ts +++ b/refact-agent/gui/src/services/refact/tools.ts @@ -51,9 +51,7 @@ export const toolsApi = createApi({ }, }; } - const toolGroups = result.data.filter((d) => - isToolGroup(d), - ) as ToolGroup[]; + const toolGroups = result.data.filter((d) => isToolGroup(d)); return { data: toolGroups }; }, }),