The API server adapter was creating agents without specifying enabled_toolsets, causing ALL tools to load — including clarify, send_message, and text_to_speech which don't work without interactive callbacks or gateway dispatch. Changes: - toolsets.py: Add hermes-api-server toolset (core tools minus clarify, send_message, text_to_speech) - api_server.py: Resolve toolsets from config.yaml platform_toolsets via _get_platform_tools() — same path as all other gateway platforms. Falls back to hermes-api-server default when no override configured. - tools_config.py: Add api_server to PLATFORMS dict so users can customize via 'hermes tools' or platform_toolsets.api_server in config.yaml - 12 tests covering toolset definition, config resolution, and user override Reported by thatwolfieguy on Discord.
130 lines
5.4 KiB
Python
130 lines
5.4 KiB
Python
"""Tests for hermes-api-server toolset and API server tool availability."""
|
|
import os
|
|
import json
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
import pytest
|
|
|
|
from toolsets import resolve_toolset, get_toolset, validate_toolset
|
|
|
|
|
|
class TestHermesApiServerToolset:
|
|
"""Tests for the hermes-api-server toolset definition."""
|
|
|
|
def test_toolset_exists(self):
|
|
ts = get_toolset("hermes-api-server")
|
|
assert ts is not None
|
|
|
|
def test_toolset_validates(self):
|
|
assert validate_toolset("hermes-api-server")
|
|
|
|
def test_toolset_includes_web_tools(self):
|
|
tools = resolve_toolset("hermes-api-server")
|
|
assert "web_search" in tools
|
|
assert "web_extract" in tools
|
|
|
|
def test_toolset_includes_core_tools(self):
|
|
tools = resolve_toolset("hermes-api-server")
|
|
expected = [
|
|
"terminal", "process",
|
|
"read_file", "write_file", "patch", "search_files",
|
|
"vision_analyze", "image_generate",
|
|
"execute_code", "delegate_task",
|
|
"todo", "memory", "session_search", "cronjob",
|
|
]
|
|
for tool in expected:
|
|
assert tool in tools, f"Missing expected tool: {tool}"
|
|
|
|
def test_toolset_includes_browser_tools(self):
|
|
tools = resolve_toolset("hermes-api-server")
|
|
for tool in ["browser_navigate", "browser_snapshot", "browser_click",
|
|
"browser_type", "browser_scroll", "browser_back",
|
|
"browser_press", "browser_close"]:
|
|
assert tool in tools, f"Missing browser tool: {tool}"
|
|
|
|
def test_toolset_includes_homeassistant_tools(self):
|
|
tools = resolve_toolset("hermes-api-server")
|
|
for tool in ["ha_list_entities", "ha_get_state", "ha_list_services", "ha_call_service"]:
|
|
assert tool in tools, f"Missing HA tool: {tool}"
|
|
|
|
def test_toolset_excludes_clarify(self):
|
|
tools = resolve_toolset("hermes-api-server")
|
|
assert "clarify" not in tools
|
|
|
|
def test_toolset_excludes_send_message(self):
|
|
tools = resolve_toolset("hermes-api-server")
|
|
assert "send_message" not in tools
|
|
|
|
def test_toolset_excludes_text_to_speech(self):
|
|
tools = resolve_toolset("hermes-api-server")
|
|
assert "text_to_speech" not in tools
|
|
|
|
|
|
class TestApiServerPlatformConfig:
|
|
def test_platforms_dict_includes_api_server(self):
|
|
from hermes_cli.tools_config import PLATFORMS
|
|
assert "api_server" in PLATFORMS
|
|
assert PLATFORMS["api_server"]["default_toolset"] == "hermes-api-server"
|
|
|
|
|
|
class TestApiServerAdapterToolset:
|
|
@patch("gateway.platforms.api_server.AIOHTTP_AVAILABLE", True)
|
|
def test_create_agent_reads_config_toolsets(self):
|
|
"""API server resolves toolsets from config like all other platforms."""
|
|
from gateway.platforms.api_server import APIServerAdapter
|
|
from gateway.config import PlatformConfig
|
|
|
|
adapter = APIServerAdapter(PlatformConfig())
|
|
|
|
with patch("gateway.run._resolve_runtime_agent_kwargs") as mock_kwargs, \
|
|
patch("gateway.run._resolve_gateway_model") as mock_model, \
|
|
patch("gateway.run._load_gateway_config") as mock_config, \
|
|
patch("run_agent.AIAgent") as mock_agent_cls:
|
|
|
|
mock_kwargs.return_value = {"api_key": "test-key", "base_url": None,
|
|
"provider": None, "api_mode": None,
|
|
"command": None, "args": []}
|
|
mock_model.return_value = "test/model"
|
|
# No platform_toolsets override — should fall back to hermes-api-server default
|
|
mock_config.return_value = {}
|
|
mock_agent_cls.return_value = MagicMock()
|
|
|
|
adapter._create_agent()
|
|
|
|
mock_agent_cls.assert_called_once()
|
|
call_kwargs = mock_agent_cls.call_args
|
|
toolsets = call_kwargs.kwargs.get("enabled_toolsets")
|
|
assert isinstance(toolsets, list)
|
|
assert len(toolsets) > 0
|
|
assert call_kwargs.kwargs.get("platform") == "api_server"
|
|
|
|
@patch("gateway.platforms.api_server.AIOHTTP_AVAILABLE", True)
|
|
def test_create_agent_respects_config_override(self):
|
|
"""User can override API server toolsets via platform_toolsets in config.yaml."""
|
|
from gateway.platforms.api_server import APIServerAdapter
|
|
from gateway.config import PlatformConfig
|
|
|
|
adapter = APIServerAdapter(PlatformConfig())
|
|
|
|
with patch("gateway.run._resolve_runtime_agent_kwargs") as mock_kwargs, \
|
|
patch("gateway.run._resolve_gateway_model") as mock_model, \
|
|
patch("gateway.run._load_gateway_config") as mock_config, \
|
|
patch("run_agent.AIAgent") as mock_agent_cls:
|
|
|
|
mock_kwargs.return_value = {"api_key": "test-key", "base_url": None,
|
|
"provider": None, "api_mode": None,
|
|
"command": None, "args": []}
|
|
mock_model.return_value = "test/model"
|
|
# User overrides with just web and terminal
|
|
mock_config.return_value = {
|
|
"platform_toolsets": {"api_server": ["web", "terminal"]}
|
|
}
|
|
mock_agent_cls.return_value = MagicMock()
|
|
|
|
adapter._create_agent()
|
|
|
|
mock_agent_cls.assert_called_once()
|
|
call_kwargs = mock_agent_cls.call_args
|
|
toolsets = call_kwargs.kwargs.get("enabled_toolsets")
|
|
assert sorted(toolsets) == ["terminal", "web"]
|