Skip to content
Merged
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
30 changes: 24 additions & 6 deletions backend/database/tool_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from database.agent_db import logger
from database.client import get_db_session, filter_property, as_dict
from database.db_models import ToolInstance, ToolInfo
from consts.model import ToolSourceEnum


def create_tool(tool_info, version_no: int = 0):
Expand Down Expand Up @@ -190,13 +191,23 @@ def check_tool_list_initialized(tenant_id: str) -> bool:
def update_tool_table_from_scan_tool_list(tenant_id: str, user_id: str, tool_list: List[ToolInfo]):
"""
scan all tools and update the tool table in PG database, remove the duplicate tools
For MCP tools, use name&source&usage as unique key to allow same tool name from different MCP servers
"""
with get_db_session() as session:
# get all existing tools (including complete information)
existing_tools = session.query(ToolInfo).filter(ToolInfo.delete_flag != 'Y',
ToolInfo.author == tenant_id).all()
existing_tool_dict = {
f"{tool.name}&{tool.source}": tool for tool in existing_tools}
# Build existing_tool_dict with different keys for MCP vs non-MCP tools
existing_tool_dict = {}
for tool in existing_tools:
if tool.source == ToolSourceEnum.MCP.value:
# For MCP tools, use name + source + usage (MCP server name) as unique key
key = f"{tool.name}&{tool.source}&{tool.usage or ''}"
else:
# For other tools, use name + source as unique key
key = f"{tool.name}&{tool.source}"
existing_tool_dict[key] = tool

# set all tools to unavailable
for tool in existing_tools:
tool.is_available = False
Expand All @@ -208,9 +219,15 @@ def update_tool_table_from_scan_tool_list(tenant_id: str, user_id: str, tool_lis
is_available = True if re.match(
r'^[a-zA-Z_][a-zA-Z0-9_]*$', tool.name) is not None else False

if f"{tool.name}&{tool.source}" in existing_tool_dict:
# by tool name and source to update the existing tool
existing_tool = existing_tool_dict[f"{tool.name}&{tool.source}"]
# Use same key generation logic as above
if tool.source == ToolSourceEnum.MCP.value:
tool_key = f"{tool.name}&{tool.source}&{tool.usage or ''}"
else:
tool_key = f"{tool.name}&{tool.source}"

if tool_key in existing_tool_dict:
# by tool name, source, and usage (for MCP) to update the existing tool
existing_tool = existing_tool_dict[tool_key]
for key, value in filtered_tool_data.items():
setattr(existing_tool, key, value)
existing_tool.updated_by = user_id
Expand Down Expand Up @@ -308,6 +325,7 @@ def delete_tools_by_agent_id(agent_id, tenant_id, user_id, version_no: int = 0):
ToolInstance.delete_flag: 'Y', 'updated_by': user_id
})


def search_last_tool_instance_by_tool_id(tool_id: int, tenant_id: str, user_id: str, version_no: int = 0):
"""
Query the latest ToolInstance by tool_id.
Expand All @@ -331,4 +349,4 @@ def search_last_tool_instance_by_tool_id(tool_id: int, tenant_id: str, user_id:
ToolInstance.delete_flag != 'Y'
).order_by(ToolInstance.update_time.desc())
tool_instance = query.first()
return as_dict(tool_instance) if tool_instance else None
return as_dict(tool_instance) if tool_instance else None
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { usePrefetchKnowledgeBases } from "@/hooks/useKnowledgeBaseSelector";
import { useConfig } from "@/hooks/useConfig";
import { updateToolConfig } from "@/services/agentConfigService";
import { useQueryClient } from "@tanstack/react-query";
import { useConfirmModal } from "@/hooks/useConfirmModal";

import { Settings, AlertTriangle } from "lucide-react";

Expand Down Expand Up @@ -74,6 +75,7 @@ export default function ToolManagement({
}: ToolManagementProps) {
const { t } = useTranslation("common");
const queryClient = useQueryClient();
const { confirm } = useConfirmModal();

// Get current agent permission from store
const currentAgentPermission = useAgentConfigStore(
Expand Down Expand Up @@ -277,7 +279,46 @@ export default function ToolManagement({
);
updateTools(newSelectedTools);
} else {
// If not selected, determine tool params and check if modal is needed
// If not selected, check for duplicate tool names first
const duplicateTool = currentSelectdTools.find(
(selectedTool) => selectedTool.name === tool.name
);

if (duplicateTool) {
// Show confirmation modal for duplicate tool name
return new Promise<void>((resolve) => {
confirm({
title: t("toolPool.duplicateToolName.title"),
content: t("toolPool.duplicateToolName.content", {
toolName: tool.name,
}),
okText: t("toolPool.duplicateToolName.confirm"),
cancelText: t("toolPool.duplicateToolName.cancel"),
danger: true,
onOk: async () => {
// User confirmed, proceed with tool selection
await proceedWithToolSelection();
resolve();
},
onCancel: () => {
// User cancelled, do nothing
resolve();
},
});
});
}

// No duplicate, proceed with normal tool selection
await proceedWithToolSelection();
}

// Helper function to proceed with tool selection after duplicate check
async function proceedWithToolSelection() {
// Get latest tools again to ensure we have the most up-to-date list
const currentSelectdTools =
useAgentConfigStore.getState().editedAgent.tools;

// Determine tool params and check if modal is needed
const configuredTool = currentSelectdTools.find(
(t) => parseInt(t.id) === numericId
);
Expand Down
4 changes: 4 additions & 0 deletions frontend/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@
"toolPool.vlmDisabledTooltip": "Please contact your administrator to configure an available Vision Language Model",
"toolPool.embeddingDisabledTooltip": "Please contact your administrator to configure an available Embedding model",
"toolPool.tooltip.functionGuide": "1. For local knowledge base search functionality, please enable the knowledge_base_search tool;\n2. For text file parsing functionality, please enable the analyze_text_file tool;\n3. For image parsing functionality, please enable the analyze_image tool.",
"toolPool.duplicateToolName.title": "Duplicate Tool Name Detected",
"toolPool.duplicateToolName.content": "You have selected tools with the same name ({{toolName}}). Duplicate tool names will cause the agent to fail during runtime. Do you want to continue selecting this tool?",
"toolPool.duplicateToolName.confirm": "Continue",
"toolPool.duplicateToolName.cancel": "Cancel",

"tool.message.unavailable": "This tool is currently unavailable and cannot be selected",
"tool.error.noMainAgentId": "Main Agent ID is not set, cannot update tool status",
Expand Down
4 changes: 4 additions & 0 deletions frontend/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,10 @@
"toolPool.vlmDisabledTooltip": "请联系管理员配置可用的视觉语言模型",
"toolPool.embeddingDisabledTooltip": "请联系管理员配置可用的向量模型",
"toolPool.tooltip.functionGuide": "1. 本地知识库检索功能,请启用knowledge_base_search工具;\n2. 文本文件解析功能,请启用analyze_text_file工具;\n3. 图片解析功能,请启用analyze_image工具。",
"toolPool.duplicateToolName.title": "检测到重复工具名",
"toolPool.duplicateToolName.content": "您已勾选相同工具名的工具({{toolName}}),重复选择会导致智能体无法正常运行。是否继续勾选?",
"toolPool.duplicateToolName.confirm": "继续",
"toolPool.duplicateToolName.cancel": "取消",

"tool.message.unavailable": "该工具当前不可用,无法选择",
"tool.error.noMainAgentId": "主代理ID未设置,无法更新工具状态",
Expand Down
Loading
Loading