From 7918b5bda169f329d7866d0341546ca5c102fd20 Mon Sep 17 00:00:00 2001 From: angiejones Date: Tue, 23 Dec 2025 10:35:37 -0600 Subject: [PATCH] remove Tool Selection Strategy preview --- crates/goose-cli/src/commands/configure.rs | 35 ---- crates/goose-cli/src/session/builder.rs | 2 - crates/goose-server/src/openapi.rs | 2 - crates/goose-server/src/routes/agent.rs | 38 ---- crates/goose/src/agents/agent.rs | 86 +------- .../src/agents/code_execution_extension.rs | 2 - crates/goose/src/agents/extension.rs | 2 - crates/goose/src/agents/extension_manager.rs | 1 - .../src/agents/extension_manager_extension.rs | 77 +------- crates/goose/src/agents/mod.rs | 4 - crates/goose/src/agents/prompt_manager.rs | 13 -- crates/goose/src/agents/reply_parts.rs | 23 +-- .../goose/src/agents/router_tool_selector.rs | 180 ----------------- crates/goose/src/agents/router_tools.rs | 65 ------ ..._agents__prompt_manager__tests__basic.snap | 2 - ..._prompt_manager__tests__one_extension.snap | 15 -- ..._prompt_manager__tests__typical_setup.snap | 15 -- crates/goose/src/agents/tool_route_manager.rs | 185 ------------------ .../src/agents/tool_router_index_manager.rs | 76 ------- crates/goose/src/execution/manager.rs | 1 - crates/goose/src/posthog.rs | 5 - .../goose/src/prompts/router_tool_selector.md | 11 -- crates/goose/src/prompts/system.md | 2 - crates/goose/tests/agent.rs | 1 - .../docs/guides/environment-variables.md | 4 - .../docs/guides/managing-tools/index.md | 5 - .../docs/guides/managing-tools/tool-router.md | 163 --------------- ui/desktop/openapi.json | 50 ----- ui/desktop/src/api/sdk.gen.ts | 11 +- ui/desktop/src/api/types.gen.ts | 35 ---- .../settings/chat/ChatSettingsSection.tsx | 14 -- .../ToolSelectionStrategySection.tsx | 145 -------------- 32 files changed, 9 insertions(+), 1261 deletions(-) delete mode 100644 crates/goose/src/agents/router_tool_selector.rs delete mode 100644 crates/goose/src/agents/router_tools.rs delete mode 100644 crates/goose/src/agents/tool_route_manager.rs delete mode 100644 crates/goose/src/agents/tool_router_index_manager.rs delete mode 100644 crates/goose/src/prompts/router_tool_selector.md delete mode 100644 documentation/docs/guides/managing-tools/tool-router.md delete mode 100644 ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx diff --git a/crates/goose-cli/src/commands/configure.rs b/crates/goose-cli/src/commands/configure.rs index b6ae23df2674..365e2e3f23d0 100644 --- a/crates/goose-cli/src/commands/configure.rs +++ b/crates/goose-cli/src/commands/configure.rs @@ -1154,11 +1154,6 @@ pub fn remove_extension_dialog() -> anyhow::Result<()> { pub async fn configure_settings_dialog() -> anyhow::Result<()> { let setting_type = cliclack::select("What setting would you like to configure?") .item("goose_mode", "goose mode", "Configure goose mode") - .item( - "goose_router_strategy", - "Router Tool Selection Strategy", - "Experimental: configure a strategy for auto selecting tools to use", - ) .item( "tool_permission", "Tool Permission", @@ -1197,9 +1192,6 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> { "goose_mode" => { configure_goose_mode_dialog()?; } - "goose_router_strategy" => { - configure_goose_router_strategy_dialog()?; - } "tool_permission" => { configure_tool_permissions_dialog().await.and(Ok(()))?; // No need to print config file path since it's already handled. @@ -1271,33 +1263,6 @@ pub fn configure_goose_mode_dialog() -> anyhow::Result<()> { Ok(()) } -pub fn configure_goose_router_strategy_dialog() -> anyhow::Result<()> { - let config = Config::global(); - - let enable_router = cliclack::select("Would you like to enable smart tool routing?") - .item( - true, - "Enable Router", - "Use LLM-based intelligence to select tools", - ) - .item( - false, - "Disable Router", - "Use the default tool selection strategy", - ) - .interact()?; - - config.set_param("GOOSE_ENABLE_ROUTER", enable_router)?; - let msg = if enable_router { - "Router enabled - using LLM-based intelligence for tool selection" - } else { - "Router disabled - using default tool selection" - }; - cliclack::outro(msg)?; - - Ok(()) -} - pub fn configure_tool_output_dialog() -> anyhow::Result<()> { let config = Config::global(); diff --git a/crates/goose-cli/src/session/builder.rs b/crates/goose-cli/src/session/builder.rs index f7771af20d64..049f48e7a24f 100644 --- a/crates/goose-cli/src/session/builder.rs +++ b/crates/goose-cli/src/session/builder.rs @@ -386,7 +386,6 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> CliSession { .set_context(PlatformExtensionContext { session_id: Some(session_id.clone()), extension_manager: Some(Arc::downgrade(&agent.extension_manager)), - tool_route_manager: Some(Arc::downgrade(&agent.tool_route_manager)), }) .await; @@ -425,7 +424,6 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> CliSession { // Extensions need to be added after the session is created because we change directory when resuming a session // If we get extensions_override, only run those extensions and none other let extensions_to_run: Vec<_> = if let Some(extensions) = session_config.extensions_override { - agent.disable_router_for_recipe().await; extensions.into_iter().collect() } else if session_config.resume { match SessionManager::get_session(&session_id, false).await { diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index 88ab9d9a8810..22ee8492a171 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -361,7 +361,6 @@ derive_utoipa!(Icon as IconSchema); super::routes::agent::agent_add_extension, super::routes::agent::agent_remove_extension, super::routes::agent::update_agent_provider, - super::routes::agent::update_router_tool_selector, super::routes::action_required::confirm_tool_action, super::routes::reply::reply, super::routes::session::list_sessions, @@ -525,7 +524,6 @@ derive_utoipa!(Icon as IconSchema); super::routes::agent::ReadResourceResponse, super::routes::agent::CallToolRequest, super::routes::agent::CallToolResponse, - super::routes::agent::UpdateRouterToolSelectorRequest, super::routes::agent::StartAgentRequest, super::routes::agent::ResumeAgentRequest, super::routes::agent::UpdateFromSessionRequest, diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index ce2d507068c3..3f8786502ad6 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -53,11 +53,6 @@ pub struct GetToolsQuery { session_id: String, } -#[derive(Deserialize, utoipa::ToSchema)] -pub struct UpdateRouterToolSelectorRequest { - session_id: String, -} - #[derive(Deserialize, utoipa::ToSchema)] pub struct StartAgentRequest { working_dir: String, @@ -501,35 +496,6 @@ async fn update_agent_provider( Ok(()) } -#[utoipa::path( - post, - path = "/agent/update_router_tool_selector", - request_body = UpdateRouterToolSelectorRequest, - responses( - (status = 200, description = "Tool selection strategy updated successfully", body = String), - (status = 401, description = "Unauthorized - invalid secret key"), - (status = 424, description = "Agent not initialized"), - (status = 500, description = "Internal server error") - ) -)] -async fn update_router_tool_selector( - State(state): State>, - Json(payload): Json, -) -> Result, StatusCode> { - let agent = state.get_agent_for_route(payload.session_id).await?; - agent - .update_router_tool_selector(None, Some(true)) - .await - .map_err(|e| { - error!("Failed to update tool selection strategy: {}", e); - StatusCode::INTERNAL_SERVER_ERROR - })?; - - Ok(Json( - "Tool selection strategy updated successfully".to_string(), - )) -} - #[utoipa::path( post, path = "/agent/add_extension", @@ -695,10 +661,6 @@ pub fn routes(state: Arc) -> Router { .route("/agent/read_resource", post(read_resource)) .route("/agent/call_tool", post(call_tool)) .route("/agent/update_provider", post(update_agent_provider)) - .route( - "/agent/update_router_tool_selector", - post(update_router_tool_selector), - ) .route("/agent/update_from_session", post(update_from_session)) .route("/agent/add_extension", post(agent_add_extension)) .route("/agent/remove_extension", post(agent_remove_extension)) diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index b0e3e530cbba..2dd5503b8b67 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -12,20 +12,17 @@ use super::final_output_tool::FinalOutputTool; use super::platform_tools; use super::tool_execution::{ToolCallResult, CHAT_MODE_TOOL_SKIPPED_RESPONSE, DECLINED_RESPONSE}; use crate::action_required_manager::ActionRequiredManager; -use crate::agents::extension::{ExtensionConfig, ExtensionError, ExtensionResult, ToolInfo}; +use crate::agents::extension::{ExtensionConfig, ExtensionResult, ToolInfo}; use crate::agents::extension_manager::{get_parameter_names, ExtensionManager}; use crate::agents::extension_manager_extension::MANAGE_EXTENSIONS_TOOL_NAME_COMPLETE; use crate::agents::final_output_tool::{FINAL_OUTPUT_CONTINUATION_MESSAGE, FINAL_OUTPUT_TOOL_NAME}; use crate::agents::platform_tools::PLATFORM_MANAGE_SCHEDULE_TOOL_NAME; use crate::agents::prompt_manager::PromptManager; use crate::agents::retry::{RetryManager, RetryResult}; -use crate::agents::router_tools::ROUTER_LLM_SEARCH_TOOL_NAME; use crate::agents::subagent_task_config::TaskConfig; use crate::agents::subagent_tool::{ create_subagent_tool, handle_subagent_tool, SUBAGENT_TOOL_NAME, }; -use crate::agents::tool_route_manager::ToolRouteManager; -use crate::agents::tool_router_index_manager::ToolRouterIndexManager; use crate::agents::types::SessionConfig; use crate::agents::types::{FrontendTool, SharedProvider, ToolResultReceiver}; use crate::config::{get_enabled_extensions, Config, GooseMode}; @@ -95,7 +92,6 @@ pub struct Agent { pub(super) tool_result_tx: mpsc::Sender<(String, ToolResult)>, pub(super) tool_result_rx: ToolResultReceiver, - pub tool_route_manager: Arc, pub(super) scheduler_service: Mutex>>, pub(super) retry_manager: RetryManager, pub(super) tool_inspection_manager: ToolInspectionManager, @@ -169,7 +165,6 @@ impl Agent { confirmation_rx: Mutex::new(confirm_rx), tool_result_tx: tool_tx, tool_result_rx: Arc::new(Mutex::new(tool_rx)), - tool_route_manager: Arc::new(ToolRouteManager::new()), scheduler_service: Mutex::new(None), retry_manager: RetryManager::new(), tool_inspection_manager: Self::create_default_tool_inspection_manager(), @@ -369,10 +364,6 @@ impl Agent { *scheduler_service = Some(scheduler); } - pub async fn disable_router_for_recipe(&self) { - self.tool_route_manager.disable_router_for_recipe().await; - } - /// Get a reference count clone to the provider pub async fn provider(&self) -> Result, anyhow::Error> { match &*self.provider.lock().await { @@ -518,15 +509,6 @@ impl Agent { "Frontend tool execution required".to_string(), None, ))) - } else if tool_call.name == ROUTER_LLM_SEARCH_TOOL_NAME { - match self - .tool_route_manager - .dispatch_route_search_tool(tool_call.arguments.unwrap_or_default()) - .await - { - Ok(tool_result) => tool_result, - Err(e) => return (request_id, Err(e)), - } } else { // Clone the result to ensure no references to extension_manager are returned let result = self @@ -617,28 +599,6 @@ impl Agent { } } - // If LLM tool selection is functional, index the tools - if self.tool_route_manager.is_router_functional().await { - let selector = self.tool_route_manager.get_router_tool_selector().await; - if let Some(selector) = selector { - let selector = Arc::new(selector); - if let Err(e) = ToolRouterIndexManager::update_extension_tools( - &selector, - &self.extension_manager, - &extension.name(), - "add", - ) - .await - { - return Err(ExtensionError::SetupError(format!( - "Failed to index tools for extension {}: {}", - extension.name(), - e - ))); - } - } - } - Ok(()) } @@ -702,29 +662,8 @@ impl Agent { prefixed_tools } - pub async fn list_tools_for_router(&self) -> Vec { - self.tool_route_manager - .list_tools_for_router(&self.extension_manager) - .await - } - pub async fn remove_extension(&self, name: &str) -> Result<()> { self.extension_manager.remove_extension(name).await?; - - // If LLM tool selection is functional, remove tools from the index - if self.tool_route_manager.is_router_functional().await { - let selector = self.tool_route_manager.get_router_tool_selector().await; - if let Some(selector) = selector { - ToolRouterIndexManager::update_extension_tools( - &selector, - &self.extension_manager, - name, - "remove", - ) - .await?; - } - } - Ok(()) } @@ -1052,10 +991,6 @@ impl Agent { remaining_requests, filtered_response, } = self.categorize_tools(&response, &tools).await; - let requests_to_record: Vec = frontend_requests.iter().chain(remaining_requests.iter()).cloned().collect(); - self.tool_route_manager - .record_tool_requests(&requests_to_record) - .await; yield AgentEvent::Message(filtered_response.clone()); tokio::task::yield_now().await; @@ -1377,9 +1312,6 @@ impl Agent { let mut current_provider = self.provider.lock().await; *current_provider = Some(provider.clone()); - self.update_router_tool_selector(Some(provider.clone()), None) - .await?; - SessionManager::update_session(session_id) .provider_name(provider.get_name()) .model_config(provider.get_model_config()) @@ -1388,22 +1320,6 @@ impl Agent { .context("Failed to persist provider config to session") } - pub async fn update_router_tool_selector( - &self, - provider: Option>, - reindex_all: Option, - ) -> Result<()> { - let provider = match provider { - Some(p) => p, - None => self.provider().await?, - }; - - // Delegate to ToolRouteManager - self.tool_route_manager - .update_router_tool_selector(provider, reindex_all, &self.extension_manager) - .await - } - /// Override the system prompt with a custom template pub async fn override_system_prompt(&self, template: String) { let mut prompt_manager = self.prompt_manager.lock().await; diff --git a/crates/goose/src/agents/code_execution_extension.rs b/crates/goose/src/agents/code_execution_extension.rs index c2f6742553fe..b041addc4cd7 100644 --- a/crates/goose/src/agents/code_execution_extension.rs +++ b/crates/goose/src/agents/code_execution_extension.rs @@ -923,7 +923,6 @@ mod tests { let context = PlatformExtensionContext { session_id: None, extension_manager: None, - tool_route_manager: None, }; let client = CodeExecutionClient::new(context).unwrap(); @@ -948,7 +947,6 @@ mod tests { let context = PlatformExtensionContext { session_id: None, extension_manager: None, - tool_route_manager: None, }; let client = CodeExecutionClient::new(context).unwrap(); diff --git a/crates/goose/src/agents/extension.rs b/crates/goose/src/agents/extension.rs index 3287a40b06e2..eedb109f605a 100644 --- a/crates/goose/src/agents/extension.rs +++ b/crates/goose/src/agents/extension.rs @@ -109,8 +109,6 @@ pub struct PlatformExtensionContext { pub session_id: Option, pub extension_manager: Option>, - pub tool_route_manager: - Option>, } #[derive(Debug, Clone)] diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index 9110fb167d18..e9c8ce1c3ae8 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -264,7 +264,6 @@ impl ExtensionManager { context: Mutex::new(PlatformExtensionContext { session_id: None, extension_manager: None, - tool_route_manager: None, }), provider, } diff --git a/crates/goose/src/agents/extension_manager_extension.rs b/crates/goose/src/agents/extension_manager_extension.rs index 099ec70b6f57..8f751af6d1e5 100644 --- a/crates/goose/src/agents/extension_manager_extension.rs +++ b/crates/goose/src/agents/extension_manager_extension.rs @@ -1,6 +1,5 @@ use crate::agents::extension::PlatformExtensionContext; use crate::agents::mcp_client::{Error, McpClientTrait}; -use crate::agents::tool_router_index_manager::ToolRouterIndexManager; use crate::config::get_extension_by_name; use anyhow::Result; use async_trait::async_trait; @@ -163,7 +162,6 @@ impl ExtensionManagerClient { } } - #[allow(clippy::too_many_lines)] async fn manage_extensions_impl( &self, action: ManageExtensionAction, @@ -182,43 +180,8 @@ impl ExtensionManagerClient { ) })?; - let tool_route_manager = self - .context - .tool_route_manager - .as_ref() - .and_then(|weak| weak.upgrade()); - - // Update tool router index if router is functional - if let Some(tool_route_manager) = &tool_route_manager { - if tool_route_manager.is_router_functional().await { - let selector = tool_route_manager.get_router_tool_selector().await; - if let Some(selector) = selector { - let selector_action = if action == ManageExtensionAction::Disable { - "remove" - } else { - "add" - }; - let selector = Arc::new(selector); - if let Err(e) = ToolRouterIndexManager::update_extension_tools( - &selector, - &extension_manager, - &extension_name, - selector_action, - ) - .await - { - return Err(ErrorData::new( - ErrorCode::INTERNAL_ERROR, - format!("Failed to update LLM index: {}", e), - None, - )); - } - } - } - } - if action == ManageExtensionAction::Disable { - let result = extension_manager + return extension_manager .remove_extension(&extension_name) .await .map(|_| { @@ -228,7 +191,6 @@ impl ExtensionManagerClient { ))] }) .map_err(|e| ErrorData::new(ErrorCode::INTERNAL_ERROR, e.to_string(), None)); - return result; } let config = match get_extension_by_name(&extension_name) { @@ -245,7 +207,7 @@ impl ExtensionManagerClient { } }; - let result = extension_manager + extension_manager .add_extension(config) .await .map(|_| { @@ -254,40 +216,7 @@ impl ExtensionManagerClient { extension_name ))] }) - .map_err(|e| ErrorData::new(ErrorCode::INTERNAL_ERROR, e.to_string(), None)); - - // Update LLM index if operation was successful and LLM routing is functional - if result.is_ok() { - if let Some(tool_route_manager) = &tool_route_manager { - if tool_route_manager.is_router_functional().await { - let selector = tool_route_manager.get_router_tool_selector().await; - if let Some(selector) = selector { - let llm_action = if action == ManageExtensionAction::Disable { - "remove" - } else { - "add" - }; - let selector = Arc::new(selector); - if let Err(e) = ToolRouterIndexManager::update_extension_tools( - &selector, - &extension_manager, - &extension_name, - llm_action, - ) - .await - { - return Err(ErrorData::new( - ErrorCode::INTERNAL_ERROR, - format!("Failed to update LLM index: {}", e), - None, - )); - } - } - } - } - } - - result + .map_err(|e| ErrorData::new(ErrorCode::INTERNAL_ERROR, e.to_string(), None)) } async fn handle_list_resources( diff --git a/crates/goose/src/agents/mod.rs b/crates/goose/src/agents/mod.rs index ceddd2bd1f65..0384990594eb 100644 --- a/crates/goose/src/agents/mod.rs +++ b/crates/goose/src/agents/mod.rs @@ -14,8 +14,6 @@ pub mod platform_tools; pub mod prompt_manager; mod reply_parts; pub mod retry; -mod router_tool_selector; -mod router_tools; mod schedule_tool; pub(crate) mod skills_extension; pub mod subagent_execution_tool; @@ -24,8 +22,6 @@ mod subagent_task_config; pub mod subagent_tool; pub(crate) mod todo_extension; mod tool_execution; -mod tool_route_manager; -mod tool_router_index_manager; pub mod types; pub use agent::{Agent, AgentEvent}; diff --git a/crates/goose/src/agents/prompt_manager.rs b/crates/goose/src/agents/prompt_manager.rs index 5304e5526d47..f13688ac63a9 100644 --- a/crates/goose/src/agents/prompt_manager.rs +++ b/crates/goose/src/agents/prompt_manager.rs @@ -6,7 +6,6 @@ use serde_json::Value; use std::collections::HashMap; use crate::agents::extension::ExtensionInfo; -use crate::agents::router_tools::llm_search_tool_prompt; use crate::hints::load_hints::{load_hint_files, AGENTS_MD_FILENAME, GOOSE_HINTS_FILENAME}; use crate::{ config::{Config, GooseMode}, @@ -33,8 +32,6 @@ impl Default for PromptManager { #[derive(Serialize)] struct SystemPromptContext { extensions: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - tool_selection_strategy: Option, current_date_time: String, #[serde(skip_serializing_if = "Option::is_none")] extension_tool_limits: Option<(usize, usize)>, @@ -52,7 +49,6 @@ pub struct SystemPromptBuilder<'a, M> { extensions_info: Vec, frontend_instructions: Option, extension_tool_count: Option<(usize, usize)>, - router_enabled: bool, subagents_enabled: bool, hints: Option, code_execution_mode: bool, @@ -85,11 +81,6 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> { self } - pub fn with_router_enabled(mut self, enabled: bool) -> Self { - self.router_enabled = enabled; - self - } - pub fn with_code_execution_mode(mut self, enabled: bool) -> Self { self.code_execution_mode = enabled; self @@ -158,7 +149,6 @@ impl<'a> SystemPromptBuilder<'a, PromptManager> { let context = SystemPromptContext { extensions: sanitized_extensions_info, - tool_selection_strategy: self.router_enabled.then(llm_search_tool_prompt), current_date_time: self.manager.current_date_timestamp.clone(), extension_tool_limits, goose_mode, @@ -247,7 +237,6 @@ impl PromptManager { extensions_info: vec![], frontend_instructions: None, extension_tool_count: None, - router_enabled: false, subagents_enabled: false, hints: None, code_execution_mode: false, @@ -369,7 +358,6 @@ mod tests { "how to use this extension", true, )) - .with_router_enabled(true) .build(); assert_snapshot!(system_prompt) @@ -391,7 +379,6 @@ mod tests { "", false, )) - .with_router_enabled(true) .with_extension_and_tool_counts(MAX_EXTENSIONS + 1, MAX_TOOLS + 1) .build(); diff --git a/crates/goose/src/agents/reply_parts.rs b/crates/goose/src/agents/reply_parts.rs index 7c5d0c939d3f..cd00a07d07c4 100644 --- a/crates/goose/src/agents/reply_parts.rs +++ b/crates/goose/src/agents/reply_parts.rs @@ -113,16 +113,8 @@ impl Agent { &self, working_dir: &std::path::Path, ) -> Result<(Vec, Vec, String)> { - // Get router enabled status - let router_enabled = self.tool_route_manager.is_router_enabled().await; - // Get tools from extension manager - let mut tools = self.list_tools_for_router().await; - - // If router is disabled and no tools were returned, fall back to regular tools - if !router_enabled && tools.is_empty() { - tools = self.list_tools(None).await; - } + let mut tools = self.list_tools(None).await; // Add frontend tools let frontend_tools = self.frontend_tools.lock().await; @@ -139,10 +131,8 @@ impl Agent { tools.retain(|tool| tool.name.starts_with(&code_exec_prefix)); } - if !router_enabled { - // Stable tool ordering is important for multi session prompt caching. - tools.sort_by(|a, b| a.name.cmp(&b.name)); - } + // Stable tool ordering is important for multi session prompt caching. + tools.sort_by(|a, b| a.name.cmp(&b.name)); // Prepare system prompt let extensions_info = self.extension_manager.get_extensions_info().await; @@ -159,7 +149,6 @@ impl Agent { .with_extensions(extensions_info.into_iter()) .with_frontend_instructions(self.frontend_instructions.lock().await.clone()) .with_extension_and_tool_counts(extension_count, tool_count) - .with_router_enabled(router_enabled) .with_code_execution_mode(code_execution_active) .with_hints(working_dir) .with_enable_subagents(self.subagents_enabled().await) @@ -437,8 +426,7 @@ mod tests { } #[tokio::test] - async fn prepare_tools_sorts_when_router_disabled_and_includes_frontend_and_list_tools( - ) -> anyhow::Result<()> { + async fn prepare_tools_sorts_and_includes_frontend_and_list_tools() -> anyhow::Result<()> { let agent = crate::agents::Agent::new(); let session = SessionManager::create_session( @@ -452,9 +440,6 @@ mod tests { let provider = std::sync::Arc::new(MockProvider { model_config }); agent.update_provider(provider, &session.id).await?; - // Disable the router to trigger sorting - agent.disable_router_for_recipe().await; - // Add unsorted frontend tools let frontend_tools = vec![ Tool::new( diff --git a/crates/goose/src/agents/router_tool_selector.rs b/crates/goose/src/agents/router_tool_selector.rs deleted file mode 100644 index 85ae7577e2b5..000000000000 --- a/crates/goose/src/agents/router_tool_selector.rs +++ /dev/null @@ -1,180 +0,0 @@ -use rmcp::model::{Content, ErrorCode, ErrorData}; -use rmcp::model::{JsonObject, Tool}; - -use anyhow::Result; -use async_trait::async_trait; -use serde::Serialize; -use std::borrow::Cow; -use std::collections::HashMap; -use std::collections::VecDeque; -use std::sync::Arc; -use tokio::sync::RwLock; - -use crate::conversation::message::Message; -use crate::prompt_template::render_global_file; -use crate::providers::base::Provider; - -#[derive(Serialize)] -struct ToolSelectorContext { - tools: String, - query: String, -} - -#[async_trait] -pub trait RouterToolSelector: Send + Sync { - async fn select_tools(&self, params: JsonObject) -> Result, ErrorData>; - async fn index_tools(&self, tools: &[Tool], extension_name: &str) -> Result<(), ErrorData>; - async fn remove_tool(&self, tool_name: &str) -> Result<(), ErrorData>; - async fn record_tool_call(&self, tool_name: &str) -> Result<(), ErrorData>; - async fn get_recent_tool_calls(&self, limit: usize) -> Result, ErrorData>; -} - -pub struct LLMToolSelector { - llm_provider: Arc, - tool_strings: Arc>>, // extension_name -> tool_string - recent_tool_calls: Arc>>, -} - -impl LLMToolSelector { - pub async fn new(provider: Arc) -> Result { - Ok(Self { - llm_provider: provider.clone(), - tool_strings: Arc::new(RwLock::new(HashMap::new())), - recent_tool_calls: Arc::new(RwLock::new(VecDeque::with_capacity(100))), - }) - } -} - -#[async_trait] -impl RouterToolSelector for LLMToolSelector { - async fn select_tools(&self, params: JsonObject) -> Result, ErrorData> { - let query = params - .get("query") - .and_then(|v| v.as_str()) - .ok_or_else(|| ErrorData { - code: ErrorCode::INVALID_PARAMS, - message: Cow::from("Missing 'query' parameter"), - data: None, - })?; - - let extension_name = params - .get("extension_name") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - // Get relevant tool strings based on extension_name - let tool_strings = self.tool_strings.read().await; - let relevant_tools = if let Some(ext) = &extension_name { - tool_strings.get(ext).cloned() - } else { - // If no extension specified, use all tools - Some( - tool_strings - .values() - .cloned() - .collect::>() - .join("\n"), - ) - }; - - if let Some(tools) = relevant_tools { - // Use template to generate the prompt - let context = ToolSelectorContext { - tools: tools.clone(), - query: query.to_string(), - }; - - let user_prompt = - render_global_file("router_tool_selector.md", &context).map_err(|e| ErrorData { - code: ErrorCode::INTERNAL_ERROR, - message: Cow::from(format!("Failed to render prompt template: {}", e)), - data: None, - })?; - - let user_message = Message::user().with_text(&user_prompt); - let response = self - .llm_provider - .complete("system", &[user_message], &[]) - .await - .map_err(|e| ErrorData { - code: ErrorCode::INTERNAL_ERROR, - message: Cow::from(format!("Failed to search tools: {}", e)), - data: None, - })?; - - // Extract just the message content from the response - let (message, _usage) = response; - let text = message.content[0].as_text().unwrap_or_default(); - - // Split the response into individual tool entries - let tool_entries: Vec = text - .split("\n\n") - .filter(|entry| entry.trim().starts_with("Tool:")) - .map(|entry| Content::text(entry.trim().to_string())) - .collect(); - - Ok(tool_entries) - } else { - Ok(vec![]) - } - } - - async fn index_tools(&self, tools: &[Tool], extension_name: &str) -> Result<(), ErrorData> { - let mut tool_strings = self.tool_strings.write().await; - - for tool in tools { - let tool_string = format!( - "Tool: {}\nDescription: {}\nSchema: {}", - tool.name, - tool.description - .as_ref() - .map(|d| d.as_ref()) - .unwrap_or_default(), - serde_json::to_string_pretty(&tool.input_schema) - .unwrap_or_else(|_| "{}".to_string()) - ); - - // Use the provided extension_name instead of parsing from tool name - let entry = tool_strings.entry(extension_name.to_string()).or_default(); - - // Check if this tool already exists in the entry - if !entry.contains(&format!("Tool: {}", tool.name)) { - if !entry.is_empty() { - entry.push_str("\n\n"); - } - entry.push_str(&tool_string); - } - } - - Ok(()) - } - async fn remove_tool(&self, tool_name: &str) -> Result<(), ErrorData> { - let mut tool_strings = self.tool_strings.write().await; - if let Some(extension_name) = tool_name.split("__").next() { - tool_strings.remove(extension_name); - } - Ok(()) - } - - async fn record_tool_call(&self, tool_name: &str) -> Result<(), ErrorData> { - let mut recent_calls = self.recent_tool_calls.write().await; - if recent_calls.len() >= 100 { - recent_calls.pop_front(); - } - recent_calls.push_back(tool_name.to_string()); - Ok(()) - } - - async fn get_recent_tool_calls(&self, limit: usize) -> Result, ErrorData> { - let recent_calls = self.recent_tool_calls.read().await; - Ok(recent_calls.iter().rev().take(limit).cloned().collect()) - } -} - -// Helper function to create a boxed tool selector -pub async fn create_tool_selector( - provider: Arc, -) -> Result> { - let selector = LLMToolSelector::new(provider).await?; - Ok(Box::new(selector)) -} diff --git a/crates/goose/src/agents/router_tools.rs b/crates/goose/src/agents/router_tools.rs deleted file mode 100644 index 9a268eb29377..000000000000 --- a/crates/goose/src/agents/router_tools.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::agents::extension_manager_extension::{ - LIST_RESOURCES_TOOL_NAME, MANAGE_EXTENSIONS_TOOL_NAME, READ_RESOURCE_TOOL_NAME, - SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME, -}; -use indoc::indoc; -use rmcp::model::{Tool, ToolAnnotations}; -use rmcp::object; - -pub const ROUTER_LLM_SEARCH_TOOL_NAME: &str = "router__llm_search"; - -pub fn llm_search_tool() -> Tool { - Tool::new( - ROUTER_LLM_SEARCH_TOOL_NAME.to_string(), - indoc! {r#" - Searches for relevant tools based on the user's messages. - Format a query to search for the most relevant tools based on the user's messages. - Pay attention to the keywords in the user's messages, especially the last message and potential tools they are asking for. - This tool should be invoked when the user's messages suggest they are asking for a tool to be run. - Use the extension_name parameter to filter tools by the appropriate extension. - For example, if the user is asking to list the files in the current directory, you filter for the "developer" extension. - Example: {"User": "list the files in the current directory", "Query": "list files in current directory", "Extension Name": "developer", "k": 5} - Extension name is not optional, it is required. - The returned result will be a list of tool names, descriptions, and schemas from which you, the agent can select the most relevant tool to invoke. - "#} - .to_string(), - object!({ - "type": "object", - "required": ["query", "extension_name"], - "properties": { - "extension_name": {"type": "string", "description": "The name of the extension to filter tools by"}, - "query": {"type": "string", "description": "The query to search for the most relevant tools based on the user's messages"}, - "k": {"type": "integer", "description": "The number of tools to retrieve (defaults to 5)", "default": 5} - } - }) - ).annotate(ToolAnnotations { - title: Some("LLM search for relevant tools".to_string()), - read_only_hint: Some(true), - destructive_hint: Some(false), - idempotent_hint: Some(false), - open_world_hint: Some(false), - }) -} - -pub fn llm_search_tool_prompt() -> String { - format!( - r#"# LLM Tool Selection Instructions - Important: the user has opted to dynamically enable tools, so although an extension could be enabled, \ - please invoke the llm search tool to actually retrieve the most relevant tools to use according to the user's messages. - For example, if the user has 3 extensions enabled, but they are asking for a tool to read a pdf file, \ - you would invoke the llm_search tool to find the most relevant read pdf tool. - By dynamically enabling tools, you (goose) as the agent save context window space and allow the user to dynamically retrieve the most relevant tools. - Be sure to format a query packed with relevant keywords to search for the most relevant tools. - In addition to the extension names available to you, you also have platform extension tools available to you. - The platform extension contains the following tools: - - {} - - {} - - {} - - {} - "#, - SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME, - MANAGE_EXTENSIONS_TOOL_NAME, - READ_RESOURCE_TOOL_NAME, - LIST_RESOURCES_TOOL_NAME - ) -} diff --git a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap index fa99f52b7866..5afeffcddf08 100644 --- a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap +++ b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__basic.snap @@ -26,8 +26,6 @@ new ones. No extensions are defined. You should let the user know that they should add extensions. - - # Response Guidelines - Use Markdown formatting for all responses. diff --git a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap index d3037a9d17fa..d7ba6de0dfec 100644 --- a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap +++ b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__one_extension.snap @@ -37,21 +37,6 @@ and platform__list_resources on this extension. how to use this extension -# LLM Tool Selection Instructions - Important: the user has opted to dynamically enable tools, so although an extension could be enabled, \ - please invoke the llm search tool to actually retrieve the most relevant tools to use according to the user's messages. - For example, if the user has 3 extensions enabled, but they are asking for a tool to read a pdf file, \ - you would invoke the llm_search tool to find the most relevant read pdf tool. - By dynamically enabling tools, you (goose) as the agent save context window space and allow the user to dynamically retrieve the most relevant tools. - Be sure to format a query packed with relevant keywords to search for the most relevant tools. - In addition to the extension names available to you, you also have platform extension tools available to you. - The platform extension contains the following tools: - - search_available_extensions - - manage_extensions - - read_resource - - list_resources - - # Response Guidelines - Use Markdown formatting for all responses. diff --git a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__typical_setup.snap b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__typical_setup.snap index b299f2e1a6be..c9848fd9ca20 100644 --- a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__typical_setup.snap +++ b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__typical_setup.snap @@ -51,21 +51,6 @@ You should only disable extensions found from the search_available_extensions to List all the extensions available to disable in the response. Explain that minimizing extensions helps with the recall of the correct tools to use. -# LLM Tool Selection Instructions - Important: the user has opted to dynamically enable tools, so although an extension could be enabled, \ - please invoke the llm search tool to actually retrieve the most relevant tools to use according to the user's messages. - For example, if the user has 3 extensions enabled, but they are asking for a tool to read a pdf file, \ - you would invoke the llm_search tool to find the most relevant read pdf tool. - By dynamically enabling tools, you (goose) as the agent save context window space and allow the user to dynamically retrieve the most relevant tools. - Be sure to format a query packed with relevant keywords to search for the most relevant tools. - In addition to the extension names available to you, you also have platform extension tools available to you. - The platform extension contains the following tools: - - search_available_extensions - - manage_extensions - - read_resource - - list_resources - - # Response Guidelines - Use Markdown formatting for all responses. diff --git a/crates/goose/src/agents/tool_route_manager.rs b/crates/goose/src/agents/tool_route_manager.rs deleted file mode 100644 index c757a173aefc..000000000000 --- a/crates/goose/src/agents/tool_route_manager.rs +++ /dev/null @@ -1,185 +0,0 @@ -use crate::agents::extension_manager::ExtensionManager; -use crate::agents::router_tool_selector::{create_tool_selector, RouterToolSelector}; -use crate::agents::router_tools::{self}; -use crate::agents::tool_execution::ToolCallResult; -use crate::agents::tool_router_index_manager::ToolRouterIndexManager; -use crate::config::Config; -use crate::conversation::message::ToolRequest; -use crate::providers::base::Provider; -use anyhow::{anyhow, Result}; -use rmcp::model::{ErrorCode, ErrorData, JsonObject, Tool}; -use std::sync::Arc; -use tokio::sync::Mutex; -use tracing::error; - -pub struct ToolRouteManager { - router_tool_selector: Mutex>>>, - router_disabled_override: Mutex, -} - -impl Default for ToolRouteManager { - fn default() -> Self { - Self::new() - } -} - -impl ToolRouteManager { - pub fn new() -> Self { - Self { - router_tool_selector: Mutex::new(None), - router_disabled_override: Mutex::new(false), - } - } - - pub async fn disable_router_for_recipe(&self) { - *self.router_disabled_override.lock().await = true; - *self.router_tool_selector.lock().await = None; - } - - pub async fn record_tool_requests(&self, requests: &[ToolRequest]) { - let selector = self.router_tool_selector.lock().await.clone(); - for request in requests { - if let Ok(tool_call) = &request.tool_call { - if let Some(ref selector) = selector { - if let Err(e) = selector.record_tool_call(&tool_call.name).await { - error!("Failed to record tool call: {}", e); - } - } - } - } - } - - pub async fn dispatch_route_search_tool( - &self, - arguments: JsonObject, - ) -> Result { - let selector = self.router_tool_selector.lock().await.clone(); - match selector.as_ref() { - Some(selector) => match selector.select_tools(arguments).await { - Ok(content) => Ok(ToolCallResult::from(Ok(rmcp::model::CallToolResult { - content, - structured_content: None, - is_error: Some(false), - meta: None, - }))), - Err(e) => Err(ErrorData::new( - ErrorCode::INTERNAL_ERROR, - format!("Failed to select tools: {}", e), - None, - )), - }, - None => Err(ErrorData::new( - ErrorCode::INTERNAL_ERROR, - "No tool selector available".to_string(), - None, - )), - } - } - - pub async fn is_router_enabled(&self) -> bool { - if *self.router_disabled_override.lock().await { - return false; - } - - let config = Config::global(); - if let Ok(config_value) = config.get_param::("GOOSE_ENABLE_ROUTER") { - return config_value.to_lowercase() == "true"; - } - - // Default to false if neither is set - false - } - - pub async fn update_router_tool_selector( - &self, - provider: Arc, - reindex_all: Option, - extension_manager: &ExtensionManager, - ) -> Result<()> { - let enabled = self.is_router_enabled().await; - if !enabled { - return Ok(()); - } - - let selector = create_tool_selector(provider.clone()) - .await - .map_err(|e| anyhow!("Failed to create tool selector: {}", e))?; - - // Wrap selector in Arc for the index manager methods - let selector_arc = Arc::new(selector); - - if reindex_all.unwrap_or(false) { - let enabled_extensions = extension_manager.list_extensions().await?; - for extension_name in enabled_extensions { - if let Err(e) = ToolRouterIndexManager::update_extension_tools( - &selector_arc, - extension_manager, - &extension_name, - "add", - ) - .await - { - error!( - "Failed to index tools for extension {}: {}", - extension_name, e - ); - } - } - } - - // Update the selector - *self.router_tool_selector.lock().await = Some(selector_arc); - - Ok(()) - } - - pub async fn get_router_tool_selector(&self) -> Option>> { - self.router_tool_selector.lock().await.clone() - } - - /// Check if the router is actually functional (enabled in config AND initialized) - pub async fn is_router_functional(&self) -> bool { - if !self.is_router_enabled().await { - return false; - } - - // Check if the selector actually exists (meaning it was successfully initialized) - self.router_tool_selector.lock().await.is_some() - } - - pub async fn list_tools_for_router(&self, extension_manager: &ExtensionManager) -> Vec { - // If router is disabled or overridden, return empty - if *self.router_disabled_override.lock().await { - return vec![]; - } - - let mut prefixed_tools = vec![]; - - // If router is enabled but not functional (no provider), just return the search tool - if !self.is_router_functional().await { - return prefixed_tools; - } - prefixed_tools.push(router_tools::llm_search_tool()); - - // Get recent tool calls from router tool selector - let selector = self.router_tool_selector.lock().await.clone(); - if let Some(selector) = selector { - if let Ok(recent_calls) = selector.get_recent_tool_calls(20).await { - // Add recent tool calls to the list, avoiding duplicates - for tool_name in recent_calls { - // Find the tool in the extension manager's tools - if let Ok(extension_tools) = extension_manager.get_prefixed_tools(None).await { - if let Some(tool) = extension_tools.iter().find(|t| t.name == tool_name) { - // Only add if not already in prefixed_tools - if !prefixed_tools.iter().any(|t| t.name == tool.name) { - prefixed_tools.push(tool.clone()); - } - } - } - } - } - } - - prefixed_tools - } -} diff --git a/crates/goose/src/agents/tool_router_index_manager.rs b/crates/goose/src/agents/tool_router_index_manager.rs deleted file mode 100644 index d3e65467e965..000000000000 --- a/crates/goose/src/agents/tool_router_index_manager.rs +++ /dev/null @@ -1,76 +0,0 @@ -use anyhow::{anyhow, Result}; -use std::sync::Arc; -use tracing; - -use crate::agents::extension_manager::ExtensionManager; -use crate::agents::router_tool_selector::RouterToolSelector; - -/// Manages tool indexing operations for the router when LLM routing is enabled -pub struct ToolRouterIndexManager; - -impl ToolRouterIndexManager { - /// Updates the LLM index for tools when extensions are added or removed - pub async fn update_extension_tools( - selector: &Arc>, - extension_manager: &ExtensionManager, - extension_name: &str, - action: &str, - ) -> Result<()> { - match action { - "add" => { - // Get tools for specific extension - let tools = extension_manager - .get_prefixed_tools(Some(extension_name.to_string())) - .await?; - - if !tools.is_empty() { - // Index all tools at once - selector - .index_tools(&tools, extension_name) - .await - .map_err(|e| { - anyhow!( - "Failed to index tools for extension {}: {}", - extension_name, - e - ) - })?; - - tracing::info!( - "Indexed {} tools for extension {}", - tools.len(), - extension_name - ); - } - } - "remove" => { - // Remove all tools for this extension - let tools = extension_manager - .get_prefixed_tools(Some(extension_name.to_string())) - .await?; - - for tool in &tools { - selector.remove_tool(&tool.name).await.map_err(|e| { - anyhow!( - "Failed to remove tool {} for extension {}: {}", - tool.name, - extension_name, - e - ) - })?; - } - - tracing::info!( - "Removed {} tools for extension {}", - tools.len(), - extension_name - ); - } - _ => { - return Err(anyhow!("Invalid action: {}", action)); - } - } - - Ok(()) - } -} diff --git a/crates/goose/src/execution/manager.rs b/crates/goose/src/execution/manager.rs index cef71c829a1d..99d9b3d0d29c 100644 --- a/crates/goose/src/execution/manager.rs +++ b/crates/goose/src/execution/manager.rs @@ -87,7 +87,6 @@ impl AgentManager { .set_context(PlatformExtensionContext { session_id: Some(session_id.clone()), extension_manager: Some(Arc::downgrade(&agent.extension_manager)), - tool_route_manager: Some(Arc::downgrade(&agent.tool_route_manager)), }) .await; if let Some(provider) = &*self.default_provider.read().await { diff --git a/crates/goose/src/posthog.rs b/crates/goose/src/posthog.rs index d009844396a1..540a4868b662 100644 --- a/crates/goose/src/posthog.rs +++ b/crates/goose/src/posthog.rs @@ -363,11 +363,6 @@ async fn send_session_event(installation: &InstallationData) -> Result<(), Strin if let Ok(max_turns) = config.get_param::("GOOSE_MAX_TURNS") { event.insert_prop("setting_max_turns", max_turns).ok(); } - if let Ok(router_enabled) = config.get_param::("GOOSE_ENABLE_ROUTER") { - event - .insert_prop("setting_router_enabled", router_enabled) - .ok(); - } if let Ok(lead_model) = config.get_param::("GOOSE_LEAD_MODEL") { event.insert_prop("setting_lead_model", lead_model).ok(); diff --git a/crates/goose/src/prompts/router_tool_selector.md b/crates/goose/src/prompts/router_tool_selector.md deleted file mode 100644 index d551ccf3f56c..000000000000 --- a/crates/goose/src/prompts/router_tool_selector.md +++ /dev/null @@ -1,11 +0,0 @@ -You are a tool selection assistant. Your task is to find the most relevant tools based on the user's query. - -Given the following tools: -{{ tools }} - -Find the most relevant tools for the query: {{ query }} - -Return the tools in this exact format for each tool: -Tool: -Description: -Schema: \ No newline at end of file diff --git a/crates/goose/src/prompts/system.md b/crates/goose/src/prompts/system.md index b17209568397..87093d3997b8 100644 --- a/crates/goose/src/prompts/system.md +++ b/crates/goose/src/prompts/system.md @@ -58,8 +58,6 @@ Explain that minimizing extensions helps with the recall of the correct tools to {% endwith %} {% endif %} -{{tool_selection_strategy}} - # Response Guidelines - Use Markdown formatting for all responses. diff --git a/crates/goose/tests/agent.rs b/crates/goose/tests/agent.rs index 44e903b2a0bb..967f56d51e02 100644 --- a/crates/goose/tests/agent.rs +++ b/crates/goose/tests/agent.rs @@ -475,7 +475,6 @@ mod tests { .set_context(PlatformExtensionContext { session_id: Some("test_session".to_string()), extension_manager: Some(Arc::downgrade(&agent.extension_manager)), - tool_route_manager: Some(Arc::downgrade(&agent.tool_route_manager)), }) .await; diff --git a/documentation/docs/guides/environment-variables.md b/documentation/docs/guides/environment-variables.md index 0e75dba487b1..6e2700759ecf 100644 --- a/documentation/docs/guides/environment-variables.md +++ b/documentation/docs/guides/environment-variables.md @@ -226,7 +226,6 @@ These variables control how goose handles [tool execution](/docs/guides/goose-pe | Variable | Purpose | Values | Default | |----------|---------|---------|---------| | `GOOSE_MODE` | Controls how goose handles tool execution | "auto", "approve", "chat", "smart_approve" | "smart_approve" | -| `GOOSE_ENABLE_ROUTER` | Enables [intelligent tool selection strategy](/docs/guides/managing-tools/tool-router) | "true", "false" | "false" | | `GOOSE_TOOLSHIM` | Enables/disables tool call interpretation | "1", "true" (case insensitive) to enable | false | | `GOOSE_TOOLSHIM_OLLAMA_MODEL` | Specifies the model for [tool call interpretation](/docs/experimental/ollama) | Model name (e.g. llama3.2, qwen2.5) | System default | | `GOOSE_CLI_MIN_PRIORITY` | Controls verbosity of [tool output](/docs/guides/managing-tools/adjust-tool-output) | Float between 0.0 and 1.0 | 0.0 | @@ -237,9 +236,6 @@ These variables control how goose handles [tool execution](/docs/guides/goose-pe **Examples** ```bash -# Enable intelligent tool selection -export GOOSE_ENABLE_ROUTER=true - # Enable tool interpretation export GOOSE_TOOLSHIM=true export GOOSE_TOOLSHIM_OLLAMA_MODEL=llama3.2 diff --git a/documentation/docs/guides/managing-tools/index.md b/documentation/docs/guides/managing-tools/index.md index c9a08e732b1e..062011b7dd4b 100644 --- a/documentation/docs/guides/managing-tools/index.md +++ b/documentation/docs/guides/managing-tools/index.md @@ -20,11 +20,6 @@ import styles from '@site/src/components/Card/styles.module.css'; description="Configure fine-grained permissions to control which tools goose can use and when, ensuring secure and controlled automation." link="/docs/guides/managing-tools/tool-permissions" /> - - - 1. Click the button in the top-left to open the sidebar - 2. Click the `Settings` button on the sidebar - 3. Click the `Chat` tab - 4. Under `Tool Selection Strategy`, choose your preferred option: - - `Disabled` - Use the default tool selection strategy - - `Enabled` - Use LLM-based intelligence to select tools - - - 1. Run the configuration command: - ```sh - goose configure - ``` - - 2. Select `goose settings`: - ```sh - ┌ goose-configure - │ - ◆ What would you like to configure? - │ ○ Configure Providers - │ ○ Add Extension - │ ○ Toggle Extensions - │ ○ Remove Extension - // highlight-start - │ ● goose settings (Set the goose mode, Tool Output, Tool Permissions, Experiment, goose recipe github repo and more) - // highlight-end - └ - ``` - - 3. Select `Router Tool Selection Strategy`: - ```sh - ┌ goose-configure - │ - ◇ What would you like to configure? - │ goose settings - │ - ◆ What setting would you like to configure? - │ ○ goose mode - // highlight-start - │ ● Router Tool Selection Strategy (Experimental: configure a strategy for auto selecting tools to use) - // highlight-end - │ ○ Tool Permission - │ ○ Tool Output - │ ○ Toggle Experiment - │ ○ goose recipe github repo - └ - ``` - - 4. Choose whether to enable smart tool routing: - ```sh - ┌ goose-configure - │ - ◇ What would you like to configure? - │ goose settings - │ - ◇ What setting would you like to configure? - │ Router Tool Selection Strategy - │ - // highlight-start - ◆ Would you like to enable smart tool routing? - │ ● Enable Router (Use LLM-based intelligence to select tools) - │ ○ Disable Router - // highlight-end - └ - ``` - - This example output shows that the router was enabled: - ``` - ┌ goose-configure - │ - ◇ What would you like to configure? - │ goose settings - │ - ◇ What setting would you like to configure? - │ Router Tool Selection Strategy - │ - ◇ Would you like to enable smart tool routing? - │ Enable Router - │ - └ Router enabled - using LLM-based intelligence for tool selection - ``` - - When the router is enabled, goose CLI displays a message indicating when the `llm_search` strategy is in use. - - - - -## Environment Variable Configuration - -You can also configure tool selection using environment variables or in your [configuration file](/docs/guides/config-files): - -```bash -# Enable LLM-based tool selection -export GOOSE_ENABLE_ROUTER=true - -# Disable (use default behavior) -export GOOSE_ENABLE_ROUTER=false -``` - -Or in your `config.yaml` file: -```yaml -GOOSE_ENABLE_ROUTER: 'true' -``` \ No newline at end of file diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index e780fc6e993f..517e9aae9c8c 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -418,45 +418,6 @@ } } }, - "/agent/update_router_tool_selector": { - "post": { - "tags": [ - "super::routes::agent" - ], - "operationId": "update_router_tool_selector", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateRouterToolSelectorRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Tool selection strategy updated successfully", - "content": { - "text/plain": { - "schema": { - "type": "string" - } - } - } - }, - "401": { - "description": "Unauthorized - invalid secret key" - }, - "424": { - "description": "Agent not initialized" - }, - "500": { - "description": "Internal server error" - } - } - } - }, "/config": { "get": { "tags": [ @@ -5902,17 +5863,6 @@ } } }, - "UpdateRouterToolSelectorRequest": { - "type": "object", - "required": [ - "session_id" - ], - "properties": { - "session_id": { - "type": "string" - } - } - }, "UpdateScheduleRequest": { "type": "object", "required": [ diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index 0aaa6fb4579b..8ea9fa5c3d69 100644 --- a/ui/desktop/src/api/sdk.gen.ts +++ b/ui/desktop/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client'; import { client } from './client.gen'; -import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateRouterToolSelectorData, UpdateRouterToolSelectorErrors, UpdateRouterToolSelectorResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; +import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; export type Options = Options2 & { /** @@ -101,15 +101,6 @@ export const updateAgentProvider = (option } }); -export const updateRouterToolSelector = (options: Options) => (options.client ?? client).post({ - url: '/agent/update_router_tool_selector', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers - } -}); - export const readAllConfig = (options?: Options) => (options?.client ?? client).get({ url: '/config', ...options }); export const backupConfig = (options?: Options) => (options?.client ?? client).post({ url: '/config/backup', ...options }); diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index 6e009a16c52d..8a4d22bd044a 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -1108,10 +1108,6 @@ export type UpdateProviderRequest = { session_id: string; }; -export type UpdateRouterToolSelectorRequest = { - session_id: string; -}; - export type UpdateScheduleRequest = { cron: string; }; @@ -1465,37 +1461,6 @@ export type UpdateAgentProviderResponses = { 200: unknown; }; -export type UpdateRouterToolSelectorData = { - body: UpdateRouterToolSelectorRequest; - path?: never; - query?: never; - url: '/agent/update_router_tool_selector'; -}; - -export type UpdateRouterToolSelectorErrors = { - /** - * Unauthorized - invalid secret key - */ - 401: unknown; - /** - * Agent not initialized - */ - 424: unknown; - /** - * Internal server error - */ - 500: unknown; -}; - -export type UpdateRouterToolSelectorResponses = { - /** - * Tool selection strategy updated successfully - */ - 200: string; -}; - -export type UpdateRouterToolSelectorResponse = UpdateRouterToolSelectorResponses[keyof UpdateRouterToolSelectorResponses]; - export type ReadAllConfigData = { body?: never; path?: never; diff --git a/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx b/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx index 219d7b5ce73d..4df9683dfbe1 100644 --- a/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx +++ b/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx @@ -1,5 +1,4 @@ import { ModeSection } from '../mode/ModeSection'; -import { ToolSelectionStrategySection } from '../tool_selection_strategy/ToolSelectionStrategySection'; import DictationSection from '../dictation/DictationSection'; import { SecurityToggle } from '../security/SecurityToggle'; import { ResponseStylesSection } from '../response_styles/ResponseStylesSection'; @@ -46,19 +45,6 @@ export default function ChatSettingsSection() { - - - - Tool Selection Strategy (preview) - - Experimental: configure how Goose selects tools for your requests, useful when there are - many tools. Only tested with Claude models currently. - - - - - - ); } diff --git a/ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx b/ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx deleted file mode 100644 index 3b172c455e7c..000000000000 --- a/ui/desktop/src/components/settings/tool_selection_strategy/ToolSelectionStrategySection.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { useEffect, useState, useCallback } from 'react'; -import { useConfig } from '../../ConfigContext'; -import { getApiUrl } from '../../../config'; - -interface ToolSelectionStrategy { - key: boolean; - label: string; - description: string; -} - -export const all_tool_selection_strategies: ToolSelectionStrategy[] = [ - { - key: false, - label: 'Disabled', - description: 'Use the default tool selection strategy', - }, - { - key: true, - label: 'Enabled', - description: - 'Use LLM-based intelligence to select the most relevant tools based on the user query context.', - }, -]; - -export const ToolSelectionStrategySection = () => { - const [routerEnabled, setRouterEnabled] = useState(false); - const [_error, setError] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const { read, upsert } = useConfig(); - - const handleStrategyChange = async (enableRouter: boolean) => { - if (isLoading) return; // Prevent multiple simultaneous requests - - setError(null); // Clear any previous errors - setIsLoading(true); - - try { - // First update the configuration - try { - await upsert('GOOSE_ENABLE_ROUTER', enableRouter.toString(), false); - } catch (error) { - console.error('Error updating configuration:', error); - setError(`Failed to update configuration: ${error}`); - setIsLoading(false); - return; - } - - // Then update the backend - try { - const response = await fetch(getApiUrl('/agent/update_router_tool_selector'), { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Secret-Key': await window.electron.getSecretKey(), - }, - body: JSON.stringify({ - session_id: '', // TODO(jack) add the session id, or remove from this request payload - }), - }); - - if (!response.ok) { - const errorData = await response - .json() - .catch(() => ({ error: 'Unknown error from backend' })); - throw new Error(errorData.error || 'Unknown error from backend'); - } - - // Parse the success response - const data = await response - .json() - .catch(() => ({ message: 'Tool selection strategy updated successfully' })); - if (data.error) { - throw new Error(data.error); - } - } catch (error) { - console.error('Error updating backend:', error); - setError(`Failed to update backend: ${error}`); - setIsLoading(false); - return; - } - - // If both succeeded, update the UI - setRouterEnabled(enableRouter); - } catch (error) { - console.error('Error updating tool selection strategy:', error); - setError(`Failed to update tool selection strategy: ${error}`); - } finally { - setIsLoading(false); - } - }; - - const fetchCurrentStrategy = useCallback(async () => { - try { - const strategy = (await read('GOOSE_ENABLE_ROUTER', false)) as string; - if (strategy) { - setRouterEnabled(strategy === 'true'); - } - } catch (error) { - console.error('Error fetching current router setting:', error); - setError(`Failed to fetch current router setting: ${error}`); - } - }, [read]); - - useEffect(() => { - fetchCurrentStrategy(); - }, [fetchCurrentStrategy]); - - return ( -
- {all_tool_selection_strategies.map((strategy) => ( -
-
handleStrategyChange(strategy.key)} - > -
-
-

{strategy.label}

-

{strategy.description}

-
-
- -
- handleStrategyChange(strategy.key)} - disabled={isLoading} - className="peer sr-only" - /> -
-
-
-
- ))} -
- ); -};