Skip to content

Add sandbox API and mini SWE agent 2 OpenSandbox evaluation - #1368

Closed
hemildesai wants to merge 24 commits into
NVIDIA-NeMo:mainfrom
hemildesai:hemil/sandbox-api
Closed

Add sandbox API and mini SWE agent 2 OpenSandbox evaluation#1368
hemildesai wants to merge 24 commits into
NVIDIA-NeMo:mainfrom
hemildesai:hemil/sandbox-api

Conversation

@hemildesai

Copy link
Copy Markdown
Contributor

Summary

Links #1337

This PR adds the sandbox-backed SWE evaluation path centered on OpenSandbox and mini SWE agent 2.

Features

  • Adds the provider-neutral nemo_gym.sandbox API with sync/async facades, sandbox handles/specs, provider registration, image rewrite support, and operation observability.
  • Adds an OpenSandbox provider with create/connect/exec/file/close support, SDK pool batch creation, retry handling, create probes, explicit sandbox.opensandbox.* event names, and grouped provider config sections: connection, create, probe, operations, and pool.
  • Adds sandbox observability recorders/exporters for local artifacts and OpenTelemetry traces, with config-driven setup for mini SWE agent 2.
  • Adds responses_api_agents/mini_swe_agent_2, a sandbox-backed mini-swe-agent v2 integration for SWE-bench style eval, including per-task sandbox config, resource profiles, rollout metadata, and ng_collect_rollouts examples.
  • Updates mini SWE agent 2 README into a unified eval-focused guide with server launch, rollout collection, profiling, observability, and sandbox environment adapter docs.
  • Moves sandbox dependencies into a separate nemo-gym[sandbox] optional extra and removes the provider-local OpenSandbox requirements file.
  • Removes the old sandbox diagnostics helper path and references.

User Impact

Users can run mini SWE agent 2 evaluations through the public Gym sandbox facade instead of embedding provider-specific runtime logic in the agent. OpenSandbox settings are now grouped by concern, which keeps the mini SWE config easier to scan and avoids misleading batch_create_* create retry names.

Validation

Completed locally:

  • uv run ruff check nemo_gym/sandbox/providers/opensandbox/provider.py nemo_gym/sandbox/providers/opensandbox/__init__.py tests/unit_tests/test_opensandbox_provider.py tests/unit_tests/test_sandbox.py
  • uv run pytest tests/unit_tests/test_opensandbox_provider.py tests/unit_tests/test_sandbox.py responses_api_agents/mini_swe_agent_2/tests/test_app.py responses_api_agents/mini_swe_agent_2/tests/test_sandbox_environment.py -q
    • Result: 57 passed, 2 warnings
  • uv run coverage run --source=nemo_gym.sandbox,responses_api_agents.mini_swe_agent_2 -m pytest tests/unit_tests/test_sandbox.py tests/unit_tests/test_opensandbox_provider.py responses_api_agents/mini_swe_agent_2/tests/test_app.py responses_api_agents/mini_swe_agent_2/tests/test_sandbox_environment.py -q
  • uv run coverage report --include='nemo_gym/sandbox/*,responses_api_agents/mini_swe_agent_2/*' --fail-under=90
    • Result: 91% focused source coverage
  • git diff --check
  • Parsed responses_api_agents/mini_swe_agent_2/configs/mini_swe_agent_opensandbox.yaml and instantiated OpenSandboxProvider(**kwargs) from its nested provider config.

Validation plan before marking ready for review:

  1. Run the focused unit test and coverage suite again in CI or a clean dev image.
  2. Launch the mini SWE agent 2 stack with the OpenSandbox config and observability enabled:
ng_run \
  "+config_paths=[responses_api_agents/mini_swe_agent_2/configs/mini_swe_agent_opensandbox.yaml,responses_api_models/vllm_model/configs/vllm_model.yaml]" \
  +mini_swe_agent_2.responses_api_agents.mini_swe_agent_2.observability.enabled=true \
  +mini_swe_agent_2.responses_api_agents.mini_swe_agent_2.observability.output_dir=results/mini_swe_agent_2_observability/{trajectory_id}
  1. Smoke-test rollout collection on a small SWE-bench Verified shard with traces enabled:
ng_collect_rollouts \
  +agent_name=mini_swe_agent_2 \
  +input_jsonl_fpath=/mnt/rl-workspace/hemild/gym_eval/refactor/inputs/mini_swe_verified_smoke8.jsonl \
  +output_jsonl_fpath=results/mini_swe_agent_2/swebench_verified_smoke8.rollouts.jsonl \
  +num_repeats=8 \
  +num_samples_in_parallel=64 \
  '+responses_create_params={max_output_tokens: 32768, temperature: 0.6, top_p: 0.95, metadata: {chat_template_kwargs: "{\"enable_thinking\": true}"}}'
  1. Profile the smoke shard and inspect observability artifacts:
ng_reward_profile \
  +input_jsonl_fpath=/mnt/rl-workspace/hemild/gym_eval/refactor/inputs/mini_swe_verified_smoke8.jsonl \
  +rollouts_jsonl_fpath=results/mini_swe_agent_2/swebench_verified_smoke8.rollouts.jsonl \
  +output_jsonl_fpath=results/mini_swe_agent_2/swebench_verified_smoke8.profiled.jsonl \
  +pass_threshold=1.0
  1. Final merge-gate validation: run the full SWE-bench Verified workflow with observability enabled, collect rollouts for the full Verified input JSONL, profile rewards, and confirm every trajectory has the expected sandbox/OpenSandbox spans and trace artifacts under results/mini_swe_agent_2_observability/{trajectory_id}.

hemildesai added 22 commits May 18, 2026 11:41
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented May 19, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Hemil Desai <hemild@nvidia.com>
yield
return
async with recorder.span(name, phase=phase, attributes=attributes):
yield

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need all this otel stuff immediately?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, breaking this down to a smaller PR without the observability module.


A NeMo Gym Responses API agent that integrates
[mini-swe-agent](https://github.com/SWE-agent/mini-swe-agent) v2 for evaluating
language models on SWE-bench style software engineering tasks through the public

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have a simple hello world like math with python tool?

return AsyncRetrying, retry_if_exception, stop_after_attempt, wait_random_exponential


def _httpx_retryable_types() -> tuple[type[BaseException], ...]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we use aiohttp instead or this is a hard opensandbox requirement?

Comment thread nemo_gym/sandbox/api.py

This module is the boundary Gym code should use when it needs a sandbox.
Provider packages implement the lower-level async protocol; callers use
``AsyncSandbox`` in async code and ``Sandbox`` in synchronous integrations.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe default should be async?

@cmunley1 cmunley1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you check the steps here, such as example.jsonl and example_rollouts.jsonl

https://docs.nvidia.com/nemo/gym/main/contribute/environments/new-environment/#contribution-workflow

Comment thread pyproject.toml
"results/*",
"/tmp/*",
"benchmarks/*",
"nemo_gym/sandbox/observability/recorder.py",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why exclude?



class _ObservedModel:
"""Add an OTel span around each mini-SWE model query."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will each sandbox-using env have to do this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is only to capture llm calls in otel telemetry and is optional

)

print(f"[EVAL]{instance_id} Running eval", flush=True)
eval_report = _run_eval_v2(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this verify step become a resources server so that eval logic can be reused across agent harness? maybe this is out of scope for sandbox poc though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For SWE I think it needs to run on a particular image and each task will likely have a different image IIUC?

@hemildesai

Copy link
Copy Markdown
Contributor Author

We are going to break this down into smaller PRs for easier review. The first split keeps the scope to the sandbox API, OpenSandbox provider, and Mini SWE Agent 2 evaluation integration, without the observability module.

@hemildesai hemildesai closed this May 20, 2026
hemildesai added a commit that referenced this pull request Jun 23, 2026
## Summary

Parent PR: #1368

Refs #1337

This is the first smaller PR split out from #1368. It keeps the scope to
the provider-neutral sandbox API, the OpenSandbox provider, and the Mini
SWE Agent 2 evaluation integration. Observability is intentionally left
out for a follow-up PR.

### Features

- Adds the public `nemo_gym.sandbox` facade with async and sync sandbox
clients, provider registration, image rewrite support, sandbox
specs/handles, and batch create support.
- Adds the OpenSandbox provider with create/connect/exec/file/close
operations, SDK pool-backed batch creation, retry handling, create
probes, direct exec endpoint support, and nested provider configuration
sections.
- Adds `responses_api_agents/mini_swe_agent_2`, a sandbox-backed
mini-swe-agent v2 integration for SWE-bench style evals, including
sandbox resource profiles, task metadata propagation, reward
aggregation, and `ng_collect_rollouts` usage docs.
- Adds focused unit coverage for the sandbox facade, provider registry,
OpenSandbox provider behavior, Mini SWE Agent 2 run/aggregation
behavior, and sandbox environment adapter.
- Moves sandbox-related dependencies behind the `nemo-gym[sandbox]`
optional extra.

### Notes

- This PR does not include the sandbox observability module from #1368.
- The Mini SWE Agent 2 README avoids internal deployment names and
user-specific paths; examples use placeholders and local `data/` /
`results/` paths.

## Validation

Completed on the squashed commit:

```bash
uv run ruff check nemo_gym/sandbox responses_api_agents/mini_swe_agent_2 tests/unit_tests/test_sandbox.py tests/unit_tests/test_opensandbox_provider.py
```

Result: `All checks passed!`

```bash
uv run pytest tests/unit_tests/test_sandbox.py tests/unit_tests/test_opensandbox_provider.py responses_api_agents/mini_swe_agent_2/tests/test_app.py responses_api_agents/mini_swe_agent_2/tests/test_sandbox_environment.py -q
```

Result: `44 passed, 2 warnings`

```bash
uv run coverage run --source=nemo_gym.sandbox,responses_api_agents.mini_swe_agent_2 -m pytest tests/unit_tests/test_sandbox.py tests/unit_tests/test_opensandbox_provider.py responses_api_agents/mini_swe_agent_2/tests/test_app.py responses_api_agents/mini_swe_agent_2/tests/test_sandbox_environment.py -q
uv run coverage combine results
uv run coverage report --include='nemo_gym/sandbox/*,responses_api_agents/mini_swe_agent_2/*' --fail-under=90
```

Result: focused coverage `92%`.

Kubernetes smoke validation:

- Model: `Qwen/Qwen3.5-27B`, served by SGLang with DFLASH draft model
`z-lab/Qwen3.5-27B-DFlash`.
- Launched the Mini SWE Agent 2 stack through the documented
`ng_collect_rollouts` path.
- Ran 8 SWE-bench Verified samples with 8 repeats and 64-way rollout
concurrency against the OpenSandbox internal service path.
- Result: 64/64 rollout rows, `pass@8=0.875`, 7/8 tasks resolved, mean
reward `0.765625`, eval error rate `0.0`, reward profile completion
`100%`.
- Cleanup completed with no leftover sandboxes for the successful
internal-service run.

Full SWE-bench Verified validation:

- Model: `Qwen/Qwen3.5-27B`, served by SGLang with DFLASH draft model
`z-lab/Qwen3.5-27B-DFlash`.
- Ran 500 SWE-bench Verified samples with pass@1, 500-way rollout
concurrency, `step_limit=250`, and OpenSandbox cleanup metadata.
- Result: 500/500 rollout rows, `pass@1=0.698`, 349/500 tasks resolved,
mean reward `0.698`, eval error rate `0.6`, tests status rate `99.0`,
reward profile completion `100%`.
- Job duration: `4h6m`; rollout collection duration: `4h04m`.
- Cleanup left no sandboxes with the run labels
`run_family=mini-swe2-firstpr-q35-cell-full-p1-r9` or
`cleanup_id=full-p1-r9-single-20260521-053410`.

---------

Signed-off-by: Hemil Desai <hemild@nvidia.com>
ritaneves pushed a commit that referenced this pull request Jun 25, 2026
## Summary

Parent PR: #1368

Refs #1337

This is the first smaller PR split out from #1368. It keeps the scope to
the provider-neutral sandbox API, the OpenSandbox provider, and the Mini
SWE Agent 2 evaluation integration. Observability is intentionally left
out for a follow-up PR.

### Features

- Adds the public `nemo_gym.sandbox` facade with async and sync sandbox
clients, provider registration, image rewrite support, sandbox
specs/handles, and batch create support.
- Adds the OpenSandbox provider with create/connect/exec/file/close
operations, SDK pool-backed batch creation, retry handling, create
probes, direct exec endpoint support, and nested provider configuration
sections.
- Adds `responses_api_agents/mini_swe_agent_2`, a sandbox-backed
mini-swe-agent v2 integration for SWE-bench style evals, including
sandbox resource profiles, task metadata propagation, reward
aggregation, and `ng_collect_rollouts` usage docs.
- Adds focused unit coverage for the sandbox facade, provider registry,
OpenSandbox provider behavior, Mini SWE Agent 2 run/aggregation
behavior, and sandbox environment adapter.
- Moves sandbox-related dependencies behind the `nemo-gym[sandbox]`
optional extra.

### Notes

- This PR does not include the sandbox observability module from #1368.
- The Mini SWE Agent 2 README avoids internal deployment names and
user-specific paths; examples use placeholders and local `data/` /
`results/` paths.

## Validation

Completed on the squashed commit:

```bash
uv run ruff check nemo_gym/sandbox responses_api_agents/mini_swe_agent_2 tests/unit_tests/test_sandbox.py tests/unit_tests/test_opensandbox_provider.py
```

Result: `All checks passed!`

```bash
uv run pytest tests/unit_tests/test_sandbox.py tests/unit_tests/test_opensandbox_provider.py responses_api_agents/mini_swe_agent_2/tests/test_app.py responses_api_agents/mini_swe_agent_2/tests/test_sandbox_environment.py -q
```

Result: `44 passed, 2 warnings`

```bash
uv run coverage run --source=nemo_gym.sandbox,responses_api_agents.mini_swe_agent_2 -m pytest tests/unit_tests/test_sandbox.py tests/unit_tests/test_opensandbox_provider.py responses_api_agents/mini_swe_agent_2/tests/test_app.py responses_api_agents/mini_swe_agent_2/tests/test_sandbox_environment.py -q
uv run coverage combine results
uv run coverage report --include='nemo_gym/sandbox/*,responses_api_agents/mini_swe_agent_2/*' --fail-under=90
```

Result: focused coverage `92%`.

Kubernetes smoke validation:

- Model: `Qwen/Qwen3.5-27B`, served by SGLang with DFLASH draft model
`z-lab/Qwen3.5-27B-DFlash`.
- Launched the Mini SWE Agent 2 stack through the documented
`ng_collect_rollouts` path.
- Ran 8 SWE-bench Verified samples with 8 repeats and 64-way rollout
concurrency against the OpenSandbox internal service path.
- Result: 64/64 rollout rows, `pass@8=0.875`, 7/8 tasks resolved, mean
reward `0.765625`, eval error rate `0.0`, reward profile completion
`100%`.
- Cleanup completed with no leftover sandboxes for the successful
internal-service run.

Full SWE-bench Verified validation:

- Model: `Qwen/Qwen3.5-27B`, served by SGLang with DFLASH draft model
`z-lab/Qwen3.5-27B-DFlash`.
- Ran 500 SWE-bench Verified samples with pass@1, 500-way rollout
concurrency, `step_limit=250`, and OpenSandbox cleanup metadata.
- Result: 500/500 rollout rows, `pass@1=0.698`, 349/500 tasks resolved,
mean reward `0.698`, eval error rate `0.6`, tests status rate `99.0`,
reward profile completion `100%`.
- Job duration: `4h6m`; rollout collection duration: `4h04m`.
- Cleanup left no sandboxes with the run labels
`run_family=mini-swe2-firstpr-q35-cell-full-p1-r9` or
`cleanup_id=full-p1-r9-single-20260521-053410`.

---------

Signed-off-by: Hemil Desai <hemild@nvidia.com>
Signed-off-by: Rita Fernandes Neves <rfernandesne@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants