refactor(agents): remove dead tool-building machinery from registry

After the main agent moved to its own build_main_agent_tools, nothing calls
the shared registry's builders. Delete the dead functions (build_tools,
build_tools_async, get_tool_by_name, get_all_tool_names,
get_default_enabled_tools) plus the now-orphaned load_mcp_tools import and the
stale __init__ re-exports.

BUILTIN_TOOLS, ToolDefinition, and get_connector_gated_tools are retained:
the catalog is still consumed for tool *metadata* (action_log revert/dedup
resolvers and the /agent/tools listing). Also drop stale references to the
deleted chat_deepagent.py within the agents module.

Verified: full unit suite green (2431 passed, 1 skipped); lints clean.
This commit is contained in:
CREDO23 2026-06-04 19:24:17 +02:00
parent 66103c68f6
commit c3238d8840
4 changed files with 8 additions and 249 deletions

View file

@ -22,10 +22,6 @@ from .podcast import create_generate_podcast_tool
from .registry import (
BUILTIN_TOOLS,
ToolDefinition,
build_tools,
get_all_tool_names,
get_default_enabled_tools,
get_tool_by_name,
)
from .video_presentation import create_generate_video_presentation_tool
@ -35,14 +31,10 @@ __all__ = [
# Knowledge base utilities
"CONNECTOR_DESCRIPTIONS",
"ToolDefinition",
"build_tools",
# Tool factories
"create_generate_image_tool",
"create_generate_podcast_tool",
"create_generate_video_presentation_tool",
"format_documents_for_context",
"get_all_tool_names",
"get_default_enabled_tools",
"get_tool_by_name",
"search_knowledge_base_async",
]

View file

@ -87,7 +87,6 @@ from .luma import (
create_list_luma_events_tool,
create_read_luma_event_tool,
)
from .mcp_tool import load_mcp_tools
from .notion import (
create_create_notion_page_tool,
create_delete_notion_page_tool,
@ -330,8 +329,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
# =========================================================================
# NOTION TOOLS - create, update, delete pages
# Auto-disabled when no Notion connector is configured (see chat_deepagent.py)
# =========================================================================
# Auto-disabled when no Notion connector is configured # =========================================================================
ToolDefinition(
name="create_notion_page",
description="Create a new page in the user's Notion workspace",
@ -370,8 +368,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
# =========================================================================
# GOOGLE DRIVE TOOLS - create files, delete files
# Auto-disabled when no Google Drive connector is configured (see chat_deepagent.py)
# =========================================================================
# Auto-disabled when no Google Drive connector is configured # =========================================================================
ToolDefinition(
name="create_google_drive_file",
description="Create a new Google Doc or Google Sheet in Google Drive",
@ -398,8 +395,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
# =========================================================================
# DROPBOX TOOLS - create and trash files
# Auto-disabled when no Dropbox connector is configured (see chat_deepagent.py)
# =========================================================================
# Auto-disabled when no Dropbox connector is configured # =========================================================================
ToolDefinition(
name="create_dropbox_file",
description="Create a new file in Dropbox",
@ -426,8 +422,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
# =========================================================================
# ONEDRIVE TOOLS - create and trash files
# Auto-disabled when no OneDrive connector is configured (see chat_deepagent.py)
# =========================================================================
# Auto-disabled when no OneDrive connector is configured # =========================================================================
ToolDefinition(
name="create_onedrive_file",
description="Create a new file in Microsoft OneDrive",
@ -579,8 +574,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
),
# =========================================================================
# CONFLUENCE TOOLS - create, update, delete pages
# Auto-disabled when no Confluence connector is configured (see chat_deepagent.py)
# =========================================================================
# Auto-disabled when no Confluence connector is configured # =========================================================================
ToolDefinition(
name="create_confluence_page",
description="Create a new page in the user's Confluence space",
@ -736,14 +730,6 @@ BUILTIN_TOOLS: list[ToolDefinition] = [
# =============================================================================
def get_tool_by_name(name: str) -> ToolDefinition | None:
"""Get a tool definition by its name."""
for tool_def in BUILTIN_TOOLS:
if tool_def.name == name:
return tool_def
return None
def get_connector_gated_tools(
available_connectors: list[str] | None,
) -> list[str]:
@ -755,222 +741,3 @@ def get_connector_gated_tools(
if tool_def.required_connector and tool_def.required_connector not in available:
disabled.append(tool_def.name)
return disabled
def get_all_tool_names() -> list[str]:
"""Get names of all registered tools."""
return [tool_def.name for tool_def in BUILTIN_TOOLS]
def get_default_enabled_tools() -> list[str]:
"""Get names of tools that are enabled by default (excludes hidden tools)."""
return [tool_def.name for tool_def in BUILTIN_TOOLS if tool_def.enabled_by_default]
def build_tools(
dependencies: dict[str, Any],
enabled_tools: list[str] | None = None,
disabled_tools: list[str] | None = None,
additional_tools: list[BaseTool] | None = None,
) -> list[BaseTool]:
"""Build the list of tools for the agent.
Args:
dependencies: Dict containing all possible dependencies:
- search_space_id: The search space ID
- db_session: Database session
- connector_service: Connector service instance
- firecrawl_api_key: Optional Firecrawl API key
enabled_tools: Explicit list of tool names to enable. If None, uses defaults.
disabled_tools: List of tool names to disable (applied after enabled_tools).
additional_tools: Extra tools to add (e.g., custom tools not in registry).
Returns:
List of configured tool instances ready for the agent.
Example:
# Use all default tools
tools = build_tools(deps)
# Use only specific tools
tools = build_tools(deps, enabled_tools=["generate_report"])
# Use defaults but disable podcast
tools = build_tools(deps, disabled_tools=["generate_podcast"])
# Add custom tools
tools = build_tools(deps, additional_tools=[my_custom_tool])
"""
# Determine which tools to enable
if enabled_tools is not None:
tool_names_to_use = set(enabled_tools)
else:
tool_names_to_use = set(get_default_enabled_tools())
# Apply disabled list
if disabled_tools:
tool_names_to_use -= set(disabled_tools)
# Build the tools (skip hidden/WIP tools unconditionally)
tools: list[BaseTool] = []
for tool_def in BUILTIN_TOOLS:
if tool_def.hidden or tool_def.name not in tool_names_to_use:
continue
# Check that all required dependencies are provided
missing_deps = [dep for dep in tool_def.requires if dep not in dependencies]
if missing_deps:
msg = f"Tool '{tool_def.name}' requires dependencies: {missing_deps}"
raise ValueError(
msg,
)
# Create the tool
tool = tool_def.factory(dependencies)
# Propagate the registry-level metadata so middleware (e.g.
# ``DedupHITLToolCallsMiddleware``) and the action-log/revert
# pipeline can pick the resolvers up via ``tool.metadata`` without
# re-importing :data:`BUILTIN_TOOLS`.
if tool_def.dedup_key is not None or tool_def.reverse is not None:
existing_meta = getattr(tool, "metadata", None) or {}
merged_meta = dict(existing_meta)
if tool_def.dedup_key is not None:
merged_meta.setdefault("dedup_key", tool_def.dedup_key)
if tool_def.reverse is not None:
merged_meta.setdefault("reverse", tool_def.reverse)
try:
tool.metadata = merged_meta
except Exception:
logger.debug(
"Tool %s rejected metadata mutation; relying on registry lookup",
tool_def.name,
)
tools.append(tool)
# Add any additional custom tools
if additional_tools:
tools.extend(additional_tools)
return tools
async def build_tools_async(
dependencies: dict[str, Any],
enabled_tools: list[str] | None = None,
disabled_tools: list[str] | None = None,
additional_tools: list[BaseTool] | None = None,
include_mcp_tools: bool = True,
) -> list[BaseTool]:
"""Async version of build_tools that also loads MCP tools from database.
Design Note:
This function exists because MCP tools require database queries to load
user configs, while built-in tools are created synchronously from static
code.
Alternative: We could make build_tools() itself async and always query
the database, but that would force async everywhere even when only using
built-in tools. The current design keeps the simple case (static tools
only) synchronous while supporting dynamic database-loaded tools through
this async wrapper.
Phase 1.3: built-in tool construction (CPU; runs in a thread pool to
avoid event-loop stalls) and MCP tool loading (HTTP/DB I/O; runs on
the event loop) are kicked off concurrently. Cold-path savings are
bounded by the slower of the two typically MCP at ~200ms-1.7s
so the parallelization recovers the ~50-200ms previously spent
serially on built-in construction.
Args:
dependencies: Dict containing all possible dependencies
enabled_tools: Explicit list of tool names to enable. If None, uses defaults.
disabled_tools: List of tool names to disable (applied after enabled_tools).
additional_tools: Extra tools to add (e.g., custom tools not in registry).
include_mcp_tools: Whether to load user's MCP tools from database.
Returns:
List of configured tool instances ready for the agent, including MCP tools.
"""
import asyncio
import time
_perf_log = logging.getLogger("surfsense.perf")
_perf_log.setLevel(logging.DEBUG)
can_load_mcp = (
include_mcp_tools
and "db_session" in dependencies
and "search_space_id" in dependencies
)
# Built-in tool construction is synchronous + CPU-only. Off-loop it so
# MCP's HTTP/DB I/O can fire concurrently. ``build_tools`` is pure
# function over its inputs — safe to thread-shift.
_t0 = time.perf_counter()
builtin_task = asyncio.create_task(
asyncio.to_thread(
build_tools, dependencies, enabled_tools, disabled_tools, additional_tools
)
)
mcp_task: asyncio.Task | None = None
if can_load_mcp:
mcp_task = asyncio.create_task(
load_mcp_tools(
dependencies["db_session"],
dependencies["search_space_id"],
)
)
# Surface failures from each task independently so a flaky MCP
# endpoint never poisons built-in tool registration. ``return_exceptions``
# gives us per-task exceptions instead of dropping the second result
# when the first raises.
if mcp_task is not None:
builtin_result, mcp_result = await asyncio.gather(
builtin_task, mcp_task, return_exceptions=True
)
else:
builtin_result = await builtin_task
mcp_result = None
if isinstance(builtin_result, BaseException):
raise builtin_result # built-in registration failure is non-recoverable
tools: list[BaseTool] = builtin_result
_perf_log.info(
"[build_tools_async] Built-in tools in %.3fs (%d tools, parallel)",
time.perf_counter() - _t0,
len(tools),
)
if mcp_task is not None:
if isinstance(mcp_result, BaseException):
# ``return_exceptions=True`` captures the exception out-of-band,
# so ``sys.exc_info()`` is empty here. Pass the captured
# exception via ``exc_info=`` to get a real traceback.
logging.error(
"Failed to load MCP tools: %s", mcp_result, exc_info=mcp_result
)
else:
mcp_tools = mcp_result or []
_perf_log.info(
"[build_tools_async] MCP tools loaded in %.3fs (%d tools, parallel)",
time.perf_counter() - _t0,
len(mcp_tools),
)
tools.extend(mcp_tools)
logging.info(
"Registered %d MCP tools: %s",
len(mcp_tools),
[t.name for t in mcp_tools],
)
logging.info(
"Total tools for agent: %d%s",
len(tools),
[t.name for t in tools],
)
return tools