mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-31 08:13:11 +03:00
refactor(agent): load MyTool through tool loader
This commit is contained in:
+1
-11
@@ -41,7 +41,6 @@ from nanobot.agent.tools.file_state import FileStateStore, bind_file_states, res
|
|||||||
from nanobot.agent.tools.message import MessageTool
|
from nanobot.agent.tools.message import MessageTool
|
||||||
from nanobot.agent.tools.registry import ToolRegistry
|
from nanobot.agent.tools.registry import ToolRegistry
|
||||||
from nanobot.agent.tools.runtime_control import AgentRuntimeControl
|
from nanobot.agent.tools.runtime_control import AgentRuntimeControl
|
||||||
from nanobot.agent.tools.self import MyTool
|
|
||||||
from nanobot.agent.turn_delivery import (
|
from nanobot.agent.turn_delivery import (
|
||||||
TurnDelivery,
|
TurnDelivery,
|
||||||
TurnDeliveryFactory,
|
TurnDeliveryFactory,
|
||||||
@@ -644,20 +643,11 @@ class AgentLoop:
|
|||||||
timezone=self.context.timezone or "UTC",
|
timezone=self.context.timezone or "UTC",
|
||||||
workspace_sandbox=self.workspace_scopes.sandbox_status,
|
workspace_sandbox=self.workspace_scopes.sandbox_status,
|
||||||
runtime_events=self.runtime_events,
|
runtime_events=self.runtime_events,
|
||||||
|
runtime_control=AgentRuntimeControl(self),
|
||||||
)
|
)
|
||||||
loader = ToolLoader()
|
loader = ToolLoader()
|
||||||
registered = loader.load(ctx, self.tools)
|
registered = loader.load(ctx, self.tools)
|
||||||
|
|
||||||
# MyTool receives only the explicit runtime-control capability.
|
|
||||||
if self.tools_config.my.enable:
|
|
||||||
self.tools.register(
|
|
||||||
MyTool(
|
|
||||||
runtime_control=AgentRuntimeControl(self),
|
|
||||||
modify_allowed=self.tools_config.my.allow_set,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
registered.append("my")
|
|
||||||
|
|
||||||
logger.info("Registered {} tools: {}", len(registered), registered)
|
logger.info("Registered {} tools: {}", len(registered), registered)
|
||||||
|
|
||||||
def register_runtime_context_provider(
|
def register_runtime_context_provider(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ if TYPE_CHECKING:
|
|||||||
from nanobot.agent.subagent import SubagentManager
|
from nanobot.agent.subagent import SubagentManager
|
||||||
from nanobot.agent.tools.exec_session import ExecSessionManager
|
from nanobot.agent.tools.exec_session import ExecSessionManager
|
||||||
from nanobot.agent.tools.file_state import FileStates
|
from nanobot.agent.tools.file_state import FileStates
|
||||||
|
from nanobot.agent.tools.runtime_control import RuntimeControl
|
||||||
from nanobot.bus.queue import MessageBus
|
from nanobot.bus.queue import MessageBus
|
||||||
from nanobot.bus.runtime_events import RuntimeEventBus
|
from nanobot.bus.runtime_events import RuntimeEventBus
|
||||||
from nanobot.config.schema import ProviderConfig, ToolsConfig
|
from nanobot.config.schema import ProviderConfig, ToolsConfig
|
||||||
@@ -90,3 +91,4 @@ class ToolContext:
|
|||||||
timezone: str = "UTC"
|
timezone: str = "UTC"
|
||||||
workspace_sandbox: WorkspaceSandboxStatus | None = None
|
workspace_sandbox: WorkspaceSandboxStatus | None = None
|
||||||
runtime_events: RuntimeEventBus | None = None
|
runtime_events: RuntimeEventBus | None = None
|
||||||
|
runtime_control: RuntimeControl | None = None
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ def _is_string_mapping(value: object) -> TypeGuard[Mapping[str, object]]:
|
|||||||
class MyTool(Tool):
|
class MyTool(Tool):
|
||||||
"""Check and set the agent loop's runtime configuration."""
|
"""Check and set the agent loop's runtime configuration."""
|
||||||
|
|
||||||
_plugin_discoverable = False # Requires AgentLoop reference; registered manually
|
|
||||||
config_key = "my"
|
config_key = "my"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -67,7 +66,16 @@ class MyTool(Tool):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def enabled(cls, ctx: ToolContext) -> bool:
|
def enabled(cls, ctx: ToolContext) -> bool:
|
||||||
return ctx.config.my.enable
|
return ctx.runtime_control is not None and ctx.config.my.enable
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, ctx: ToolContext) -> Tool:
|
||||||
|
if ctx.runtime_control is None:
|
||||||
|
raise RuntimeError("MyTool requires a runtime control capability")
|
||||||
|
return cls(
|
||||||
|
runtime_control=ctx.runtime_control,
|
||||||
|
modify_allowed=ctx.config.my.allow_set,
|
||||||
|
)
|
||||||
|
|
||||||
BLOCKED = frozenset({
|
BLOCKED = frozenset({
|
||||||
# Core infrastructure
|
# Core infrastructure
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ def test_tool_context_has_required_fields():
|
|||||||
"config", "workspace", "bus", "subagent_manager",
|
"config", "workspace", "bus", "subagent_manager",
|
||||||
"cron_service", "exec_session_manager", "file_state_store",
|
"cron_service", "exec_session_manager", "file_state_store",
|
||||||
"provider_snapshot_loader", "image_generation_provider_configs", "timezone",
|
"provider_snapshot_loader", "image_generation_provider_configs", "timezone",
|
||||||
|
"runtime_control",
|
||||||
}
|
}
|
||||||
assert required <= field_names
|
assert required <= field_names
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ def test_tool_context_defaults():
|
|||||||
assert ctx.exec_session_manager is None
|
assert ctx.exec_session_manager is None
|
||||||
assert ctx.provider_snapshot_loader is None
|
assert ctx.provider_snapshot_loader is None
|
||||||
assert ctx.image_generation_provider_configs is None
|
assert ctx.image_generation_provider_configs is None
|
||||||
|
assert ctx.runtime_control is None
|
||||||
assert ctx.timezone == "UTC"
|
assert ctx.timezone == "UTC"
|
||||||
|
|
||||||
|
|
||||||
@@ -91,6 +93,7 @@ def test_discover_finds_concrete_tools():
|
|||||||
assert "ExecTool" in class_names
|
assert "ExecTool" in class_names
|
||||||
assert "CliAppsTool" in class_names
|
assert "CliAppsTool" in class_names
|
||||||
assert "MessageTool" in class_names
|
assert "MessageTool" in class_names
|
||||||
|
assert "MyTool" in class_names
|
||||||
assert "SpawnTool" in class_names
|
assert "SpawnTool" in class_names
|
||||||
assert "ExecSessionTool" in class_names
|
assert "ExecSessionTool" in class_names
|
||||||
|
|
||||||
@@ -373,12 +376,26 @@ def test_my_tool_enabled():
|
|||||||
from nanobot.agent.tools.self import MyTool
|
from nanobot.agent.tools.self import MyTool
|
||||||
mock_config = MagicMock()
|
mock_config = MagicMock()
|
||||||
mock_config.my.enable = True
|
mock_config.my.enable = True
|
||||||
ctx = ToolContext(config=mock_config, workspace="/tmp")
|
ctx = ToolContext(
|
||||||
|
config=mock_config,
|
||||||
|
workspace="/tmp",
|
||||||
|
runtime_control=MagicMock(),
|
||||||
|
)
|
||||||
assert MyTool.enabled(ctx) is True
|
assert MyTool.enabled(ctx) is True
|
||||||
mock_config.my.enable = False
|
mock_config.my.enable = False
|
||||||
assert MyTool.enabled(ctx) is False
|
assert MyTool.enabled(ctx) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_my_tool_requires_runtime_control():
|
||||||
|
from nanobot.agent.tools.self import MyTool
|
||||||
|
|
||||||
|
mock_config = MagicMock()
|
||||||
|
mock_config.my.enable = True
|
||||||
|
ctx = ToolContext(config=mock_config, workspace="/tmp")
|
||||||
|
|
||||||
|
assert MyTool.enabled(ctx) is False
|
||||||
|
|
||||||
|
|
||||||
def test_mcp_wrappers_not_discoverable():
|
def test_mcp_wrappers_not_discoverable():
|
||||||
from nanobot.agent.tools.mcp import MCPPromptWrapper, MCPResourceWrapper, MCPToolWrapper
|
from nanobot.agent.tools.mcp import MCPPromptWrapper, MCPResourceWrapper, MCPToolWrapper
|
||||||
assert MCPToolWrapper._plugin_discoverable is False
|
assert MCPToolWrapper._plugin_discoverable is False
|
||||||
@@ -411,6 +428,7 @@ def test_loader_registers_same_tools_as_old_hardcoded():
|
|||||||
mock_config.web.user_agent = None
|
mock_config.web.user_agent = None
|
||||||
mock_config.image_generation.enabled = False
|
mock_config.image_generation.enabled = False
|
||||||
mock_config.my.enable = True
|
mock_config.my.enable = True
|
||||||
|
mock_config.my.allow_set = False
|
||||||
|
|
||||||
ctx = ToolContext(
|
ctx = ToolContext(
|
||||||
config=mock_config,
|
config=mock_config,
|
||||||
@@ -419,6 +437,7 @@ def test_loader_registers_same_tools_as_old_hardcoded():
|
|||||||
subagent_manager=MagicMock(),
|
subagent_manager=MagicMock(),
|
||||||
cron_service=MagicMock(),
|
cron_service=MagicMock(),
|
||||||
timezone="UTC",
|
timezone="UTC",
|
||||||
|
runtime_control=MagicMock(),
|
||||||
)
|
)
|
||||||
registry = ToolRegistry()
|
registry = ToolRegistry()
|
||||||
loader = ToolLoader()
|
loader = ToolLoader()
|
||||||
@@ -429,6 +448,7 @@ def test_loader_registers_same_tools_as_old_hardcoded():
|
|||||||
"find_files", "grep", "exec", "exec_session", "list_exec_sessions",
|
"find_files", "grep", "exec", "exec_session", "list_exec_sessions",
|
||||||
"web_search", "web_fetch",
|
"web_search", "web_fetch",
|
||||||
"message", "spawn", "cron",
|
"message", "spawn", "cron",
|
||||||
|
"my",
|
||||||
}
|
}
|
||||||
actual = set(registered)
|
actual = set(registered)
|
||||||
assert expected <= actual, f"Missing tools: {expected - actual}"
|
assert expected <= actual, f"Missing tools: {expected - actual}"
|
||||||
|
|||||||
Reference in New Issue
Block a user