From 3951eab39970dddc5f3244a62cae6d9238973f33 Mon Sep 17 00:00:00 2001 From: Shannon Sands Date: Tue, 10 Feb 2026 09:22:22 +0000 Subject: [PATCH] fixed bug in check terminal requirements for slot pool --- atropos/__init__.py | 7 ++++--- atropos/envs/__init__.py | 14 +++++++++++--- environments/hermes_base_env.py | 26 ++++++++++++++++++++------ tools/terminal_tool.py | 13 ++++++------- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/atropos/__init__.py b/atropos/__init__.py index 18bfed5df2..48432f1f7f 100644 --- a/atropos/__init__.py +++ b/atropos/__init__.py @@ -31,8 +31,11 @@ def _require_atroposlib() -> None: _require_atroposlib() # Re-export the most commonly used pieces for convenience. +# Agent imports are eager (always available). from .agent import AgentConfig, AgentResult, AgentStep, AtroposAgent, SequenceData # noqa: E402 -from .envs import AgentEnv, AgentEnvConfig # noqa: E402 + +# Env imports are lazy to avoid pulling in deleted atropos.tools dependencies. +# Use: from atropos.envs import AgentEnv, AgentEnvConfig (if needed) __all__ = [ "AtroposAgent", @@ -40,7 +43,5 @@ __all__ = [ "AgentResult", "AgentStep", "SequenceData", - "AgentEnv", - "AgentEnvConfig", ] diff --git a/atropos/envs/__init__.py b/atropos/envs/__init__.py index c56655345f..73fcea601c 100644 --- a/atropos/envs/__init__.py +++ b/atropos/envs/__init__.py @@ -1,10 +1,18 @@ """ Environment implementations for atropos-agent. + +NOTE: AgentEnv is the OLD environment system, replaced by +environments/hermes_base_env.py (HermesAgentBaseEnv). +Import is lazy to avoid pulling in deleted dependencies. """ -from .agent_env import AgentEnv, AgentEnvConfig -# NOTE: Additional example envs exist as modules (e.g. `test_env`, `swe_smith_oracle_env`), -# but are intentionally not imported here to avoid pulling heavy optional deps at import time. +def __getattr__(name): + """Lazy import to avoid breaking when old dependencies are removed.""" + if name in ("AgentEnv", "AgentEnvConfig"): + from .agent_env import AgentEnv, AgentEnvConfig + return {"AgentEnv": AgentEnv, "AgentEnvConfig": AgentEnvConfig}[name] + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + __all__ = ["AgentEnv", "AgentEnvConfig"] diff --git a/environments/hermes_base_env.py b/environments/hermes_base_env.py index 8dc8b697fb..27cf6e90fe 100644 --- a/environments/hermes_base_env.py +++ b/environments/hermes_base_env.py @@ -332,8 +332,13 @@ class HermesAgentBaseEnv(BaseEnv): os.environ.setdefault("TERMINAL_NOMAD_MIN", str(self.config.min_containers)) os.environ.setdefault("TERMINAL_NOMAD_MAX", str(self.config.max_containers)) + # Eagerly start the _SlotPoolManager so the backend is ready + # before any trajectories try to use it + from tools.terminal_tool import _SlotPoolManager + _SlotPoolManager.get_instance() # Triggers _start() which creates sandboxes + self._sandbox_backend = True # Flag that sandbox mode is active - print(f"🔧 Slot pool configured: TERMINAL_ENV=slot_pool, backend={mode}") + print(f"🔧 Slot pool started: TERMINAL_ENV=slot_pool, backend={mode}") async def _stop_sandbox_backend(self) -> None: """Stop the slot pool backend.""" @@ -710,18 +715,27 @@ class HermesAgentBaseEnv(BaseEnv): logger.info("Sandbox slot acquired for task %s", task_id) # 2. Create exec_tool for setup/verify hooks - # Routes through _SlotPoolManager (same slot as terminal_tool) + # Routes through handle_function_call → terminal_tool → same _SlotPoolEnvironment async def exec_tool(tool_name: str, args: Dict[str, Any], timeout: float = 300) -> _ExecResult: command = args.get("command", "") - result_dict = _SlotPoolManager.get_instance().execute( - task_id, command, timeout=timeout + result_json = await loop.run_in_executor( + None, + lambda: handle_function_call( + "terminal", + {"command": command, "timeout": int(timeout)}, + task_id=task_id, + ), ) - returncode = result_dict.get("returncode", 1) + try: + result_dict = json.loads(result_json) + except (json.JSONDecodeError, TypeError): + result_dict = {"output": str(result_json), "exit_code": 1} + returncode = result_dict.get("exit_code", result_dict.get("returncode", 1)) output = result_dict.get("output", "") return _ExecResult( success=(returncode == 0), output=output, - error="" if returncode == 0 else f"Exit code: {returncode}", + error=result_dict.get("error", "") if returncode != 0 else "", metadata={"returncode": returncode}, ) diff --git a/tools/terminal_tool.py b/tools/terminal_tool.py index 950c7205ab..878aa062bd 100644 --- a/tools/terminal_tool.py +++ b/tools/terminal_tool.py @@ -2411,17 +2411,14 @@ def check_terminal_requirements() -> bool: try: if env_type == "local": - from minisweagent.environments.local import LocalEnvironment return True elif env_type == "docker": - from minisweagent.environments.docker import DockerEnvironment - # Check if docker is available + # check actually available.. import subprocess result = subprocess.run(["docker", "version"], capture_output=True, timeout=5) return result.returncode == 0 elif env_type == "singularity": - from minisweagent.environments.singularity import SingularityEnvironment - # Check if singularity/apptainer is available + # Check if singularity/apptainer is available (doesn't work on mac) import subprocess import shutil executable = shutil.which("apptainer") or shutil.which("singularity") @@ -2430,9 +2427,11 @@ def check_terminal_requirements() -> bool: return result.returncode == 0 return False elif env_type == "modal": - from minisweagent.environments.extra.swerex_modal import SwerexModalEnvironment - # Check for modal token + # check modal is actually configured return os.getenv("MODAL_TOKEN_ID") is not None or Path.home().joinpath(".modal.toml").exists() + elif env_type == "slot_pool": + # Slot pool uses atropos/backends/ & always available if modal/nomad is configured + return True else: return False except Exception as e: