Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions benchmarks/browsecomp/config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
config_paths:
- nemo_gym/sandbox/providers/docker/configs/docker.yaml

# Judge: a hosted reasoning model, independent of the policy under test. Keeping
# the judge off the policy endpoint stops a weak policy from grading itself.
judge_model:
Expand Down Expand Up @@ -28,6 +31,11 @@ browsecomp_benchmark_resources_server:
# terminal mode: search/browse write pages to a per-session disk workspace
# and the read-only bash_command tool is exposed for the model to grep/read them.
workspace: per_session
sandbox_provider: sandbox
sandbox_config:
image: python:3.13-slim
workdir: /workspace
ttl_s: 7200
use_judge: true
judge_model_server:
type: responses_api_models
Expand Down
65 changes: 64 additions & 1 deletion resources_servers/browsecomp_advanced_harness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ requiring multi-hop investigation through search and page-level browsing.

## Overview

This resources server provides two browsing tools backed by [Tavily](https://tavily.com/):
This resources server provides browsing tools backed by [Tavily](https://tavily.com/) or Exa:

- **`search`** — parallel web search returning top results with content snippets
- **`browse`** — full-page content extraction from given URLs
- **`bash_command`** — the existing read-only pocketshell interpreter over retrieved pages, executed inside a
resources-owned sandbox when `workspace: per_session` is enabled

Reward is computed via an LLM judge that compares the model's final answer against the ground truth.

Expand All @@ -25,6 +27,67 @@ judge_model_api_key: ""
judge_model_name: Qwen/Qwen3-235B-A22B-Instruct-2507
```

## Workspace sandbox

Workspace tools require a configured sandbox provider. Add these fields under the resources server's
`resources_servers` block and compose the corresponding provider configuration:

```yaml
workspace: per_session
sandbox_provider: sandbox
sandbox_config:
image: python:3.13-slim
workdir: /workspace
ttl_s: 7200
sandbox_install_timeout_seconds: 900
sandbox_close_timeout_seconds: 60
```

For example, composing `nemo_gym/sandbox/providers/docker/configs/docker.yaml` defines the named `sandbox` provider.
The shared sandbox API also supports other providers; Enroot needs an image available on its cluster. Provider
`default_metadata` is merged with `sandbox_config.metadata`, then the resource server and session labels.

Enroot must provide a PID namespace separate from the resources-server host, with `/proc` mounted for that same
namespace. Setup checks the running sandbox's namespace IDs before permitting model commands; a version string
or `ENROOT_UNSHARE_PID` setting alone is insufficient because older Enroot builds may ignore that setting. If the
check fails, upgrade or configure Enroot with working PID isolation, or select another isolating provider. The
failed session follows the usual sandbox cleanup and close-retry path. Only Enroot is probed. Choose any other
provider for sandboxes that cannot see the host filesystem: Apptainer, for example, bind-mounts the host's home
directory and `/tmp` unless configured otherwise.

Each resources session owns one tool sandbox. An agent that runs its loop in a sandbox owns a separate sandbox;
neither component borrows the other's container. This works with providers such as Enroot that do not support
serializing and reconnecting to a sandbox. Search and browse retain their existing responses and write immutable
retrieved pages. Only those pages and their manifest are copied into the tool workspace. Search/judge credentials
and verifier inputs are not copied into the sandbox.

The shell uses the checked-out pocketshell implementation and `SandboxPythonRuntime` with the installed `regex`
version pinned into its isolated Python runtime. The first seed builds that runtime if needed; subsequent sessions
reuse its host-side artifact cache. Setup failures surface to the caller and retain ownership when cleanup fails,
so a later close can retry. Commands are supervised inside the sandbox; their guard, supported commands, output
truncation, and exit-code envelope are unchanged. A command that outlives the supervisor deadline answers
`[exit_code=-1]` and a sandbox failure answers `[exit_code=-2]`, as on the host. There is no fallback to host
execution after a sandbox failure. A sandbox that cannot start or cannot run a command marks its session, and
`/verify` keeps the judged reward but masks the sample (`mask_sample: true`, `failure_kind:
provider_unavailable`), since the model lost its tool to the infrastructure. A blocked command answers without
starting a sandbox. A missing provider block or an unknown `sandbox_config` key refuses the seed with 422.

`allow_host_execution: true` explicitly enables the former host interpreter for development when no provider is
configured, or permits the non-isolating `local` provider. It defaults to `false`; production and benchmark configs
must provide an isolating provider. Configurations with `workspace: none` do not need a tool sandbox.

## Sessions

Each rollout has its own session: the provider-call metrics that `/verify` reports and, with `workspace: per_session`, the disk workspace that `search` and `browse` write pages to and `bash_command` reads.

- **Environment Server.** `/seed_session` takes a `ResourcesSeedSessionRequest` and keys the session by its `resources_session_id`. The seed is refused with 422 when `task_data` lacks the `question` and `ground_truth` that `/verify` needs, or, with a workspace, when the ID is not a plain directory name. `/close_session` removes the workspace and the metrics, and is idempotent. A close that cannot remove the workspace fails and keeps the session, so the caller can retry it. After a close, a seed for the same ID is refused with 409, and a tool call for it gets 404 (`session_lost`) instead of recreating the workspace. `/verify` of a closed session returns a masked sample (`reward: 0`, `mask_sample: true`, `failure_kind: session_lost`). The reward depends only on the response and the task, so the mask is kept on purpose: a closed session reads the same on every server under the resources session contract. `single_agent_turn` verifies before it closes, so only an out-of-order verify gets the mask.
- **An agent's `/run`.** `/seed_session` takes the flat row and keys the session by the cookie. `/verify` releases the sandbox and removes the workspace, since this path sends no close. The sandbox starts with the first `bash_command`, not at the seed. A run whose agent never sends `/verify`, as `browsecomp_agent` does after a timeout or an aborted run, keeps its sandbox until the provider's `ttl_s` reclaims it or the server shuts down; Enroot ignores `ttl_s`. Sandboxed runs should use the Environment Server path, which always closes.

`/verify` and `/close_session` stop the owned sandbox before deleting its workspace and metrics. A failed stop leaves
the session available for a close retry. Shutdown also stops any remaining tool sandboxes. Tool calls, verification,
and close are serialized per session, so cleanup cannot delete files while a tool is running. `/verify` drops the
session's metrics after reporting them, unless `dump_session_id_to_metrics_on_exit` keeps them for the dump at shutdown.

## Running

```bash
Expand Down
Loading
Loading