Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions shinkai-bin/shinkai-node/src/network/handle_commands_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3011,6 +3011,80 @@ impl Node {
}
}

pub async fn v2_api_remove_network_tool(
db: Arc<SqliteManager>,
bearer: String,
tool_key: String,
res: Sender<Result<Value, APIError>>,
) -> 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<SqliteManager>,
bearer: String,
Expand Down
56 changes: 56 additions & 0 deletions shinkai-libs/shinkai-http-api/src/api_v2/api_v2_handlers_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ pub fn tool_routes(
.and(warp::query::<HashMap<String, String>>())
.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::<String>("authorization"))
.and(warp::query::<HashMap<String, String>>())
.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()))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<NodeCommand>,
authorization: String,
query_params: HashMap<String, String>,
) -> Result<impl warp::Reply, warp::Rejection> {
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",
Expand Down
5 changes: 5 additions & 0 deletions shinkai-libs/shinkai-http-api/src/node_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,11 @@ pub enum NodeCommand {
tool_key: String,
res: Sender<Result<Value, APIError>>,
},
V2ApiRemoveNetworkTool {
bearer: String,
tool_key: String,
res: Sender<Result<Value, APIError>>,
},
V2ApiResolveShinkaiFileProtocol {
bearer: String,
shinkai_file_protocol: String,
Expand Down