diff --git a/shinkai-bin/shinkai-node/src/network/handle_commands_list.rs b/shinkai-bin/shinkai-node/src/network/handle_commands_list.rs index 872d83d1c..3e34e5fe6 100644 --- a/shinkai-bin/shinkai-node/src/network/handle_commands_list.rs +++ b/shinkai-bin/shinkai-node/src/network/handle_commands_list.rs @@ -2080,6 +2080,12 @@ impl Node { let _ = Node::v2_api_remove_tool(db_clone, bearer, tool_key, res).await; }); } + NodeCommand::V2ApiRemoveNetworkTool { bearer, tool_key, res } => { + let db_clone = Arc::clone(&self.db); + tokio::spawn(async move { + let _ = Node::v2_api_remove_network_tool(db_clone, bearer, tool_key, res).await; + }); + } NodeCommand::V2ApiResolveShinkaiFileProtocol { bearer, shinkai_file_protocol, diff --git a/shinkai-bin/shinkai-node/src/network/v2_api/api_v2_commands_tools.rs b/shinkai-bin/shinkai-node/src/network/v2_api/api_v2_commands_tools.rs index 92e43be1b..8c103441a 100644 --- a/shinkai-bin/shinkai-node/src/network/v2_api/api_v2_commands_tools.rs +++ b/shinkai-bin/shinkai-node/src/network/v2_api/api_v2_commands_tools.rs @@ -3011,6 +3011,80 @@ impl Node { } } + pub async fn v2_api_remove_network_tool( + db: Arc, + bearer: String, + tool_key: String, + res: Sender>, + ) -> Result<(), NodeError> { + if Self::validate_bearer_token(&bearer, db.clone(), &res).await.is_err() { + return Ok(()); + } + + let tool_router_key = match ToolRouterKey::from_string(&tool_key) { + Ok(key) => key, + Err(e) => { + let api_error = APIError { + code: StatusCode::BAD_REQUEST.as_u16(), + error: "Bad Request".to_string(), + message: format!("Invalid tool key: {}", e), + }; + let _ = res.send(Err(api_error)).await; + return Ok(()); + } + }; + + let tool_key_name = tool_router_key.to_string_without_version(); + let version = tool_router_key.version; + + let sanitized_key = ToolRouterKey::sanitize(&tool_key_name); + let prefix = "agent_"; + let mut agent_id = format!("{}{}", prefix, sanitized_key); + let max_len = 64; + if agent_id.len() > max_len { + let allowed_len = max_len - prefix.len(); + let truncated: String = sanitized_key.chars().skip(sanitized_key.len() - allowed_len).collect(); + agent_id = format!("{}{}", prefix, truncated); + } + + if let Ok(Some(_)) = db.get_agent(&agent_id) { + if let Err(err) = db.remove_agent(&agent_id) { + eprintln!("Warning: Failed to remove associated agent {}: {}", agent_id, err); + } else if let Ok(agent_tool) = db.get_tool_by_agent_id(&agent_id) { + let tk = agent_tool.tool_router_key(); + let _ = db.remove_tool(&tk.to_string_without_version(), tk.version().map(|v| v.to_string())); + } + } + + if let Err(e) = db.remove_tool_playground(&tool_key) { + log::warn!( + "Attempt to remove associated playground tool for key '{}' failed (this might be expected if none exists): {}. Continuing with main tool removal.", + tool_key, + e + ); + } + + match db.remove_tool(&tool_key_name, version) { + Ok(_) => { + let response = json!({ + "status": "success", + "message": "Network tool and associated agent removed successfully" + }); + let _ = res.send(Ok(response)).await; + } + Err(err) => { + let api_error = APIError { + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16(), + error: "Internal Server Error".to_string(), + message: format!("Failed to remove tool: {}", err), + }; + let _ = res.send(Err(api_error)).await; + } + } + + Ok(()) + } + pub async fn v2_api_enable_all_tools( db: Arc, bearer: String, diff --git a/shinkai-libs/shinkai-http-api/src/api_v2/api_v2_handlers_tools.rs b/shinkai-libs/shinkai-http-api/src/api_v2/api_v2_handlers_tools.rs index 4913169df..213a90da3 100644 --- a/shinkai-libs/shinkai-http-api/src/api_v2/api_v2_handlers_tools.rs +++ b/shinkai-libs/shinkai-http-api/src/api_v2/api_v2_handlers_tools.rs @@ -274,6 +274,13 @@ pub fn tool_routes( .and(warp::query::>()) .and_then(remove_tool_handler); + let remove_network_tool_route = warp::path("remove_network_tool") + .and(warp::delete()) + .and(with_sender(node_commands_sender.clone())) + .and(warp::header::("authorization")) + .and(warp::query::>()) + .and_then(remove_network_tool_handler); + let tool_store_proxy_route = warp::path("tool_store_proxy") .and(warp::get()) .and(with_sender(node_commands_sender.clone())) @@ -362,6 +369,7 @@ pub fn tool_routes( .or(playground_file_route) .or(list_tool_asset_route) .or(delete_tool_asset_route) + .or(remove_network_tool_route) .or(remove_tool_route) .or(list_all_network_shinkai_tools_route) .or(enable_all_tools_route) @@ -1840,6 +1848,54 @@ pub async fn remove_tool_handler( } } +#[utoipa::path( + delete, + path = "/v2/remove_network_tool", + params( + ("tool_key" = String, Query, description = "Key of the network tool to remove") + ), + responses( + (status = 200, description = "Successfully removed network tool", body = bool), + (status = 400, description = "Bad request", body = APIError), + (status = 500, description = "Internal server error", body = APIError) + ) +)] +pub async fn remove_network_tool_handler( + sender: Sender, + authorization: String, + query_params: HashMap, +) -> Result { + let bearer = authorization.strip_prefix("Bearer ").unwrap_or("").to_string(); + let tool_key = query_params + .get("tool_key") + .ok_or_else(|| { + warp::reject::custom(APIError { + code: 400, + error: "Invalid Query".to_string(), + message: "The request query string is invalid.".to_string(), + }) + })? + .to_string(); + let (res_sender, res_receiver) = async_channel::bounded(1); + sender + .send(NodeCommand::V2ApiRemoveNetworkTool { + bearer, + tool_key, + res: res_sender, + }) + .await + .map_err(|_| warp::reject::reject())?; + let result = res_receiver.recv().await.map_err(|_| warp::reject::reject())?; + + match result { + Ok(response) => Ok(warp::reply::with_status(warp::reply::json(&response), StatusCode::OK)), + Err(error) => Ok(warp::reply::with_status( + warp::reply::json(&error), + StatusCode::from_u16(error.code).unwrap(), + )), + } +} + #[utoipa::path( post, path = "/v2/tool_asset", diff --git a/shinkai-libs/shinkai-http-api/src/node_commands.rs b/shinkai-libs/shinkai-http-api/src/node_commands.rs index 51a9836f0..8be12c2e9 100644 --- a/shinkai-libs/shinkai-http-api/src/node_commands.rs +++ b/shinkai-libs/shinkai-http-api/src/node_commands.rs @@ -805,6 +805,11 @@ pub enum NodeCommand { tool_key: String, res: Sender>, }, + V2ApiRemoveNetworkTool { + bearer: String, + tool_key: String, + res: Sender>, + }, V2ApiResolveShinkaiFileProtocol { bearer: String, shinkai_file_protocol: String,