feat: unify web tool config under WebToolsConfig + add web tool toggle controls

- Rename WebSearchConfig references to the new WebToolsConfig root struct that wraps both search config and global proxy settings
- Add 'enable' flag to WebToolsConfig to allow fully disabling all web-related tools (WebSearch, WebFetch) at runtime
- Update AgentLoop and SubagentManager to receive the full web config object instead of separate web_search_config/web_proxy parameters
- Update CLI command initialization to pass the consolidated web config struct instead of split fields
- Change default web search provider from brave to duckduckgo for better out-of-the-box usability (no API key required)
This commit is contained in:
Shiniese 2026-03-30 15:16:58 +08:00
parent c8c520cc9a
commit 7f1dca3186
4 changed files with 25 additions and 28 deletions

View File

@ -33,7 +33,7 @@ from nanobot.providers.base import LLMProvider
from nanobot.session.manager import Session, SessionManager from nanobot.session.manager import Session, SessionManager
if TYPE_CHECKING: if TYPE_CHECKING:
from nanobot.config.schema import ChannelsConfig, ExecToolConfig, WebSearchConfig from nanobot.config.schema import ChannelsConfig, ExecToolConfig, WebToolsConfig
from nanobot.cron.service import CronService from nanobot.cron.service import CronService
@ -59,8 +59,7 @@ class AgentLoop:
model: str | None = None, model: str | None = None,
max_iterations: int = 40, max_iterations: int = 40,
context_window_tokens: int = 65_536, context_window_tokens: int = 65_536,
web_search_config: WebSearchConfig | None = None, web_config: WebToolsConfig | None = None,
web_proxy: str | None = None,
exec_config: ExecToolConfig | None = None, exec_config: ExecToolConfig | None = None,
cron_service: CronService | None = None, cron_service: CronService | None = None,
restrict_to_workspace: bool = False, restrict_to_workspace: bool = False,
@ -69,7 +68,7 @@ class AgentLoop:
channels_config: ChannelsConfig | None = None, channels_config: ChannelsConfig | None = None,
timezone: str | None = None, timezone: str | None = None,
): ):
from nanobot.config.schema import ExecToolConfig, WebSearchConfig from nanobot.config.schema import ExecToolConfig, WebToolsConfig
self.bus = bus self.bus = bus
self.channels_config = channels_config self.channels_config = channels_config
@ -78,8 +77,7 @@ class AgentLoop:
self.model = model or provider.get_default_model() self.model = model or provider.get_default_model()
self.max_iterations = max_iterations self.max_iterations = max_iterations
self.context_window_tokens = context_window_tokens self.context_window_tokens = context_window_tokens
self.web_search_config = web_search_config or WebSearchConfig() self.web_config = web_config or WebToolsConfig()
self.web_proxy = web_proxy
self.exec_config = exec_config or ExecToolConfig() self.exec_config = exec_config or ExecToolConfig()
self.cron_service = cron_service self.cron_service = cron_service
self.restrict_to_workspace = restrict_to_workspace self.restrict_to_workspace = restrict_to_workspace
@ -95,8 +93,7 @@ class AgentLoop:
workspace=workspace, workspace=workspace,
bus=bus, bus=bus,
model=self.model, model=self.model,
web_search_config=self.web_search_config, web_config=self.web_config,
web_proxy=web_proxy,
exec_config=self.exec_config, exec_config=self.exec_config,
restrict_to_workspace=restrict_to_workspace, restrict_to_workspace=restrict_to_workspace,
) )
@ -142,8 +139,9 @@ class AgentLoop:
restrict_to_workspace=self.restrict_to_workspace, restrict_to_workspace=self.restrict_to_workspace,
path_append=self.exec_config.path_append, path_append=self.exec_config.path_append,
)) ))
self.tools.register(WebSearchTool(config=self.web_search_config, proxy=self.web_proxy)) if self.web_config.enable:
self.tools.register(WebFetchTool(proxy=self.web_proxy)) self.tools.register(WebSearchTool(config=self.web_config.search, proxy=self.web_config.proxy))
self.tools.register(WebFetchTool(proxy=self.web_config.proxy))
self.tools.register(MessageTool(send_callback=self.bus.publish_outbound)) self.tools.register(MessageTool(send_callback=self.bus.publish_outbound))
self.tools.register(SpawnTool(manager=self.subagents)) self.tools.register(SpawnTool(manager=self.subagents))
if self.cron_service: if self.cron_service:

View File

@ -17,7 +17,7 @@ from nanobot.agent.tools.shell import ExecTool
from nanobot.agent.tools.web import WebFetchTool, WebSearchTool from nanobot.agent.tools.web import WebFetchTool, WebSearchTool
from nanobot.bus.events import InboundMessage from nanobot.bus.events import InboundMessage
from nanobot.bus.queue import MessageBus from nanobot.bus.queue import MessageBus
from nanobot.config.schema import ExecToolConfig from nanobot.config.schema import ExecToolConfig, WebToolsConfig
from nanobot.providers.base import LLMProvider from nanobot.providers.base import LLMProvider
@ -30,8 +30,7 @@ class SubagentManager:
workspace: Path, workspace: Path,
bus: MessageBus, bus: MessageBus,
model: str | None = None, model: str | None = None,
web_search_config: "WebSearchConfig | None" = None, web_config: "WebToolsConfig | None" = None,
web_proxy: str | None = None,
exec_config: "ExecToolConfig | None" = None, exec_config: "ExecToolConfig | None" = None,
restrict_to_workspace: bool = False, restrict_to_workspace: bool = False,
): ):
@ -41,8 +40,7 @@ class SubagentManager:
self.workspace = workspace self.workspace = workspace
self.bus = bus self.bus = bus
self.model = model or provider.get_default_model() self.model = model or provider.get_default_model()
self.web_search_config = web_search_config or WebSearchConfig() self.web_config = web_config or WebToolsConfig()
self.web_proxy = web_proxy
self.exec_config = exec_config or ExecToolConfig() self.exec_config = exec_config or ExecToolConfig()
self.restrict_to_workspace = restrict_to_workspace self.restrict_to_workspace = restrict_to_workspace
self.runner = AgentRunner(provider) self.runner = AgentRunner(provider)
@ -100,14 +98,16 @@ class SubagentManager:
tools.register(WriteFileTool(workspace=self.workspace, allowed_dir=allowed_dir)) tools.register(WriteFileTool(workspace=self.workspace, allowed_dir=allowed_dir))
tools.register(EditFileTool(workspace=self.workspace, allowed_dir=allowed_dir)) tools.register(EditFileTool(workspace=self.workspace, allowed_dir=allowed_dir))
tools.register(ListDirTool(workspace=self.workspace, allowed_dir=allowed_dir)) tools.register(ListDirTool(workspace=self.workspace, allowed_dir=allowed_dir))
tools.register(ExecTool( if self.exec_config.enable:
working_dir=str(self.workspace), tools.register(ExecTool(
timeout=self.exec_config.timeout, working_dir=str(self.workspace),
restrict_to_workspace=self.restrict_to_workspace, timeout=self.exec_config.timeout,
path_append=self.exec_config.path_append, restrict_to_workspace=self.restrict_to_workspace,
)) path_append=self.exec_config.path_append,
tools.register(WebSearchTool(config=self.web_search_config, proxy=self.web_proxy)) ))
tools.register(WebFetchTool(proxy=self.web_proxy)) if self.web_config.enable:
tools.register(WebSearchTool(config=self.web_config.search, proxy=self.web_config.proxy))
tools.register(WebFetchTool(proxy=self.web_config.proxy))
system_prompt = self._build_subagent_prompt() system_prompt = self._build_subagent_prompt()
messages: list[dict[str, Any]] = [ messages: list[dict[str, Any]] = [

View File

@ -541,8 +541,7 @@ def gateway(
model=config.agents.defaults.model, model=config.agents.defaults.model,
max_iterations=config.agents.defaults.max_tool_iterations, max_iterations=config.agents.defaults.max_tool_iterations,
context_window_tokens=config.agents.defaults.context_window_tokens, context_window_tokens=config.agents.defaults.context_window_tokens,
web_search_config=config.tools.web.search, web_config=config.tools.web,
web_proxy=config.tools.web.proxy or None,
exec_config=config.tools.exec, exec_config=config.tools.exec,
cron_service=cron, cron_service=cron,
restrict_to_workspace=config.tools.restrict_to_workspace, restrict_to_workspace=config.tools.restrict_to_workspace,
@ -747,8 +746,7 @@ def agent(
model=config.agents.defaults.model, model=config.agents.defaults.model,
max_iterations=config.agents.defaults.max_tool_iterations, max_iterations=config.agents.defaults.max_tool_iterations,
context_window_tokens=config.agents.defaults.context_window_tokens, context_window_tokens=config.agents.defaults.context_window_tokens,
web_search_config=config.tools.web.search, web_config=config.tools.web,
web_proxy=config.tools.web.proxy or None,
exec_config=config.tools.exec, exec_config=config.tools.exec,
cron_service=cron, cron_service=cron,
restrict_to_workspace=config.tools.restrict_to_workspace, restrict_to_workspace=config.tools.restrict_to_workspace,

View File

@ -107,7 +107,7 @@ class GatewayConfig(Base):
class WebSearchConfig(Base): class WebSearchConfig(Base):
"""Web search tool configuration.""" """Web search tool configuration."""
provider: str = "brave" # brave, tavily, duckduckgo, searxng, jina provider: str = "duckduckgo" # brave, tavily, duckduckgo, searxng, jina
api_key: str = "" api_key: str = ""
base_url: str = "" # SearXNG base URL base_url: str = "" # SearXNG base URL
max_results: int = 5 max_results: int = 5
@ -116,6 +116,7 @@ class WebSearchConfig(Base):
class WebToolsConfig(Base): class WebToolsConfig(Base):
"""Web tools configuration.""" """Web tools configuration."""
enable: bool = True
proxy: str | None = ( proxy: str | None = (
None # HTTP/SOCKS5 proxy URL, e.g. "http://127.0.0.1:7890" or "socks5://127.0.0.1:1080" None # HTTP/SOCKS5 proxy URL, e.g. "http://127.0.0.1:7890" or "socks5://127.0.0.1:1080"
) )