Skip to content
Draft
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
22 changes: 22 additions & 0 deletions astrbot/builtin_stars/web_searcher/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ def __init__(self, context: star.Context) -> None:
self.sogo_search = Sogo()
self.baidu_initialized = False

# Deactivate built-in web search tools if web_search is disabled
# This allows MCP to provide custom web_search tools
websearch_enable = (provider_settings or {}).get("web_search", False)
if not websearch_enable:
self._set_tools_active(False)

def _set_tools_active(self, active: bool) -> None:
"""Set the active status of all built-in web search tools.

Args:
active: True to activate tools, False to deactivate them
"""
func_tool_mgr = self.context.get_llm_tool_manager()
for tool_name in self.TOOLS:
tool = func_tool_mgr.get_func(tool_name)
if tool:
tool.active = active

Comment on lines +64 to +69

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

🚨 _set_tools_active 可能误伤 MCP 覆盖的同名工具(以及其他来源的同名工具)

当前实现通过 func_tool_mgr.get_func(tool_name) 获取“当前注册表里该名字对应的工具”,然后直接写 tool.active。若 MCP 在 built-in 被禁用后注册了同名工具(PR 目标场景),get_func("web_search") 可能返回 MCP 的自定义工具;随后 edit_web_search_tools 每次请求都会调用 _set_tools_active(websearch_enable),当 web_search=false 时会把 MCP 工具也设置为 inactive,导致 MCP 工具不可用,反而违背“允许 MCP overrides”的目标。同样地,在 init 里禁用也可能提前把后续注册/已注册的同名非内置工具置为 inactive。要真正做到“内置工具不占名”,应只切换“本插件定义的工具实例”,而不是按名字从全局注册表取当前实例。

建议: 将本插件定义的工具对象在初始化阶段缓存为实例引用(例如 self._builtin_tool_instances = {name: <tool_obj>}),后续只对这些实例改 active;或者在工具对象上有 owner/module 标识时,先校验 tool 是否属于本插件再修改。至少应避免通过 get_func(name) 修改到已被覆盖的工具。

Suggested change
func_tool_mgr = self.context.get_llm_tool_manager()
for tool_name in self.TOOLS:
tool = func_tool_mgr.get_func(tool_name)
if tool:
tool.active = active
def __init__(self, context: star.Context) -> None:
self.context = context
self.tavily_key_index = 0
self.tavily_key_lock = asyncio.Lock()
# 将 str 类型的 key 迁移至 list[str],并保存
cfg = self.context.get_config()
provider_settings = cfg.get("provider_settings")
if provider_settings:
tavily_key = provider_settings.get("websearch_tavily_key")
if isinstance(tavily_key, str):
logger.info(
"检测到旧版 websearch_tavily_key (字符串格式),自动迁移为列表格式并保存。",
)
if tavily_key:
provider_settings["websearch_tavily_key"] = [tavily_key]
else:
provider_settings["websearch_tavily_key"] = []
cfg.save_config()
self.bing_search = Bing()
self.sogo_search = Sogo()
self.baidu_initialized = False
# Cache built-in tool instances so we don't accidentally toggle MCP overrides
func_tool_mgr = self.context.get_llm_tool_manager()
self._builtin_tool_instances = {
tool_name: func_tool_mgr.get_func(tool_name) for tool_name in self.TOOLS
}
# Deactivate built-in web search tools if web_search is disabled
# This allows MCP to provide custom web_search tools
cfg = self.context.get_config()
websearch_enable = cfg.get("provider_settings", {}).get("web_search", False)
if not websearch_enable:
self._set_tools_active(False)
def _set_tools_active(self, active: bool) -> None:
"""Set the active status of all built-in web search tools.
Args:
active: True to activate tools, False to deactivate them
"""
for tool_name, tool in getattr(self, "_builtin_tool_instances", {}).items():
if tool:
tool.active = active

async def _tidy_text(self, text: str) -> str:
"""清理文本,去除空格、换行符等"""
return text.strip().replace("\n", " ").replace("\r", " ").replace(" ", " ")
Expand Down Expand Up @@ -394,6 +412,10 @@ async def edit_web_search_tools(
websearch_enable = prov_settings.get("web_search", False)
provider = prov_settings.get("websearch_provider", "default")

# Globally activate/deactivate built-in web search tools based on config
# This allows MCP to provide custom web_search tools when built-in is disabled
self._set_tools_active(websearch_enable)

tool_set = req.func_tool
if isinstance(tool_set, FunctionToolManager):
req.func_tool = tool_set.get_full_tool_set()
Expand Down