From a4d0c3a8cb77fd69b5964b47ff8fba964e965d4f Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 26 Mar 2026 23:01:45 +0530 Subject: [PATCH] refactor: enhance tool discovery by excluding read-only tools - Introduced a utility function to identify read-only tools in both TypeScript and Rust implementations. - Updated tool discovery logic to filter out read-only tools, ensuring only actionable tools are included in the skill definitions. - Improved comments for clarity regarding the handling of tool data from the memory layer. --- src-tauri/src/commands/chat.rs | 15 +++++++++++++-- src-tauri/src/memory/mod.rs | 1 - src/pages/Conversations.tsx | 26 ++++++++++++++++++-------- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/commands/chat.rs b/src-tauri/src/commands/chat.rs index af90f8b7b..fa2b5d763 100644 --- a/src-tauri/src/commands/chat.rs +++ b/src-tauri/src/commands/chat.rs @@ -312,15 +312,26 @@ fn has_meaningful_content(content: &str) -> bool { // ─── Tool discovery (desktop only) ─────────────────────────────────────────── +/// Returns true for read-only tools whose data is served by the memory layer, not the LLM tool loop. +fn is_read_tool(name: &str) -> bool { + name.starts_with("get-") + || name.starts_with("list-") + || name.starts_with("query-") + || name == "search" + || name == "sync-status" +} + /// Build OpenAI-format tool definitions from the Rust skill registry. /// Tool names are namespaced as `{skill_id}__{tool_name}`. +/// Read-only tools are excluded — their data comes from the memory layer (Step 2 context recall). #[cfg(not(any(target_os = "android", target_os = "ios")))] fn discover_tools( engine: &crate::runtime::qjs_engine::RuntimeEngine, ) -> Vec { - let raw_tools = engine.all_tools(); - raw_tools + engine + .all_tools() .into_iter() + .filter(|(_, tool)| !is_read_tool(&tool.name)) .map(|(skill_id, tool)| { serde_json::json!({ "type": "function", diff --git a/src-tauri/src/memory/mod.rs b/src-tauri/src/memory/mod.rs index f92312daa..2187ceea4 100644 --- a/src-tauri/src/memory/mod.rs +++ b/src-tauri/src/memory/mod.rs @@ -230,7 +230,6 @@ impl MemoryClient { log::warn!("[memory] recall_skill_context: exit — error (namespace={namespace}): {e}"); format!("Memory recall failed: {e}") })?; - log::info!("[memory] recall_skill_context: response: {res:?}"); let response = res.data.context; log::info!( "[memory] recall_skill_context: exit — ok (namespace={namespace}, has_response={})", diff --git a/src/pages/Conversations.tsx b/src/pages/Conversations.tsx index aabe07a57..baeea6411 100644 --- a/src/pages/Conversations.tsx +++ b/src/pages/Conversations.tsx @@ -463,18 +463,28 @@ const Conversations = () => { { role: 'user' as const, content: processedUserContent }, ]; + // Read-only tools are excluded — their data comes from the memory layer (recalled in context above). + const isReadTool = (toolName: string): boolean => + toolName.startsWith('get-') || + toolName.startsWith('list-') || + toolName.startsWith('query-') || + toolName === 'search' || + toolName === 'sync-status'; + // Build tool definitions for ALL ready skills — namespaced as {skillId}__{toolName} const allSkillTools: Tool[] = Object.entries(skillsState.skills) .filter(([, skill]) => skill.status === 'ready' && skill.tools?.length) .flatMap(([skillId, skill]) => - (skill.tools ?? []).map(t => ({ - type: 'function' as const, - function: { - name: `${skillId}__${t.name}`, - description: t.description, - parameters: t.inputSchema as Tool['function']['parameters'], - }, - })) + (skill.tools ?? []) + .filter(t => !isReadTool(t.name)) + .map(t => ({ + type: 'function' as const, + function: { + name: `${skillId}__${t.name}`, + description: t.description, + parameters: t.inputSchema as Tool['function']['parameters'], + }, + })) ); console.log(