mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-01 16:31:56 +08:00
89 lines
3.0 KiB
Python
89 lines
3.0 KiB
Python
"""
|
|
Toolset resolution for Hermes-Agent Atropos integration.
|
|
|
|
We primarily reuse Hermes-Agent toolsets (`toolsets.py`), but Atropos training/envs
|
|
need a few extra sandbox-oriented toolsets that Hermes doesn't expose by default
|
|
(e.g. filesystem + stateful terminal).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, List, Optional, Set
|
|
|
|
import toolsets as hermes_toolsets
|
|
|
|
|
|
ATROPOS_TOOLSETS: Dict[str, Dict[str, Any]] = {
|
|
"filesystem": {
|
|
"description": "Read/write files in the sandbox workspace.",
|
|
"tools": ["read_file", "write_file"],
|
|
"includes": [],
|
|
},
|
|
"terminal_stateful": {
|
|
"description": "Stateful terminal execution (tmux/TUI support) inside the sandbox.",
|
|
"tools": ["terminal_stateful", "tmux"],
|
|
"includes": [],
|
|
},
|
|
"sandbox": {
|
|
"description": "Sandbox tools (terminal + filesystem).",
|
|
"tools": [],
|
|
"includes": ["terminal", "filesystem"],
|
|
},
|
|
"default": {
|
|
"description": "Default toolset for Atropos AgentEnv tasks.",
|
|
"tools": [],
|
|
"includes": ["sandbox"],
|
|
},
|
|
"full": {
|
|
"description": "All Hermes tools plus Atropos sandbox additions.",
|
|
"tools": [],
|
|
"includes": ["all", "filesystem", "sandbox", "terminal_stateful"],
|
|
},
|
|
}
|
|
|
|
|
|
def validate_toolset(name: str) -> bool:
|
|
if name in {"all", "*"}:
|
|
return True
|
|
return hermes_toolsets.validate_toolset(name) or name in ATROPOS_TOOLSETS
|
|
|
|
|
|
def resolve_toolset(name: str, visited: Optional[Set[str]] = None) -> List[str]:
|
|
if visited is None:
|
|
visited = set()
|
|
|
|
if name in {"all", "*"}:
|
|
# Union Hermes + Atropos toolsets.
|
|
all_tools: Set[str] = set()
|
|
for tname in hermes_toolsets.get_toolset_names():
|
|
all_tools.update(resolve_toolset(tname, visited=set()))
|
|
for tname, spec in ATROPOS_TOOLSETS.items():
|
|
# Avoid recursion: some Atropos toolsets (e.g. "full") include "all".
|
|
if tname == "full" or "all" in (spec.get("includes") or []):
|
|
continue
|
|
all_tools.update(resolve_toolset(tname, visited=set()))
|
|
return sorted(all_tools)
|
|
|
|
if name in ATROPOS_TOOLSETS:
|
|
if name in visited:
|
|
return []
|
|
visited.add(name)
|
|
spec = ATROPOS_TOOLSETS[name]
|
|
tools: Set[str] = set(spec.get("tools", []))
|
|
for inc in spec.get("includes", []):
|
|
tools.update(resolve_toolset(inc, visited=set(visited)))
|
|
return sorted(tools)
|
|
|
|
# Fall back to Hermes toolsets.
|
|
# IMPORTANT: do not pre-add `name` to `visited` here; Hermes' resolver uses
|
|
# `visited` for its own cycle detection and will treat the presence of `name`
|
|
# as a circular dependency.
|
|
return sorted(hermes_toolsets.resolve_toolset(name, visited=set(visited)))
|
|
|
|
|
|
def resolve_multiple_toolsets(names: List[str]) -> List[str]:
|
|
tools: Set[str] = set()
|
|
for name in names:
|
|
tools.update(resolve_toolset(name, visited=set()))
|
|
return sorted(tools)
|