Repository navigation
osworld integration(for benchmarking) - #1408
Conversation
A NeMo Gym responses-API agent that wraps the OSWorld benchmark (xlang-ai/OSWorld, ~369 real desktop tasks across Chrome, LibreOffice, GIMP, VS Code, VLC, multi-app workflows, OS-level tasks). One /run = one full OSWorld rollout (sync inside Ray, pyautogui action space, returns final evaluator score as BaseVerifyResponse). Supports two deployment modes (see README): - local docker (single machine, no SSH; recommended for first run) - remote_docker (controller + VM host on different machines; uses the remote_docker provider from forked OSWorld, with a bundled bringup_remote_host.sh for one-shot remote setup) Ships example.jsonl, tests, and a comprehensive README. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
nemo-gym pyproject already requires ray[default]>=2.55.1 (CVE bump 2026-05-19). The agent's old pin at 2.52.1 conflicts, breaking uv per-server venv install with "ray==2.52.1 and nemo-gym[dev] are unsatisfiable". Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
Mode A (single-host local docker) is the README's recommended-first-run path. The CLI Hydra override 'provider_name=docker' fails with "Key 'osworld_simple_agent' is not in struct" due to NeMo-Gym's config-load order (override parsed before configs load), so the yaml default is the only working path. Mode B (remote_docker via SSH) users can override locally. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
…s set
OSWorld's controller already exposes start_recording / end_recording (the
in-VM ffmpeg path that lib_run_single.py uses). Wire it into the agent's
rollout loop, gated on the OSWORLD_RECORD_VIDEO_DIR env var:
- just after env.reset(), call env.controller.start_recording()
- in the finally block (before env.close()), download the mp4 to
${OSWORLD_RECORD_VIDEO_DIR}/{task_id}.mp4
Both calls are best-effort (caught + logged) so recording failures
don't break the rollout. With the env var unset, the agent behaves
exactly as before — zero overhead.
Verified on Brev (Mode A, claude-opus-4-7, docker provider): 5 example
tasks → 5 mp4 files (1.5-5.2 MB each, 16 MB total), no impact on
reward (4/5 reward=1.0 same as without recording).
Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
Recording was previously only mentioned in the Mode B architecture diagram (the remote_docker / VNC-stream path). After the matching controller-recording wiring in client.py, any provider works with the env var. Add an explicit "Recording rollout videos" section and TOC entry covering the usage, output naming, size expectation, and the best-effort failure mode. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
uv 0.11.16 has a reproducible bug: when `-e nemo-gym[dev]` and `osworld @ git+https://...` appear in the same `uv pip install -r requirements.txt` invocation, 7 of osworld's pyproject deps are silently dropped from the install (cryptography, cffi, scipy, Pillow, scikit-learn, matplotlib, flask). The Ray task then crashes at first /run with `ModuleNotFoundError: No module named 'cryptography'`. Minimal repro: a fresh venv with ONLY `osworld @ git+...` installs all 89 deps correctly; only the combination with the editable nemo-gym in the same install triggers the skip. Add the 7 (+ func-timeout, inconsistently affected) directly to this requirements.txt — uv installs explicit top-level pins reliably. Drop after the uv bug is filed + fixed upstream. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
OSWorld's docker provider lazy-downloads Ubuntu.qcow2 (12 GB) to docker_vm_data/ on first DesktopEnv start. With osworld_agent.yaml's default concurrency=4, all 4 rollouts race to write the same Ubuntu.qcow2.zip → torn file (49 GB observed vs 12 GB normal) → every rollout sees `BadZipFile: File is not a zip file`. The existing "First-Run Cache Acquisition" section mentioned pre- staging as a nice-to-have for shared clusters; upgrade the wording to "required for any run with concurrency > 1" and include the actual curl + unzip recipe (was implicit before). Split the recipe by provider (Docker provider downloads to docker_vm_data/, Apptainer to \$CACHE_DIR/osworld-images/) so users don't have to read source. Also fix the qcow2 size in the cache table (~12 GB compressed / ~23 GB extracted vs the prior ~10 GB / ~25 GB). Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
The agent README's "Quickstart" handles Mode A install once you have docker, uv, and a working PATH. But several host-prep details aren't trivially repo-fixable: apt installs for ffmpeg/xvfb/tigervnc (video recording stack), enabling the docker daemon, adding $USER to the docker group, installing uv, and (the subtle one) symlinking uv into /usr/local/bin so ng_run's `bash -c "uv run …"` subshells can find it. Add `scripts/bringup_local_host.sh` as the Mode A symmetric counterpart to the existing `bringup_remote_host.sh` (Mode B), an idempotent one-shot host-prep helper. Add a `scripts/README.md` mapping the two scripts to their deployment modes and showing the end-to-end usage flow for each. Also add a Troubleshooting entry to the main README documenting the `uv: command not found` symptom and the symlink fix, so users who hit it without running the script have a self-serve recipe. Verified end-to-end on a brand-new clone (no prior workarounds applied): `uv venv && uv sync --extra dev` + per-server venv install lands all 89 forked-osworld deps including the 7 that uv 0.11.16 silently dropped in earlier runs. desktop_env + DockerProvider + cv2 + PIL + cryptography all import cleanly. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
…ipt mention) After running `ruff format` and `pre-commit run --all-files`: - responses_api_agents/osworld_agent/client.py: ruff-format normalization (multi-line json.dumps formatting + LOG.info arg splitting). No semantic change. - README.md (root): the `update-readme-table` pre-commit hook auto-added an `Osworld Agent` row to the environments listing. First-time run after adding the agent — expected per CLAUDE.md "Pre-commit Hooks" section. - responses_api_agents/osworld_agent/README.md: add a `scripts/bringup_local_host.sh` reference in the Mode A "One-time setup" subsection, mirroring how Mode B already calls out `scripts/bringup_remote_host.sh`. The bare-minimum `sudo apt-get install -y docker.io` kept as the manual fallback. ruff check + ruff format check + pytest osworld_agent/tests/ (7/7) all green at this commit. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
…ut user-required steps Previously the bringup_local_host.sh final RECIPE block and the scripts/ README.md Mode A flow both said only `# fill in env.yaml at project root (see README's "Quickstart")` — a brief, easy-to-miss reference. New users following the helper would hit OmegaConf resolver errors at ng_run time because env.yaml hadn't been written. Surface the three user-must-do steps (clone + env.yaml + qcow2 prestage) with inline copy-paste-ready commands, instead of pointers to the main README. Specifically: - bringup_local_host.sh: replace the one-line `# fill in env.yaml` hint with three clearly numbered REQUIRED sections covering git clone, env.yaml (with `cat > env.yaml <<YAML ... YAML` template inline), and qcow2 prestage (with the curl + unzip recipe inline). The optional OSWORLD_RECORD_VIDEO_DIR mention stays at the end. - scripts/README.md: same — replace `# 3. Fill in env.yaml at project root (see main README Quickstart for fields)` with the inline `cat > env.yaml <<YAML ... YAML` template, and inline the qcow2 curl + unzip recipe in step 4. Verified end-to-end: smoke v7 on Brev with fresh `git clone -b feature/osworld` (SHA 328aae8), pulls all 89 forked-osworld deps cleanly (P1-2 explicit pins catch the uv 0.11.16 missing-deps bug), ng_run starts without `uv: command not found` (P1 uv symlink), 5/5 tasks complete (4 reward=1.0 / 1 reward=0 on impress as before), 5 mp4 recorded. No regression from prior smoke baselines. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
cmunley1
left a comment
There was a problem hiding this comment.
also could add this to benchmarks/
but needs further review from team
…s mode Addresses review feedback on the OSWorld integration PR: - data/example_rollouts.jsonl: saved rollouts for the 5 example.jsonl tasks (claude-opus-4-7, Screenshot only), 4/5 reward=1.0 — demonstrates the reward signal both awards (chrome/gimp/calc/writer) and withholds (impress) credit, per the contribution workflow. - README Reward Profiling: replace the TBD table with a strict reproduction of the official OSWorld verified leaderboard — o3 via NVIDIA Inference Hub, Screenshot only, max_steps=15, test_all 369 -> 9.21% (34/369) vs the board's 9.10% (32.85/361), delta +0.11 pp (within run-to-run variance). - Accessibility-tree-only observation mode: a new OSWORLD_OMIT_SCREENSHOT_IN_OBS env var blanks the screenshot from the observation, leaving only the a11y tree. Combined with require_a11y_tree=true this is the paper's "a11y only" setting (Table 5, e.g. the GPT-4 12.24% row). README now documents all four observation modes and how to select each. - scripts/paper_to_gym_jsonl.py: converter from upstream OSWorld manifests (test_all.json / test_small.json / ...) to the gym-shape JSONL the agent consumes. data/test_small.jsonl (39 tasks) checked in; test_all.jsonl (369) is regenerated via the script. Fix the Dataset Information table (test_small is 39 tasks, not 12). Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
ng_viewer is no longer a console_scripts entry point (only ng_run and ng_collect_rollouts are). Replace the two `ng_viewer +jsonl_fpath=...` invocations with a `jq` one-liner over the output JSONL — reward is top-level and per-step actions live in verifier_metadata.osworld_steps. Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
Makes sense — happy to move it under benchmarks/ if that's the preferred home. I'll defer to the team on placement; let me know the call and I'll restructure. |
|
cc @gchlebus |
…r false positives)
- copyright-check: add SPDX/Apache header to scripts/paper_to_gym_jsonl.py
- Lint: ruff-format scripts/paper_to_gym_jsonl.py (+ ruff check --fix)
- secrets-detector: mark false positives. The OSWorld guest VM password is
literally "password" and the tests use dummy values; no real secret.
- inline allowlist pragma on prompts.py, configs/osworld_agent.yaml, tests/test_app.py
- data/test_small.jsonl (JSON takes no inline comment) added to
.github/workflows/config/.secrets.baseline
Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
730f2fc to
3ccc9f9
Compare
Use Pointer's OSWorld environment with Gym Sandbox, defer policy credential validation until rollout configuration is available, and keep proxy-disabled tasks on native direct-network behavior. Pin numkong to the last Linux-buildable release. Signed-off-by: Michal Bien <mbien@nvidia.com>
Add a backward-compatible derived capture mode for model-call metrics without request, response, reasoning, or tool-call payloads. Correlate Gym-backed OSWorld calls by rollout and keep the legacy raw model I/O log opt-in. Signed-off-by: Michal Bien <mbien@nvidia.com>
Glorf
left a comment
There was a problem hiding this comment.
I think the integration is close, but I would request changes for two concrete issues before merging:
- The generic
HeadServerservice registry remains after its only intended in-tree consumer was deferred and removed. - The local
/dev/kvmvalidation breaks the advertised split-host deployment.
Also, please try to pass a simplification round with the agent of your choice, as the amount of code is still very large
fix(osworld): preserve Pointer behavior in Gym Sandbox
|
🌿 Preview your docs: https://nvidia-preview-feature-osworld.docs.buildwithfern.com/nemo/gym Here are the markdown pages you've updated: |
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
1762d3a to
663e53e
Compare
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
714fb6a to
2e6bdf8
Compare
Glorf
left a comment
There was a problem hiding this comment.
Cond. approved if sandbox changes are well tested. I'd strongly recommend merging only after 0.5.0 release freeze (today-tomorrow) to give some more time for testing (we don't need this in 0.5.0 AFAIK)
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
7fe4f2f to
aaf3048
Compare
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
Signed-off-by: Jeff Peng <jepeng@nvidia.com>
## Summary - stirrup 0.2.0 (published 2026-08-04) made `context_window_tokens` a required constructor argument in `ChatCompletionsClient`, breaking the stirrup_agent unit tests on the r0.5.0 release branch - Caps `stirrup<0.2` so CI stays green until the 0.2 migration is ready - Identical change to what landed on `main` via #1408 Signed-off-by: Kajal Jain <kajalj@nvidia.com>
# Summary This PR adds OSWorld as a first-class NeMo Gym benchmark and introduces a reusable Gym rollout runtime for desktop-computer agents. [OSWorld](https://github.com/xlang-ai/OSWorld) is a stateful desktop benchmark covering Chrome, LibreOffice, GIMP, VS Code, VLC, multi-application workflows, and operating-system tasks. Each task requires a real desktop VM, iterative visual observations and actions, task-specific setup, and final-state evaluation. One Gym `/run` request corresponds to one complete OSWorld rollout: 1. create a fresh task environment; 2. reset it with the original OSWorld task definition; 3. run the selected model/agent loop; 4. execute actions through OSWorld; 5. call OSWorld's inline evaluator; 6. return the trajectory, evaluator score, Gym reward, failure classification, and artifact metadata in a `BaseVerifyResponse`-compatible shape. The integration preserves OSWorld's task, controller, action, and evaluator semantics. Gym owns orchestration, model routing, concurrency, sandbox lifecycle integration, reproducibility, observability, and result accounting. The full benchmark guide is in [`benchmarks/osworld/README.md`](benchmarks/osworld/README.md). The reusable runtime contract is documented in [`responses_api_agents/osworld_agent/README.md`](responses_api_agents/osworld_agent/README.md). ## Why the integration is structured this way OSWorld is not a stateless verifier. Its evaluator depends on the live `DesktopEnv`, task setup state, files inside the VM, and application-specific controller endpoints. For that reason: - the complete rollout and evaluation stay in the same Ray worker; - each rollout owns one independent VM lifecycle; - OSWorld continues to own `DesktopEnv.reset()`, action execution, and `DesktopEnv.evaluate()`; - Gym wraps that lifecycle instead of translating OSWorld tasks or reimplementing evaluators; - there is no separate `resources_servers/osworld` service. Splitting evaluation into another service would require exporting live environment handles or reproducing task state outside the worker, without providing a useful stateless boundary. ## Architecture ```text Benchmark preparation benchmarks/osworld/prepare.py |-- validates the selected task JSONL |-- optionally creates deterministic task shards |-- prefetches task setup and evaluator assets |-- records input, qcow2, and container identities `-- writes a private benchmark-local env.yaml | v gym eval run | v OSWorld agent /run async admission gate concurrency semaphore | v one Ray task per rollout | v RunnerRegistry +-----------------+------------------+ | | | Gym-owned runner upstream runners model-specific adapter gym_pyautogui Prompt/Pointer/M3 Nemotron Nano Omni Qwen3 Omni +-----------------+------------------+ | configured model Gym model server or runner-native transport contract | v OSWorld DesktopEnv reset -> observe -> model -> parse -> execute | evaluate() | +-----------+------------+ | | OSWorld provider Gym Sandbox bridge owns VM lifecycle GymSandboxDesktopProvider | Gym Sandbox API | Docker provider, local or SSH | container -> QEMU/KVM -> desktop VM +-----------+------------+ | v OSWorldVerifyResponse |-- raw OSWorld evaluator score |-- configured binary or raw reward |-- mask_sample and termination reason |-- executed model steps `-- optional artifact provenance ``` The physical roles are also explicit and may be co-located or split: ```text MODEL_HOST <- model HTTP - AGENT_CONTROL_HOST - Docker API -> ENVIRONMENT_HOST | `-- prepare / control / eval ``` - The model host serves a compatible vision-language model. - The agent/control host runs benchmark preparation, Gym control, Ray workers, and evaluation. - The environment host runs Docker, QEMU/KVM, and the OSWorld desktop VMs. For a local smoke test, the agent/control and environment roles can be the same machine. For a split-host deployment, Gym uses Docker's standard SSH transport through `DOCKER_HOST=ssh://...`; no persistent interactive SSH session is part of the rollout path. ## Ownership boundaries | Layer | Owns | Does not own | | --- | --- | --- | | `benchmarks/osworld` | Dataset composition, profiles, task sharding, asset preparation, manifests, and launch recipes | Agent loop or evaluator semantics | | `responses_api_agents/osworld_agent` | `/run`, concurrency, runner selection, model routing, rollout lifecycle, failure classification, and Gym response mapping | OSWorld task setup or final-state evaluation logic | | OSWorld | `DesktopEnv`, controllers, setup, observations, action execution, and evaluators | Gym orchestration or cluster lifecycle | | Gym Sandbox | Container lifecycle, endpoint discovery, status, and cleanup | Desktop task semantics | | Runner/adapter | Prompt, history, model protocol, parser, and coordinate contract | Environment scoring | | Model server | Model inference and optional model-call observability | VM or task lifecycle | ## Design principles ### Preserve benchmark semantics The complete task object from `verifier_metadata.osworld_task` is passed to `DesktopEnv.reset(task_config=...)` without translating setup or evaluator behavior. Final scoring still comes from `DesktopEnv.evaluate()`. ### Separate independent axes The following are independent configuration choices: - runner/model protocol; - observation and action contract; - VM lifecycle backend; - local versus split-host placement; - model-serving topology. A remote environment host is not a different agent implementation, and selecting Gym Sandbox does not change the prompt, actions, or evaluator. ### Adapt at stable contracts `RunnerRegistry` makes each runner's environment class, agent class, observation type, action space, and default arguments explicit. Where possible, Gym preserves an upstream OSWorld runner. Gym-owned adapters are used only when a model has a distinct prompt/history/parser contract that is not represented by an upstream runner. The Gym Sandbox integration implements OSWorld's provider contract instead of replacing `DesktopEnv` or copying the rollout loop. ### Distinguish model outcomes from infrastructure failures A valid evaluator score of zero is still a valid benchmark or training sample. Infrastructure failures, rollout timeouts, invalid environment configuration, and unfinished rollouts set `mask_sample=true` and retain a specific termination reason. They are not silently converted into ordinary model failures. Task setup operations that explicitly declare `on_nonzero: score_zero` remain valid score-zero results rather than masked infrastructure failures. ### Make scale reproducible - One rollout owns one VM lifecycle. - Agent concurrency is bounded explicitly. - Task sharding is deterministic, disjoint, and exhaustive. - Shard manifests include the source hash, selected task IDs, and task-ID digest. - The OSWorld dependency and container image are immutable. - Benchmark preparation records the qcow2 identity and reset semantics. - Completed task IDs can be retained while recovery runs only the missing canonical set. ### Keep diagnostics opt-in Screenshots, video, VM execution logs, and full model I/O can be large and sensitive. They are disabled by default and enabled only through explicit environment variables. ## What this PR adds ### Benchmark composition `benchmarks/osworld/` provides: - benchmark metadata and default composition; - five-task example data and example rollouts; - the 39-task `test_small` subset; - `prepare.py` for input validation, profile selection, environment generation, deterministic sharding, VM identity recording, and asset preparation; - model/runner profiles for the generic Gym runner, Pointer Agent, PromptAgent, and Nemotron 3 Nano Omni; - model endpoint, environment, VM preparation, control, evaluation, and run-scoped cleanup tools; - local and split-host deployment documentation. The larger canonical task input is supplied explicitly at preparation time and is not committed as a generated repository artifact. ### Reusable OSWorld runtime `responses_api_agents/osworld_agent/` provides: - the Gym `/run` server and aggregate OSWorld metrics; - the synchronous `DesktopEnv` rollout loop executed inside Ray; - explicit raw versus binary reward handling; - timeout and mask semantics; - runner registration and validation; - model-specific adapters; - pyautogui and control-action parsing; - proxy-task validation and non-secret provenance; - shared setup-cache staging; - task-scoped logs, screenshots, trajectories, manifests, and video hooks; - Gym Sandbox compatibility for standard and Pointer `DesktopEnv` variants. ### Registered runner contracts | Runner | Ownership and contract | | --- | --- | | `gym_pyautogui` | Gym prompt, screenshot observations, and Python/pyautogui actions | | `prompt_agent` and `prompt_agent_*` | Upstream OSWorld `PromptAgent` observation/action variants, routed to the configured policy model | | `pointer_agent` | Upstream PointerAgent planner/executor/verifier behavior and Anthropic-compatible model contract | | `m3_agent` | Upstream MiniMax M3 scaffold, transport, prompt, and relative-coordinate protocol | | `nemotron_v3_nano_omni_agent` | Gym-owned Nemotron 3 Nano Omni prompt, bounded image history, parser, and coordinate projection | | `qwen3_omni_agent` | Upstream Qwen3VL scaffold with Gym-owned transport compatibility | The maintained `prepare.py --profile` entry points are currently `default`, `pointer`, and `nano_omni`. Other registered runner contracts can be selected through explicit compatible configuration. ### Gym Sandbox integration The runtime supports two execution backends: | Backend | Lifecycle owner | Intended use | | --- | --- | --- | | `osworld_provider` | OSWorld provider implementation | Compatibility and direct OSWorld-provider deployments | | `gym_sandbox` | Gym Sandbox, through `GymSandboxDesktopProvider` | Default benchmark workflow and provider-neutral lifecycle integration | In the Gym Sandbox path: - OSWorld still owns `DesktopEnv`, setup, controllers, actions, and evaluators; - Gym Sandbox owns container creation, endpoint discovery, status, and cleanup; - the OSWorld qcow2 is mounted read-only; - reset means closing the Sandbox and recreating a fresh VM from the same base; - `/dev/kvm` is validated by Docker on the actual environment host; - all required OSWorld service ports are published dynamically; - local and remote Docker daemons use the same provider code. ### Small generic Gym changes required by OSWorld This PR also includes narrowly scoped core changes needed by the integration: - optional Sandbox endpoint discovery as a provider capability; - explicit Sandbox service-port declarations; - Docker provider support for dynamic multi-port publication and remote environment hosts; - positional compatibility for existing `SandboxSpec` construction; - benchmark-config discovery in the generated environment table; - configurable preservation of reasoning in multi-turn vLLM assistant history; - opt-in correlated vLLM transport logging. The endpoint capability is optional, model capture remains disabled unless configured, and existing non-OSWorld provider behavior is preserved. ## Request, reward, and failure contract The rollout request carries the complete OSWorld task under: ```text verifier_metadata.osworld_task ``` Per-task `responses_create_params` may override configured sampling parameters. A completed response contains: - `reward`; - `mask_sample`; - `verifier_metadata.osworld_score`; - the number of executed OSWorld steps; - completion/error and termination state; - runner and model identity; - proxy provenance without credentials; - optional task artifact location; - one assistant output item per model step. `reward_mode` controls the top-level Gym reward: - `binary`: reward is `1.0` only for a full OSWorld evaluator score of `1.0`; - `raw`: reward preserves fractional OSWorld evaluator credit. Aggregate metrics always report both: - `osworld/binary_success_rate`; - `osworld/raw_reward_rate`. This prevents partial evaluator credit from being confused with full task success. ## Dataset and denominator convention The canonical full input used by the validation runs contains 369 unique OSWorld task IDs with SHA-256: ```text 3be0746d59fc5a311447e3e36241381c022932c624315b851fef2c28ecbf5ac9 ``` Eight GDrive tasks require an external Google OAuth `client_secrets.json` that was not available in the validation environment. Validation therefore reports two explicit views: - full attempted input: 369 tasks; - comparable no-GDrive view: the same 361 task IDs after excluding exactly those eight credential-blocked IDs. Every run first validates all 369 canonical IDs. Only the frozen GDrive-eight set is excluded from the 361-task comparison. Non-GDrive model failures, masks, proxy failures, and evaluator-zero outcomes remain in the denominator as zero. ## Reproducibility and dependency boundary The agent installs OSWorld from the immutable archive: ```text JeffPengCoder/OSWorld@31b76bf ``` This is a fork commit, not an upstream OSWorld release. It includes upstream OSWorld main `83e85344` plus the `nv-gym` provider/proxy integration changes. Gym does not modify the installed OSWorld tree at runtime. The phrase "unmodified dependency" in this integration means that the pinned archive is installed and consumed without run-local patching; it does not mean that the pinned commit is byte-identical to `xlang-ai/OSWorld` main. The default OSWorld container is pinned to: ```text happysixd/osworld-docker@ sha256:0e6497a9295647cf05bf2b2af522fdd79bdeba2737595259cab310a3bcf6baa9 ``` Benchmark preparation records: - task input SHA-256; - generated shard membership; - qcow2 path, size, and SHA-256; - container digest; - selected profile/backend; - output and cache locations. Asset preparation is idempotent. Prefetched assets change download timing and resilience, not task semantics. ## Verification ### Current-head contract tests Current PR head: ```text 32f37c0 ``` The following targeted suite passes on the current checkout: ```bash .venv/bin/pytest -q \ responses_api_agents/osworld_agent/tests \ benchmarks/osworld/tests \ tests/test_update_env_list.py \ tests/unit_tests/test_update_env_list.py \ tests/unit_tests/test_sandbox.py \ tests/unit_tests/test_sandbox_connect.py \ tests/unit_tests/test_base_responses_api_model.py \ responses_api_models/vllm_model/tests/test_app.py ``` Result: ```text 392 passed, 11 skipped (optional tenacity sandbox dependency not installed) ``` The CI-equivalent isolated server suites also pass: ```bash .venv/bin/gym env test +entrypoint=responses_api_agents/osworld_agent .venv/bin/gym env test +entrypoint=responses_api_models/vllm_model .venv/bin/gym env test +entrypoint=responses_api_agents/stirrup_agent ``` Results: ```text OSWorld: 153 passed vLLM: 110 passed Stirrup: 151 passed ``` The suite covers runner contracts, action parsing, request/response mapping, raw and binary metrics, masks, proxy handling, setup caching, task artifacts, Sandbox lifecycle adaptation, endpoint resolution, sandbox serialization and reconnection, model-call observability, task sharding, VM manifests, launch-script contracts, reasoning history, and model transport behavior. `git diff --check upstream/main...HEAD` is also clean. ### Live Gym Sandbox validation A near-head branch ancestor, `663e53e62ed740d803c65ac6df5494da93a7c687`, completed the full canonical Nano Omni validation using: - `nemotron_v3_nano_omni_agent`; - Gym Docker Sandbox; - two deterministic 185/184 task shards; - 16 concurrent environments per shard; - 100 maximum actions; - temperature `0.6`, top-p `0.95`; - a shared TP=8 vLLM 0.23 model endpoint; - the pinned OSWorld and VM/container identities documented above. Result: | View | Tasks | Raw evaluator | Raw rate | Binary successes | Binary rate | Masks | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | Full canonical input | 369 | 180.9759876 | 49.0450% | 175 | 47.4255% | 11 | | Exact no-GDrive view | 361 | 180.9759876 | **50.1319%** | 175 | **48.4765%** | 3 | Integrity checks: - 369/369 unique canonical IDs; - 0 missing IDs; - 0 extra IDs; - 0 duplicate IDs; - exactly eight GDrive credential failures excluded from the comparable view; - all three non-GDrive masked outcomes retained in the 361 denominator; - zero run-owned containers remaining after cleanup. ### Controlled repeated runs The same fixed Nano Omni contract was repeated three times over the same canonical task set: | Run | Official tasks | Raw rate | Binary rate | Retained non-GDrive masks | | --- | ---: | ---: | ---: | ---: | | Round 1 | 361 | 50.13% | 48.48% | 3 | | Round 2 | 361 | 49.48% | 47.09% | 11 | | Round 3 | 361 | 48.13% | 45.98% | 1 | | Mean +/- sample SD | 361/run | **49.25% +/- 1.02 pp** | **47.18% +/- 1.25 pp** | - | Every round contains the same 369 canonical IDs before the exact GDrive-eight exclusion and the same 361 IDs afterward. These are system-level benchmark results at nonzero sampling temperature. They validate end-to-end completeness, task accounting, reproducibility, recovery, and score stability; they are not presented as an isolated model-quality claim. ### Pointer Agent compatibility evidence A separate Pointer/Opus 4.7 experiment at Gym commit `09a895d7` completed all 369 tasks. On the comparable 361-task view: ```text Gym Pointer/Opus raw rate: 296.5627129 / 361 = 82.1503% Verified leaderboard reference: 301.94 / 361 = 83.64% Delta: -1.4897 pp ``` That experimental commit is not an ancestor of the current PR head, so this result is compatibility evidence for the Pointer wrapping design, not a current-head merge gate. ## Backward compatibility - `sandbox_provider: null` retains the OSWorld-native lifecycle path. - Sandbox endpoint lookup is an optional provider capability. - Existing `SandboxSpec` positional construction remains compatible. - Existing Docker provider behavior is unchanged unless ports, devices, mounts, or a remote Docker daemon are explicitly configured. - Reasoning-history preservation is opt-in. - Model-call capture and OSWorld artifact logging are opt-in. - Video recording has no runtime overhead when `OSWORLD_RECORD_VIDEO_DIR` is unset. - Existing non-OSWorld agents and resources servers do not consume the OSWorld runtime. ## Security and artifact handling - Proxy credentials are external runtime configuration and are never committed. - Result metadata records only non-secret proxy provenance. - The pinned OSWorld revision prevents task setup DEBUG logging from serializing the complete worker environment. - Full model I/O may contain prompts and embedded screenshots. - Task artifacts and video may contain user-visible desktop content. - Large screenshots, trajectories, VM images, model logs, and caches are intentionally not part of the normal repository artifact set. ## Limitations and planned follow-ups 1. **OSWorld fork dependency** The runtime currently depends on the immutable `JeffPengCoder/OSWorld` commit above rather than an upstream release. Provider and proxy changes should be upstreamed or replaced with an official pinned revision before removing this dependency. 2. **Full dataset publication** The committed five-task example and 39-task subset are runnable. The complete generated benchmark JSONL is intentionally not committed, and the registry-backed `train.jsonl` and `validation.jsonl` artifacts have not yet been published. 3. **GDrive credential tasks** Eight canonical tasks require valid external Google OAuth credentials. This PR does not fabricate, distribute, or bypass those credentials. 4. **Default platform** The default OSWorld Docker image is x86_64 and expects Linux with Docker and KVM. ARM64 environment images are outside this PR. 5. **Reset semantics** Gym Sandbox resets by closing the current Sandbox and recreating it from the read-only qcow2 base. Live RAM/device-state snapshots are not implemented. 6. **OpenSandbox backend** OpenSandbox has been exercised downstream but its provider implementation is not included here. 7. **Training recipes** The response contains the reward, mask, trajectory, and model-step information needed by downstream training integrations, but a complete NeMo RL training recipe is outside this PR. 8. **Sensitive diagnostics** Full model I/O, screenshots, VM execution logs, and video should be enabled only for controlled diagnostic runs with appropriate storage and access controls. ## Reviewer guide A suggested review order is: 1. Request, reward, and failure semantics: - `responses_api_agents/osworld_agent/app.py` - `responses_api_agents/osworld_agent/client.py` 2. Runner ownership and model-facing contracts: - `responses_api_agents/osworld_agent/runner_registry.py` - `responses_api_agents/osworld_agent/adapter_agents.py` - `responses_api_agents/osworld_agent/action_parser.py` 3. Sandbox ownership boundary: - `responses_api_agents/osworld_agent/sandbox_desktop_env.py` - `responses_api_agents/osworld_agent/sandbox_provider.py` - `nemo_gym/sandbox/` - `nemo_gym/sandbox/providers/docker/` 4. Benchmark preparation and operations: - `benchmarks/osworld/prepare.py` - `benchmarks/osworld/assets.py` - `benchmarks/osworld/configs/` - `benchmarks/osworld/tools/` 5. Model-server compatibility and observability: - `nemo_gym/base_responses_api_model.py` - `responses_api_models/vllm_model/app.py` 6. Contract and regression tests: - `responses_api_agents/osworld_agent/tests/` - `benchmarks/osworld/tests/` - `tests/unit_tests/test_sandbox.py` - `tests/unit_tests/test_docker_provider.py` ## Documentation - [OSWorld benchmark guide](benchmarks/osworld/README.md) - [OSWorld runtime reference](responses_api_agents/osworld_agent/README.md) - [Deployment tool reference](benchmarks/osworld/tools/README.md) - [Gym Docker Sandbox provider notes](nemo_gym/sandbox/providers/docker/README.md) --------- Signed-off-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com> Signed-off-by: Jeff Peng <jepeng@nvidia.com> Signed-off-by: Michal Bien <mbien@nvidia.com> Co-authored-by: Jeff Peng <jepeng@cw-dfw-cs-001-login-02.cm.cluster> Co-authored-by: JeffPengCoder <239749643+JeffPengCoder@users.noreply.github.com> Co-authored-by: Michal Bien <mbien@nvidia.com>
…2308) ## Summary This PR adds a production-oriented OpenSandbox Pool backend for the OSWorld benchmark while preserving the existing Gym Docker/KVM path. It also fixes the root dependency policy that previously made OSWorld's managed-server environment impossible to express cleanly: dependency exclusions are now scoped to the upstream packages that declare the unwanted edges instead of globally removing those package names from every server environment. Key outcomes: - Adds `gym_opensandbox` as an OSWorld execution backend backed by a pre-provisioned, server-managed KVM Pool. - Keeps the OSWorld agent, task setup, action loop, and evaluators identical across Docker and OpenSandbox. - Adapts OpenSandbox path-based gateway endpoints, routing headers, and Chrome CDP WebSockets to OSWorld's host-and-port interface. - Makes create, cancellation, shutdown, and interrupted-run cleanup bounded and ownership-aware. - Moves the OSWorld managed-server dependency declaration to the repository's established `requirements.txt` convention and pins OSWorld to an immutable archive. - Replaces global dependency exclusions with source-package-scoped exclusions and aligns CI on the minimum `uv` version that supports that policy. ## Motivation The existing OSWorld integration assumes that the Gym client owns a Docker container, mounts a local qcow2 image, and can expose the guest services as plain `host:port` endpoints. An OpenSandbox Pool changes all three assumptions: 1. The server-side Pool owns the VM image, QEMU entrypoint, and capacity. 2. Allocation is image-less from the client perspective and is selected by `poolRef`. 3. Externally reachable services may be returned as gateway URLs with path prefixes and required routing headers rather than directly routable Pod addresses. The goal is therefore not to fork the OSWorld execution path. It is to keep OSWorld's behavior unchanged and translate only the lifecycle and endpoint boundary beneath it. ## Architecture ```text model HTTP +--------------------------+ | v Task JSONL -> prepare -> OSWorld agent/control -> Nano Omni endpoint | | DesktopEnv provider contract v GymSandboxDesktopProvider | v Gym Sandbox public API / \ / \ Docker provider OpenSandbox provider - client-owned image - server-owned KVM Pool - client-owned qcow2 mount - image-less allocation by poolRef - direct host:port endpoints - gateway path/header endpoints \ / \ / OSWorld guest services screenshot | input | file | CDP | VLC ``` For gateway endpoints, the OSWorld adapter starts one loopback forwarder per guest service: ```text OSWorld http://127.0.0.1:<local-port>/... -> preserve gateway path prefix -> inject required routing headers -> forward HTTP or Chrome CDP WebSocket -> OpenSandbox gateway -> allocated VM ``` ## Design principles ### Preserve one OSWorld execution contract `GymSandboxDesktopProvider` continues to implement OSWorld's existing provider interface. Backend selection changes resource acquisition and endpoint resolution only; task setup, observations, actions, proxy behavior, and evaluators remain on the same code path. ### Make resource ownership explicit - Docker mode requires the client-owned image and readable qcow2 path, then supplies the QEMU entrypoint and mounts. - OpenSandbox Pool mode rejects client images and VM paths. It sends only lifecycle fields, metadata, exposed ports, and `provider_options.extensions.poolRef`; the Pool owns the image, entrypoint, and compute capacity. This prevents local Docker defaults from leaking into a server-managed Pool allocation. ### Translate endpoints at the narrowest boundary OSWorld expects one host and integer ports, whereas OpenSandbox can expose URL paths, headers, and WebSocket routes. The local forwarder performs that translation at the adapter boundary, disables ambient proxy inheritance, and leaves OSWorld itself unaware of transport-specific routing. ### Bound failure and cancellation - Provider operations have explicit timeouts and retry policies. - Synchronous Sandbox calls cancel and drain their underlying async task before returning on timeout, interruption, or another `BaseException`. - Pool creates carry a unique marker so an allocation whose response is lost can be found and reaped without matching another run. - Startup failures close forwarders and best-effort release the allocated Sandbox. ### Clean up only owned resources Each run carries exact run metadata. Normal shutdown releases its Sandbox; the recovery tool first audits and then optionally terminates only instances whose metadata value exactly matches the requested run ID. It checks both the OSWorld and Gym metadata keys and revalidates the match immediately before deletion. Credentials remain environment-only and are not written to generated `env.yaml` files. ## Dependency policy The OSWorld agent now uses `responses_api_agents/osworld_agent/requirements.txt`, matching the managed-server convention used throughout `responses_api_agents`. The prior root `exclude-dependencies` list acted globally when the editable root project was installed into an isolated server environment. That meant a package excluded only because it was an unwanted dependency of `mlflow` was also silently unavailable when OSWorld or another component required it directly. Keeping a component-local workaround would preserve that incorrect repository-wide behavior. This PR fixes the policy at its source: - `mlflow` exclusions apply only to dependency edges declared by `mlflow`. - OSWorld's unavailable optional `agp-client` edge and conflicting GUI OpenCV edges are scoped to `osworld`. - Repeated GUI OpenCV edges are scoped to `paddleocr` and `albumentations`. - Direct dependencies such as `cryptography` remain resolvable for other agents and model servers. - `uv >= 0.11.25` is required because package-scoped exclusions need that resolver behavior; both test workflows install the same minimum version. - OSWorld is installed from immutable revision `dc23424e9f6316b181bde149e0dc9bc3c5ff78c9`, with the compatible headless OpenCV wheel declared explicitly. This keeps the root policy composable for every managed server instead of special-casing OSWorld around a global exclusion. ## User-facing workflow `benchmarks/osworld/prepare.py` accepts `--execution-backend gym_opensandbox`, validates that `--vm-path` is absent, and renders the Pool reference from `OPENSANDBOX_POOL_REF` (default: `osworld-kvm`). The normal control and evaluation wrappers are unchanged. The OSWorld README documents: - deployment roles and resource ownership for both backends; - model endpoint probing and run preparation; - OpenSandbox configuration through environment variables; - normal shutdown and exact-run recovery cleanup; - direct, proxy-required, Chrome CDP, and VLC endpoint behavior. ## Validation All validation below applies to the PR patch now published as commit `435ba3bc38373c3b04ccd685f565b89b7066920a` on Linux/x86_64 unless noted otherwise. Live artifacts were initially recorded at `949a5e5963d241d4179712070fe954d6f7f468d5`; adding missing DCO trailers and rebasing onto current `main` changed only commit metadata and the unrelated base. The complete `git diff --binary main...HEAD` remained byte-for-byte identical, with SHA-256 `49e8c919c5c39f4054d8d45398d7ba2c11142eb6344d74d65d26ce4206b6cb6d`. ### Dependency and test gates - Fresh root environment with `uv 0.11.25`: 178 packages installed. - Relevant root, Sandbox, OSWorld agent, and benchmark suite: **2041 passed, 4 skipped, 5 subtests passed**. - Fresh OSWorld managed-server environment from `requirements.txt`: 336 packages installed; **157 agent tests + 32 benchmark tests = 189 passed**. - Fresh vLLM managed-server environment: 175 packages installed; `cryptography 49.0.0` resolved without a compatibility cap; **110 passed**. - Pre-commit hooks passed for every changed file. ### Live Nano Omni + OSWorld smoke matrix | Backend | Scenario | Task ID | Reward | | --- | --- | --- | ---: | | Gym local Docker + KVM | Direct Chrome | `2ad9387a-65d8-4e33-ad5b-7580065a27ca` | 1.0 | | Gym local Docker + KVM | Proxy-required Chrome | `06fe7178-4491-4589-810f-2e2bc9502122` | 1.0 | | Gym local Docker + KVM | VLC | `59f21cfb-0120-4326-b255-a5b827b38967` | 1.0 | | OpenSandbox KVM Pool | Direct Chrome | `2ad9387a-65d8-4e33-ad5b-7580065a27ca` | 1.0 | | OpenSandbox KVM Pool | Proxy-required Chrome | `06fe7178-4491-4589-810f-2e2bc9502122` | 1.0 | | OpenSandbox KVM Pool | VLC | `59f21cfb-0120-4326-b255-a5b827b38967` | 1.0 | The Docker route used a real qcow2 VM with KVM acceleration (`accel=kvm`, `-enable-kvm`), not a mocked lifecycle. The first independent Docker/proxy sample completed end-to-end but scored 0.0; a second sample scored 1.0, consistent with model sampling variance rather than a transport or evaluator failure. No runtime source overlay or `PYTHONPATH` patch was used. The installed pinned OSWorld package supplied the VLC gateway-auth fallback directly. ### Cleanup gate - No labeled Docker container remained after the run. - Post-run OpenSandbox audits for direct Chrome, proxy-required Chrome, and VLC each returned zero matching instances. ## Scope This branch is based on current `main` after #1408. It intentionally excludes developer-local ignore rules, local experiment harnesses and paths, `.pre-commit-config.yaml` changes, and unrelated vLLM component metadata changes. --------- Signed-off-by: Jeff Peng <jepeng@nvidia.com> Co-authored-by: Marta Stepniewska-Dziubinska <marta-sd@users.noreply.github.com> Co-authored-by: oliver könig <okoenig@nvidia.com>
Summary
This PR adds OSWorld as a first-class NeMo Gym benchmark and introduces a reusable Gym rollout runtime for desktop-computer agents.
OSWorld is a stateful desktop benchmark covering Chrome, LibreOffice, GIMP, VS Code, VLC, multi-application workflows, and operating-system tasks. Each task requires a real desktop VM, iterative visual observations and actions, task-specific setup, and final-state evaluation.
One Gym
/runrequest corresponds to one complete OSWorld rollout:BaseVerifyResponse-compatible shape.The integration preserves OSWorld's task, controller, action, and evaluator semantics. Gym owns orchestration, model routing, concurrency, sandbox lifecycle integration, reproducibility, observability, and result accounting.
The full benchmark guide is in
benchmarks/osworld/README.md. The reusable runtime contract is documented inresponses_api_agents/osworld_agent/README.md.Why the integration is structured this way
OSWorld is not a stateless verifier. Its evaluator depends on the live
DesktopEnv, task setup state, files inside the VM, and application-specific controller endpoints.For that reason:
DesktopEnv.reset(), action execution, andDesktopEnv.evaluate();resources_servers/osworldservice.Splitting evaluation into another service would require exporting live environment handles or reproducing task state outside the worker, without providing a useful stateless boundary.
Architecture
The physical roles are also explicit and may be co-located or split:
For a local smoke test, the agent/control and environment roles can be the same machine. For a split-host deployment, Gym uses Docker's standard SSH transport through
DOCKER_HOST=ssh://...; no persistent interactive SSH session is part of the rollout path.Ownership boundaries
benchmarks/osworldresponses_api_agents/osworld_agent/run, concurrency, runner selection, model routing, rollout lifecycle, failure classification, and Gym response mappingDesktopEnv, controllers, setup, observations, action execution, and evaluatorsDesign principles
Preserve benchmark semantics
The complete task object from
verifier_metadata.osworld_taskis passed toDesktopEnv.reset(task_config=...)without translating setup or evaluator behavior. Final scoring still comes fromDesktopEnv.evaluate().Separate independent axes
The following are independent configuration choices:
A remote environment host is not a different agent implementation, and selecting Gym Sandbox does not change the prompt, actions, or evaluator.
Adapt at stable contracts
RunnerRegistrymakes each runner's environment class, agent class, observation type, action space, and default arguments explicit.Where possible, Gym preserves an upstream OSWorld runner. Gym-owned adapters are used only when a model has a distinct prompt/history/parser contract that is not represented by an upstream runner.
The Gym Sandbox integration implements OSWorld's provider contract instead of replacing
DesktopEnvor copying the rollout loop.Distinguish model outcomes from infrastructure failures
A valid evaluator score of zero is still a valid benchmark or training sample.
Infrastructure failures, rollout timeouts, invalid environment configuration, and unfinished rollouts set
mask_sample=trueand retain a specific termination reason. They are not silently converted into ordinary model failures.Task setup operations that explicitly declare
on_nonzero: score_zeroremain valid score-zero results rather than masked infrastructure failures.Make scale reproducible
Keep diagnostics opt-in
Screenshots, video, VM execution logs, and full model I/O can be large and sensitive. They are disabled by default and enabled only through explicit environment variables.
What this PR adds
Benchmark composition
benchmarks/osworld/provides:test_smallsubset;prepare.pyfor input validation, profile selection, environment generation, deterministic sharding, VM identity recording, and asset preparation;The larger canonical task input is supplied explicitly at preparation time and is not committed as a generated repository artifact.
Reusable OSWorld runtime
responses_api_agents/osworld_agent/provides:/runserver and aggregate OSWorld metrics;DesktopEnvrollout loop executed inside Ray;DesktopEnvvariants.Registered runner contracts
gym_pyautoguiprompt_agentandprompt_agent_*PromptAgentobservation/action variants, routed to the configured policy modelpointer_agentm3_agentnemotron_v3_nano_omni_agentqwen3_omni_agentThe maintained
prepare.py --profileentry points are currentlydefault,pointer, andnano_omni. Other registered runner contracts can be selected through explicit compatible configuration.Gym Sandbox integration
The runtime supports two execution backends:
osworld_providergym_sandboxGymSandboxDesktopProviderIn the Gym Sandbox path:
DesktopEnv, setup, controllers, actions, and evaluators;/dev/kvmis validated by Docker on the actual environment host;Small generic Gym changes required by OSWorld
This PR also includes narrowly scoped core changes needed by the integration:
SandboxSpecconstruction;The endpoint capability is optional, model capture remains disabled unless configured, and existing non-OSWorld provider behavior is preserved.
Request, reward, and failure contract
The rollout request carries the complete OSWorld task under:
Per-task
responses_create_paramsmay override configured sampling parameters.A completed response contains:
reward;mask_sample;verifier_metadata.osworld_score;reward_modecontrols the top-level Gym reward:binary: reward is1.0only for a full OSWorld evaluator score of1.0;raw: reward preserves fractional OSWorld evaluator credit.Aggregate metrics always report both:
osworld/binary_success_rate;osworld/raw_reward_rate.This prevents partial evaluator credit from being confused with full task success.
Dataset and denominator convention
The canonical full input used by the validation runs contains 369 unique OSWorld task IDs with SHA-256:
Eight GDrive tasks require an external Google OAuth
client_secrets.jsonthat was not available in the validation environment.Validation therefore reports two explicit views:
Every run first validates all 369 canonical IDs. Only the frozen GDrive-eight set is excluded from the 361-task comparison. Non-GDrive model failures, masks, proxy failures, and evaluator-zero outcomes remain in the denominator as zero.
Reproducibility and dependency boundary
The agent installs OSWorld from the immutable archive:
This is a fork commit, not an upstream OSWorld release. It includes upstream OSWorld main
83e85344plus thenv-gymprovider/proxy integration changes.Gym does not modify the installed OSWorld tree at runtime. The phrase "unmodified dependency" in this integration means that the pinned archive is installed and consumed without run-local patching; it does not mean that the pinned commit is byte-identical to
xlang-ai/OSWorldmain.The default OSWorld container is pinned to:
Benchmark preparation records:
Asset preparation is idempotent. Prefetched assets change download timing and resilience, not task semantics.
Verification
Current-head contract tests
Current PR head:
The following targeted suite passes on the current checkout:
Result:
The CI-equivalent isolated server suites also pass:
Results:
The suite covers runner contracts, action parsing, request/response mapping, raw and binary metrics, masks, proxy handling, setup caching, task artifacts, Sandbox lifecycle adaptation, endpoint resolution, sandbox serialization and reconnection, model-call observability, task sharding, VM manifests, launch-script contracts, reasoning history, and model transport behavior.
git diff --check upstream/main...HEADis also clean.Live Gym Sandbox validation
A near-head branch ancestor,
663e53e62ed740d803c65ac6df5494da93a7c687, completed the full canonical Nano Omni validation using:nemotron_v3_nano_omni_agent;0.6, top-p0.95;Result:
Integrity checks:
Controlled repeated runs
The same fixed Nano Omni contract was repeated three times over the same canonical task set:
Every round contains the same 369 canonical IDs before the exact GDrive-eight exclusion and the same 361 IDs afterward.
These are system-level benchmark results at nonzero sampling temperature. They validate end-to-end completeness, task accounting, reproducibility, recovery, and score stability; they are not presented as an isolated model-quality claim.
Pointer Agent compatibility evidence
A separate Pointer/Opus 4.7 experiment at Gym commit
09a895d7completed all 369 tasks.On the comparable 361-task view:
That experimental commit is not an ancestor of the current PR head, so this result is compatibility evidence for the Pointer wrapping design, not a current-head merge gate.
Backward compatibility
sandbox_provider: nullretains the OSWorld-native lifecycle path.SandboxSpecpositional construction remains compatible.OSWORLD_RECORD_VIDEO_DIRis unset.Security and artifact handling
Limitations and planned follow-ups
OSWorld fork dependency
The runtime currently depends on the immutable
JeffPengCoder/OSWorldcommit above rather than an upstream release. Provider and proxy changes should be upstreamed or replaced with an official pinned revision before removing this dependency.Full dataset publication
The committed five-task example and 39-task subset are runnable. The complete generated benchmark JSONL is intentionally not committed, and the registry-backed
train.jsonlandvalidation.jsonlartifacts have not yet been published.GDrive credential tasks
Eight canonical tasks require valid external Google OAuth credentials. This PR does not fabricate, distribute, or bypass those credentials.
Default platform
The default OSWorld Docker image is x86_64 and expects Linux with Docker and KVM. ARM64 environment images are outside this PR.
Reset semantics
Gym Sandbox resets by closing the current Sandbox and recreating it from the read-only qcow2 base. Live RAM/device-state snapshots are not implemented.
OpenSandbox backend
OpenSandbox has been exercised downstream but its provider implementation is not included here.
Training recipes
The response contains the reward, mask, trajectory, and model-step information needed by downstream training integrations, but a complete NeMo RL training recipe is outside this PR.
Sensitive diagnostics
Full model I/O, screenshots, VM execution logs, and video should be enabled only for controlled diagnostic runs with appropriate storage and access controls.
Reviewer guide
A suggested review order is:
Request, reward, and failure semantics:
responses_api_agents/osworld_agent/app.pyresponses_api_agents/osworld_agent/client.pyRunner ownership and model-facing contracts:
responses_api_agents/osworld_agent/runner_registry.pyresponses_api_agents/osworld_agent/adapter_agents.pyresponses_api_agents/osworld_agent/action_parser.pySandbox ownership boundary:
responses_api_agents/osworld_agent/sandbox_desktop_env.pyresponses_api_agents/osworld_agent/sandbox_provider.pynemo_gym/sandbox/nemo_gym/sandbox/providers/docker/Benchmark preparation and operations:
benchmarks/osworld/prepare.pybenchmarks/osworld/assets.pybenchmarks/osworld/configs/benchmarks/osworld/tools/Model-server compatibility and observability:
nemo_gym/base_responses_api_model.pyresponses_api_models/vllm_model/app.pyContract and regression tests:
responses_api_agents/osworld_agent/tests/benchmarks/osworld/tests/tests/unit_tests/test_sandbox.pytests/unit_tests/test_docker_provider.pyDocumentation