From 3b89a50aad59462c2266d4c51e4f7d8cc35bba01 Mon Sep 17 00:00:00 2001 From: Teknium Date: Thu, 26 Mar 2026 16:04:39 -0700 Subject: [PATCH] fix: add explicit hermes-api-server toolset for API server platform The API server adapter was creating agents without specifying enabled_toolsets, causing ALL tools from ALL toolsets to be loaded (including clarify, send_message, and text_to_speech which don't work without interactive callbacks or gateway dispatch). This could confuse models by presenting too many irrelevant tools, and meant the platform_toolsets config override didn't apply to API server. Changes: - Add hermes-api-server toolset to toolsets.py with appropriate tools (web, terminal, files, browser, vision, skills, HA tools, etc.) but excluding clarify, send_message, and text_to_speech - Update _create_agent() in api_server.py to use enabled_toolsets=[hermes-api-server] - Add api_server to PLATFORMS dict in tools_config.py for config override support - Add tests for toolset definition, tool inclusion/exclusion, and adapter wiring --- gateway/platforms/api_server.py | 1 + hermes_cli/tools_config.py | 1 + tests/gateway/test_api_server_toolset.py | 93 ++++++++++++++++++++++++ toolsets.py | 36 +++++++++ 4 files changed, 131 insertions(+) create mode 100644 tests/gateway/test_api_server_toolset.py diff --git a/gateway/platforms/api_server.py b/gateway/platforms/api_server.py index 425613cb5663..664c54e55b39 100644 --- a/gateway/platforms/api_server.py +++ b/gateway/platforms/api_server.py @@ -383,6 +383,7 @@ class APIServerAdapter(BasePlatformAdapter): quiet_mode=True, verbose_logging=False, ephemeral_system_prompt=ephemeral_system_prompt or None, + enabled_toolsets=["hermes-api-server"], session_id=session_id, platform="api_server", stream_delta_callback=stream_delta_callback, diff --git a/hermes_cli/tools_config.py b/hermes_cli/tools_config.py index 6f76c98aea63..0f5390c87172 100644 --- a/hermes_cli/tools_config.py +++ b/hermes_cli/tools_config.py @@ -134,6 +134,7 @@ PLATFORMS = { "homeassistant": {"label": "🏠 Home Assistant", "default_toolset": "hermes-homeassistant"}, "email": {"label": "📧 Email", "default_toolset": "hermes-email"}, "dingtalk": {"label": "💬 DingTalk", "default_toolset": "hermes-dingtalk"}, + "api_server": {"label": "🌐 API Server", "default_toolset": "hermes-api-server"}, } diff --git a/tests/gateway/test_api_server_toolset.py b/tests/gateway/test_api_server_toolset.py new file mode 100644 index 000000000000..420b80a615f1 --- /dev/null +++ b/tests/gateway/test_api_server_toolset.py @@ -0,0 +1,93 @@ +"""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_uses_api_server_toolset(self): + 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("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" + mock_agent_cls.return_value = MagicMock() + + adapter._create_agent() + + mock_agent_cls.assert_called_once() + call_kwargs = mock_agent_cls.call_args + assert call_kwargs.kwargs.get("enabled_toolsets") == ["hermes-api-server"] + assert call_kwargs.kwargs.get("platform") == "api_server" diff --git a/toolsets.py b/toolsets.py index 1f6a0674d08f..c9f39e75fab6 100644 --- a/toolsets.py +++ b/toolsets.py @@ -248,6 +248,42 @@ TOOLSETS = { ], "includes": [] }, + + "hermes-api-server": { + "description": "OpenAI-compatible API server — full agent tools accessible via HTTP (no interactive UI tools like clarify or send_message)", + "tools": [ + # Web + "web_search", "web_extract", + # Terminal + process management + "terminal", "process", + # File manipulation + "read_file", "write_file", "patch", "search_files", + # Vision + image generation + "vision_analyze", "image_generate", + # MoA + "mixture_of_agents", + # Skills + "skills_list", "skill_view", "skill_manage", + # Browser automation + "browser_navigate", "browser_snapshot", "browser_click", + "browser_type", "browser_scroll", "browser_back", + "browser_press", "browser_close", "browser_get_images", + "browser_vision", "browser_console", + # Planning & memory + "todo", "memory", + # Session history search + "session_search", + # Code execution + delegation + "execute_code", "delegate_task", + # Cronjob management + "cronjob", + # Home Assistant smart home control (gated on HASS_TOKEN via check_fn) + "ha_list_entities", "ha_get_state", "ha_list_services", "ha_call_service", + # Honcho memory tools (gated on honcho being active via check_fn) + "honcho_context", "honcho_profile", "honcho_search", "honcho_conclude", + ], + "includes": [] + }, "hermes-cli": { "description": "Full interactive CLI toolset - all default tools plus cronjob management",