Something went wrong. Try again.
The agent that grows with you
Something went wrong. Try again.
7.1 kB · 178 lines
Python
at bb/theme
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179"""Tests for ManagedServer / tool-parser integration.
Validates that:1. The installed atroposlib API still matches Hermes's expectations2. Hermes's parser registry remains compatible with ManagedServer parsing3. HermesAgentBaseEnv wires the selected parser into ServerManager correctly
These tests verify the contract between hermes-agent's environments/ codeand atroposlib's ManagedServer. They detect API incompatibilities early."""
import inspectimport sysfrom pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
try: import atroposlib # noqa: F401except ImportError: pytest.skip("atroposlib not installed", allow_module_level=True)
class TestManagedServerAPI: """Test that ManagedServer's API matches what hermes-agent expects."""
def test_managed_server_init_signature(self): """ManagedServer should accept tool_call_parser parameter.""" from atroposlib.envs.server_handling.managed_server import ManagedServer
sig = inspect.signature(ManagedServer.__init__) params = list(sig.parameters.keys())
# Core params that must exist assert "self" in params assert "server" in params assert "tokenizer" in params assert "track_tree" in params
# tool_call_parser — required for tool_call_support branch # If this fails, atroposlib hasn't been updated to tool_call_support has_tool_parser = "tool_call_parser" in params if not has_tool_parser: pytest.skip( "ManagedServer does not have tool_call_parser param — " "baseline atroposlib (pre tool_call_support branch)" )
def test_server_manager_managed_server_signature(self): """ServerManager.managed_server() should accept tool_call_parser.""" from atroposlib.envs.server_handling.server_manager import ServerManager
sig = inspect.signature(ServerManager.managed_server) params = list(sig.parameters.keys())
assert "self" in params assert "tokenizer" in params
has_tool_parser = "tool_call_parser" in params if not has_tool_parser: pytest.skip( "ServerManager.managed_server() does not have tool_call_parser param — " "baseline atroposlib (pre tool_call_support branch)" )
def test_managed_server_chat_template_kwargs(self): """ManagedServer should have CHAT_TEMPLATE_KWARGS for forwarding tools/thinking.""" from atroposlib.envs.server_handling.managed_server import ManagedServer
if not hasattr(ManagedServer, "CHAT_TEMPLATE_KWARGS"): pytest.skip( "ManagedServer does not have CHAT_TEMPLATE_KWARGS — " "baseline atroposlib (pre tool_call_support branch)" )
kwargs = ManagedServer.CHAT_TEMPLATE_KWARGS assert "tools" in kwargs, "tools must be in CHAT_TEMPLATE_KWARGS"
def test_no_get_logprobs_method(self): """get_logprobs should be removed in tool_call_support branch.""" from atroposlib.envs.server_handling.managed_server import ManagedServer
# In baseline, get_logprobs exists. In tool_call_support, it's removed. # We just note the state — not a hard fail either way. has_get_logprobs = hasattr(ManagedServer, "get_logprobs") if has_get_logprobs: pytest.skip( "ManagedServer still has get_logprobs — baseline atroposlib" )
class TestParserCompatibility: """Test that hermes-agent's parsers match ManagedServer's expectations."""
def test_parser_parse_returns_correct_format(self): """ ManagedServer expects parser.parse(text) -> (content, tool_calls) where tool_calls is a list of objects with .id, .function.name, .function.arguments """ from environments.tool_call_parsers import get_parser
parser = get_parser("hermes") text = '<tool_call>{"name": "terminal", "arguments": {"command": "ls"}}</tool_call>' content, tool_calls = parser.parse(text)
assert tool_calls is not None assert len(tool_calls) == 1
tc = tool_calls[0] # ManagedServer accesses these attrs directly assert hasattr(tc, "id") assert hasattr(tc, "function") assert hasattr(tc.function, "name") assert hasattr(tc.function, "arguments")
def test_parser_no_tools_returns_none(self): """ManagedServer checks `if parsed_tool_calls:` — None should be falsy.""" from environments.tool_call_parsers import get_parser
parser = get_parser("hermes") content, tool_calls = parser.parse("Just text, no tools") assert tool_calls is None
def test_parser_content_is_string_or_none(self): """ManagedServer uses `parsed_content or ""` — must be str or None.""" from environments.tool_call_parsers import get_parser
parser = get_parser("hermes")
# With tool calls text = '<tool_call>{"name": "terminal", "arguments": {"command": "ls"}}</tool_call>' content, _ = parser.parse(text) assert content is None or isinstance(content, str)
# Without tool calls content2, _ = parser.parse("Just text") assert isinstance(content2, str)
class TestBaseEnvCompatibility: """Test that hermes_base_env.py's tool-parser wiring matches the current API."""
def test_hermes_base_env_sets_server_manager_tool_parser(self): """Hermes wires parser selection through ServerManager.tool_parser.""" import ast
base_env_path = Path(__file__).parent.parent.parent / "environments" / "hermes_base_env.py" source = base_env_path.read_text() tree = ast.parse(source)
found_assignment = False for node in ast.walk(tree): if isinstance(node, ast.Assign): for target in node.targets: if isinstance(target, ast.Attribute) and target.attr == "tool_parser": parent = target.value if ( isinstance(parent, ast.Attribute) and parent.attr == "server" and isinstance(parent.value, ast.Name) and parent.value.id == "self" ): found_assignment = True
assert found_assignment, ( "hermes_base_env.py should set self.server.tool_parser from config.tool_call_parser" )
def test_hermes_base_env_uses_config_tool_call_parser(self): """Verify hermes_base_env uses the config field rather than a local parser instance.""" base_env_path = Path(__file__).parent.parent.parent / "environments" / "hermes_base_env.py" source = base_env_path.read_text()
assert 'tool_call_parser: str = Field(' in source assert 'self.server.tool_parser = config.tool_call_parser' in source