feat: harness agnostic TerminalBench environment#1631
Conversation
89b10ee to
476e57f
Compare
476e57f to
a56c375
Compare
|
/claude review |
There was a problem hiding this comment.
SHIP WITH CARE
New anyterminal_agent environment that runs any Gym agent inside Terminal Bench task containers. The core design is sound — sequential agent→eval in a single Apptainer container, file-based reward communication, Ray-dispatched parallelism. Async patterns are correct (Ray futures awaited, no httpx, no ray.get() in async).
Three findings worth resolving before or shortly after merge:
1. RISK — body.metadata crash (anyterminal_agent/app.py:576)
dict(body.metadata) raises TypeError when metadata is None. Guard with dict(body.metadata or {}). See inline comment.
2. RISK — SIGTERM handler race (hermes_agent/app.py:288-305)
add_signal_handler / remove_signal_handler on the shared event loop is not concurrency-safe. Concurrent responses() calls overwrite each other's handlers. See inline comment for the race sequence and fix options.
3. RISK — Breaking default changes to HermesAgentConfig
max_turns 30→90, temperature 1.0→None, terminal_timeout 60→180, and compression_enabled flipped from hard-coded False to default True. These silently change behavior for all existing hermes_agent users. The compression change is the most impactful — it alters trajectories and eval scores. Consider keeping hermes_agent's existing defaults and putting the new values only in the anyterminal_hermes.yaml config. See inline comment.
4. NOTE — No tests
CLAUDE.md requires test coverage for new environments. There are no tests for anyterminal_agent. At minimum, unit tests for _instruction_from_input, _read_task_meta, reward-file parsing, and the mask_sample logic would catch regressions in the scoring path.
|
can you add 5 example input + rollouts? https://docs.nvidia.com/nemo/gym/main/contribute/environments/new-environment#4-generate-example-rollouts |
090ea70 to
e6da07c
Compare
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: elisam0 <elisam@nvidia.com> Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez <elisam@nvidia.com> Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
e6da07c to
81989a9
Compare
cmunley1
left a comment
There was a problem hiding this comment.
looks good
i have used the older version with various harnesses with good results. will add results here in a day or two, but dont think its blocking. useful would be to reproduce reported TB scores from some leading models.
| os.environ["TERMINAL_TIMEOUT"] = str(self.config.terminal_timeout) | ||
|
|
||
| # Build config.yaml with config parameters | ||
| hermes_home = tempfile.mkdtemp(prefix="hermes_agent_") |
There was a problem hiding this comment.
i wonder if this could cause a memory leak without cleanup
There was a problem hiding this comment.
Added atexit cleanup so the temp dir is removed on process exit. This is a disk resource under /tmp and /tmp is cleared on reboot anyway, so the practical impact is low.
coverage seems a bit low. i dont think we need 96% here but would be nice to get a bit higher, e.g. |
|
some tb2 avg@1 scores from testing this |
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
Signed-off-by: Elisa Martinez Abad <elisam@nvidia.com>
anyterminal_agent
Runs any Gym agent inside a Terminal Bench task container and evaluates the result by executing the task's
tests/test.shinside the same container. Works withhermes_agent,claude_code_agent, or any other compatible Gym agent.Unlike
anyswe_agent(which runs agent and eval in two concurrent containers),anyterminal_agentruns everything sequentially in one container: the agent completes its work, thentest.shruns and writes a binary reward (0.0/1.0) to/logs/verifier/reward.txt. The test directory is mounted read-only so the agent cannot tamper with the tests before evaluation.Quickstart
1. Prepare the dataset — downloads tasks via Harbor and writes the input JSONL:
Requires
harboron PATH. Tasks are cached at~/.cache/harbor/tasks/terminal-bench/.2. Start the environment:
ng_run "+config_paths=[responses_api_agents/anyterminal_agent/configs/anyterminal_hermes.yaml,responses_api_models/vllm_model/configs/vllm_model.yaml]"3. Collect rollouts:
Each rollout row contains
reward(0.0 or 1.0), the full agent trajectory, andmask_samplefor timeouts or unreliable rewards.Agent wiring
Swap the agent by changing three fields in the YAML (or overriding on the CLI):
Agent dependencies install once at startup into a portable Python prefix mounted read-only inside the task container at
/agent_deps_mount. Addsetup_scripts/<agent_dir>_deps.shto support a new agent.Container images
Each Terminal Bench task specifies a Docker image in its
task.toml. You can either:tb_sif_dir: null): Apptainer pullsdocker://<image>on first use. Requires internet access on compute nodes.prepare.py --sif-dir PATH): Converts each image to a.siffile. Faster and works on air-gapped clusters.Key config options
tb_tasks_cache_dir~/.cache/harbor/taskstb_sif_dirnullnull= pull docker:// at runtimetb_agent_timeout1800tb_eval_timeout300test.shto completeapptainer_memory_limit_mb32768concurrency8Hermes agent changes
Updated
HermesAgentConfigto expose Hermes 2.1 configuration fields (compression_enabled,compression_threshold,delegation_max_iterations,checkpoints_enabled) and generate the agent's YAML config dynamically via_build_config(). Also updated defaults:max_turns30 → 90,terminal_timeout60 → 180,temperaturenow defaults toNone(model default).