Repository navigation
feat(simple-agent): run simple agent through environment server sessions - #3784
Merged
Merged
Conversation
ananthsub
force-pushed
the
ananthsub/simple-agent-sessions
branch
from
September 30, 2026 00:52
beaf7f1 to
3b34283
Compare
aroshanghias-nvd
pushed a commit
to aroshanghias-nvd/Gym
that referenced
this pull request
Sep 30, 2026
) ## What does this PR do? Adds the configuration that runs SWE-bench Pro with Hermes through an Environment Server: - `benchmarks/swebench/pro/hermes.yaml`, alongside the existing `opencode.yaml`. It runs SWE-bench Pro rows through `single_agent_turn_legacy`, with the SWE-bench Pro Resources Server preparing the task sandbox and Hermes working inside it. - `resources_servers/swebench_pro/configs/swebench_pro.yaml` lists `hermes_agent` among its allowed agents, so the Hermes configuration composes the same Resources Server config as the other SWE-bench Pro agents. Rows reach the Environment Server by their agent, as with `opencode.yaml`: one Environment Server fronts the Hermes agent in this configuration. Simple Agent sessions and the example Resources Server's sessions moved to their own PR, [NVIDIA-NeMo#3784](NVIDIA-NeMo#3784). ## Stack position Part 3 of 3. Previous: [NVIDIA-NeMo#3721](NVIDIA-NeMo#3721). This is the top of the stack. No issue is linked because this is one layer of the Environment Server stack. ## Validation - `gym env resolve --config benchmarks/swebench/pro/hermes.yaml ...` resolves to the Hermes agent, the SWE-bench Pro Resources Server, and the `single_agent_turn_legacy` Environment Server, with that Environment Server as the only one fronting the Hermes agent. - `pytest tests/unit_tests/test_task_data.py tests/unit_tests/test_global_config.py tests/unit_tests/test_benchmarks.py -q`: 572 passed. - `pre-commit run` on the changed files. ## Rollouts Real Hermes + SWE-bench Pro rollouts with `gpt-5.6-sol` ran through `single_agent_turn_legacy` on this stack, with Hermes calling the Model Server directly from the task sandbox. The task resolved with reward `1.0` (300 of 300 tests) in two runs and passed 299 of 300 tests in two others, with empty failure sidecars and no leftover sandboxes. Those runs selected the Environment Server explicitly; this configuration reaches the same Environment Server by agent routing. ## Compatibility and benchmark impact Configuration only. Existing SWE-bench Pro configurations are unchanged apart from the added allowed agent. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
aroshanghias-nvd
pushed a commit
to aroshanghias-nvd/Gym
that referenced
this pull request
Sep 30, 2026
…VIDIA-NeMo#3786) ## What does this PR do? Lets any Resources Server take part in an Environment Server episode without writing session code. The single-agent-turn Environment Server seeds each episode with a typed `ResourcesSeedSessionRequest`, expects the caller's `resources_session_id` back, and closes the session through `/close_session` during cleanup. On `main`, only Resources Servers that implement both calls themselves can run an episode. For every other server, the base `/seed_session` returns an empty body that fails validation, so the episode fails before verification, and `/close_session` returns 404. `SimpleResourcesServer` now answers both calls by default: - `/seed_session` accepts a `ResourcesSeedSessionRequest` and returns its `resources_session_id`. An Agent's `/run` still seeds with its legacy body and still gets the empty response, so existing agents are unaffected. - `/close_session` is registered in the base and calls `close_resources_session`, which does nothing by default. A Resources Server with per-rollout state overrides `seed_session()` and `close_resources_session()`. The hook is not called `close_session` because several Gymnasium-based servers (`gymnasium`, `openair_congestion`, `tales`) already define a `close_session(session_id)` helper for their own state. Registering that name would have exposed those helpers as HTTP routes with the wrong signature. SWE-bench Pro, the only server on `main` with its own `/close_session`, moves its handler onto the hook. The base route is registered first, so a route the server registered itself would never be reached. Its behavior is unchanged: the same handler still accepts both the typed close and the empty cookie-session close that `hermes_sandboxed_agent` sends. The Resources Server API guide describes the defaults and when to override them. ## Issue No issue is linked. This fills a gap found while reviewing the Environment Server stack ([NVIDIA-NeMo#3720](NVIDIA-NeMo#3720)). ## Validation - `pytest tests/unit_tests/test_base_resources_server.py -q`: 14 passed. A new HTTP-level test checks that a server with no session code answers a typed seed with its `resources_session_id`, a legacy seed with an empty body, and a typed close. It fails on `main`. - `pytest resources_servers/swebench_pro/tests -q`: 84 passed, including the HTTP test that sends a typed close, an empty cookie close, and a malformed close to `/close_session`. - `gym env test` for `gymnasium` (10), `grl_sokoban` (9), and `tales` (10), which define their own `close_session`, and for `finance_sec_search` (96), which calls `super().seed_session()`; all pass. `example_mcp_weather` (6) and `example_session_state_mgmt` (1) pass. - `pytest tests/unit_tests -q`: passes except two tests that also fail on `main` in the local environment (missing `sandbox` extras and a pip notice). - `pre-commit run` on the changed files. ## Rollouts N/A. This adds default responses to two session calls that `main`'s agents do not make, and moves SWE-bench Pro's close handler without changing its behavior, which its HTTP tests cover. No agent on `main` seeds typed sessions yet, so an end-to-end Environment Server episode needs [NVIDIA-NeMo#3784](NVIDIA-NeMo#3784) or [NVIDIA-NeMo#3720](NVIDIA-NeMo#3720). ## Compatibility - Legacy `/seed_session` bodies get the same empty response as before, and servers that override `seed_session()` are unchanged. - A server that registers its own `/close_session` route after calling `super().setup_webserver()` is shadowed by the base route and must override `close_resources_session()` instead. On `main` that applies only to SWE-bench Pro, which this PR migrates. [NVIDIA-NeMo#3784](NVIDIA-NeMo#3784) also registers one for the example single-tool-call server. Whichever of the two PRs merges second should move that server onto the hook. --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
5 tasks done
Contributor
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/37380663103 |
ananthsub
added a commit
that referenced
this pull request
Oct 5, 2026
…nvironment Server Adds a config that fronts Simple Agent with single_agent_turn_legacy and grants it direct HTTP access to the counter tools, and checks the server with the shared session contract check. Both needed Simple Agent sessions and nemo_gym.testing from #3784. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ananthsub
added a commit
that referenced
this pull request
Oct 6, 2026
## What does this PR do? Removes "native" from the Environment Server code. It meant two different things there, which made the code hard to read: - **Sessions an Environment Server creates**, as opposed to the cookie sessions an agent's `/run` starts. These now use the protocol's own names: agent session, Resources session, or Environment Server session when both are meant. - **Typed episode requests**, as opposed to flat legacy rows. These are now episode requests, and tasks with a `task_id.taskset` and `task_input` are materialized tasksets and rows, matching the existing `MaterializedTask` and `_materialized_taskset`. Renames: | Before | After | |---|---| | `SingleAgentTurnLegacyEnvironmentServer._native_request` | `_episode_request_from_row` (it builds an episode request from a flat row) | | `rollout_collection._native_episode_request_body` | `_episode_request_body` | | SWE-bench Pro `_native_session_locks`, `_closed_native_sessions` | `_session_locks`, `_closed_sessions` | Comments and help text that said "native taskset", "native row", "native-only run", or "native episode response" now say materialized taskset or row, a run of materialized rows only, or episode response. Test names and fixture values in the adapter, `single_agent_turn`, SWE-bench Pro, and Hermes tests follow the same names. Other uses of "native" are unrelated and unchanged: Gym's built-in agents, native Gym rollouts in reverification, native PDFs, Anthropic- and MCP-native harnesses, and `is_responses_native`. No behavior changes. ## Issue No issue is linked; this is naming cleanup for the Environment Server work, following review of [#3784](#3784). ## Validation - `pytest environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests -q`: 30 and 12 passed. - `pytest resources_servers/swebench_pro/tests -q`: 96 passed. - `pytest tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_server.py -q`: 433 passed. - `pytest responses_api_agents/hermes_agent/tests -q` (its own venv): 219 passed. - `pre-commit run` on the changed files. ## Rollouts N/A: renames identifiers, comments, help text, and test names only; no behavior changes. ## Compatibility No API, configuration, or result changes. The renamed functions and attributes are private. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
This was referenced Oct 6, 2026
ananthsub
added a commit
to ananthsub/Gym
that referenced
this pull request
Oct 6, 2026
…-NeMo#4070) Removes "native" from the Environment Server code. It meant two different things there, which made the code hard to read: - **Sessions an Environment Server creates**, as opposed to the cookie sessions an agent's `/run` starts. These now use the protocol's own names: agent session, Resources session, or Environment Server session when both are meant. - **Typed episode requests**, as opposed to flat legacy rows. These are now episode requests, and tasks with a `task_id.taskset` and `task_input` are materialized tasksets and rows, matching the existing `MaterializedTask` and `_materialized_taskset`. Renames: | Before | After | |---|---| | `SingleAgentTurnLegacyEnvironmentServer._native_request` | `_episode_request_from_row` (it builds an episode request from a flat row) | | `rollout_collection._native_episode_request_body` | `_episode_request_body` | | SWE-bench Pro `_native_session_locks`, `_closed_native_sessions` | `_session_locks`, `_closed_sessions` | Comments and help text that said "native taskset", "native row", "native-only run", or "native episode response" now say materialized taskset or row, a run of materialized rows only, or episode response. Test names and fixture values in the adapter, `single_agent_turn`, SWE-bench Pro, and Hermes tests follow the same names. Other uses of "native" are unrelated and unchanged: Gym's built-in agents, native Gym rollouts in reverification, native PDFs, Anthropic- and MCP-native harnesses, and `is_responses_native`. No behavior changes. No issue is linked; this is naming cleanup for the Environment Server work, following review of [NVIDIA-NeMo#3784](NVIDIA-NeMo#3784). - `pytest environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests -q`: 30 and 12 passed. - `pytest resources_servers/swebench_pro/tests -q`: 96 passed. - `pytest tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_server.py -q`: 433 passed. - `pytest responses_api_agents/hermes_agent/tests -q` (its own venv): 219 passed. - `pre-commit run` on the changed files. N/A: renames identifiers, comments, help text, and test names only; no behavior changes. No API, configuration, or result changes. The renamed functions and attributes are private. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> (cherry picked from commit 412efc7) Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
This was referenced Oct 6, 2026
ananthsub
added a commit
to ananthsub/Gym
that referenced
this pull request
Oct 6, 2026
## Expected Harden Hermes sandbox sessions. Data/CLI changes are separate in NVIDIA-NeMo#3818. Implements [MB-1694](https://linear.app/nvidia/issue/MB-1694/runbook-onboard-sandboxed-harnesses-to-native-environmentserver). ## Added - Shared base-agent seed/close retry handling, immutable seed binding, cookie checks, session locks, and close receipts. - Confirmed sandbox process cleanup, launch fencing, internal deadlines, and owned-sandbox teardown. Close confirms cleanup before cancelling borrowed-sandbox execution. - Wall-time stops checkpoint partial results; stale PID signaling is guarded by the remote receipt. Failed setup retains cleanup-only state through a shared API. - SWE-Pro keeps its existing scoring and completion rules. Patch-extraction exceptions remain visible in the task's `error` field without masking the sample. - Identical activation retries join or replay the original run. Provider failures remain retryable and skip verification. - One request validator and prompt/settings builder for host and sandbox execution. ## Concerns - Sandbox prompt composition is unchanged: config system prompt, request instructions, then input system message. Host execution now combines these prompts instead of letting config replace the dataset prompt, and honors request temperature, including zero. `developer` messages now return 422. These host-path changes can change benchmark scores. - Unsupported non-default controls return 422, including `top_p=1.0`, `store`, `service_tier`, and metadata overrides. `model` must match the configured model. `max_output_tokens` is rejected because Hermes does not enforce a total-response budget; config `max_tokens` remains per model call. These 422s are permanent episode errors. - One activation per session, one agent worker, Linux exec-capable sandbox. Close retries must finish within the retention window. Final usage aggregation remains TODO. - Separate follow-up found during smoke: the iteration-limit summary sends `function.call_id` / `function.response_item_id` in tool history, which Gym rejects with 422. The run returns `incomplete`; its existing patch remains gradable. This update does not fix that summary path. - Simple Agent (NVIDIA-NeMo#3784) is not modified here. The SWE-Pro recipe still uses prepared flat rows through `single_agent_turn_legacy` and the EnvironmentServer session lifecycle. ## Validation Latest review follow-up: `e378cd45c` (includes the merge-queue assertion fix in `4df61bf18`). - Removed the redundant session-lock lookup guard and unused thinking-setting diagnostics. Close-requested stops now report `cancelled`, not `wall_time`; deadline behavior and scoring are unchanged. - Five targeted regression cases failed on the previous code. Final focused suite: **563 passed on Linux**; **548 passed, 15 platform skips on macOS**, Python 3.13.14. - `pre-commit run --all-files`, `git diff --check`, and **18 Fern link tests** passed. `cd fern && npm run check`: **0 errors, 1 warning**. Cleanup docs now identify close ordering, not `setsid()` alone, as the guarantee. - Real-model Hermes × SWE-Pro smoke with `nvidia/nemotron-3-super-120b-a12b` and a 15-second runner limit: 3 model calls, `incomplete` / `stop_reason=wall_time`, reward **0**, `mask_sample=false`, `evaluation_completed=true`, 18 verifier tests. Agent close preceded verification; no owned containers/network remained. Runtime file hashes matched the local revision. This is a lifecycle smoke, not an accuracy baseline. <details> <summary>Latest focused test command</summary> ```bash python -m pytest \ environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests \ environment_servers/legacy_agent/tests tests/unit_tests/test_episode_types.py \ tests/unit_tests/test_environment_server.py tests/unit_tests/test_rollout_outcomes.py \ tests/unit_tests/test_failure_kinds.py responses_api_agents/hermes_agent/tests \ responses_api_agents/hermes_sandboxed_agent/tests resources_servers/swebench_pro/tests \ tests/unit_tests/test_agent_session_retries.py tests/unit_tests/test_agent_sessions.py \ tests/unit_tests/test_base_responses_api_agent.py tests/unit_tests/test_process_supervisor.py \ tests/unit_tests/test_local_provider.py \ --import-mode=importlib -q -o addopts='' -o log_cli=false --tb=short -rs ``` </details> Scoring revert: `f06ef8dba888fcffaf86eae265fb63f3411db5f6`. - `python -m pytest resources_servers/swebench_pro/tests environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests --import-mode=importlib -q -o addopts='' -o log_cli=false --tb=short`: **135 passed**. Four failure-policy regression cases failed before the revert, then passed; extraction errors retain the original exception message. - `pre-commit run --all-files` and `git diff --check` passed. - Real-model SWE-Pro HTTP smoke with `nvidia/nemotron-3-super-120b-a12b`, a 15-second agent limit, and an invalid Git base injected only in the smoke driver: reward **0**, `mask_sample=false`, `evaluation_completed=true`, and the actual extraction exception in `error`. The agent closed before verification; all owned containers/network were cleaned up. This checks failure reporting, not benchmark accuracy. - No documentation change needed: this restores the existing verifier contract rather than adding an API. Earlier review-fix validation at `95a2b6481fa0225fc5a363443d9a536e98cd304f`: - macOS/Python 3.13.14: **494 passed, 13 platform skips**. - Linux/Python 3.13.14: **422 passed** across Hermes, shared sessions, process supervision, SWE-Pro, and Environment Server tests. Final verifier tests: **49 passed**; final supervisor/runner recheck: **40 passed**. - Real-process regressions cover provider-group cancellation, lost spawn handles, detached children, timeout checkpoints (including an uncooperative worker), stale-PID protection, partial setup, and cleanup retries. - Two real-model SWE-Pro HTTP smokes with `nvidia/nemotron-3-super-120b-a12b`: both reached verification after agent close, returned valid reward 0, and left no owned containers or networks. One stopped on model-output truncation; the forced 15-second wall-time run returned `incomplete` / `stop_reason=wall_time`. Both had no patch; these are lifecycle checks, **not accuracy baselines**. Partial-work preservation is separately covered by deterministic subprocess tests. - `pre-commit run --all-files` and diff checks passed; Fern: 0 errors, 1 warning. The test import conflict with main was removed without rebasing. - Full repository CI is not claimed. The inherited DCO mismatch in `07ca331ca1e12d234f1a3a60bb12e1651e496786` remains; the new commit is signed off. <details> <summary>Current focused command</summary> ```bash python -m pytest responses_api_agents/hermes_agent/tests \ responses_api_agents/hermes_sandboxed_agent/tests \ tests/unit_tests/test_process_supervisor.py tests/unit_tests/test_local_provider.py \ tests/unit_tests/test_sandbox_stop_retry.py tests/unit_tests/test_base_responses_api_agent.py \ tests/unit_tests/test_agent_sessions.py tests/unit_tests/test_agent_session_retries.py \ tests/unit_tests/test_environment_server.py resources_servers/swebench_pro/tests \ environment_servers/single_agent_turn/tests \ --import-mode=importlib -q -o addopts='' -o log_cli=false --tb=short python -m pre_commit run --all-files cd fern && npm run check ``` </details> <details> <summary>Previous validation at d5f13f5 (historical)</summary> Head: `d5f13f54cf762c7e687dd2d50d2583efe2c9b717`. - macOS/Python 3.13.14: **487 passed, 8 skipped** (Linux-only checks). - Linux/Python 3.13.14: **292 passed**, including Hermes, sessions, process cleanup, and EnvironmentServer tests. Uploaded source and test hashes match this commit. - New regressions failed against the old code: ignored controls and host/sandbox prompt/settings drift. Also test matching model names, zero temperature, unchanged input, validation once per request, and rejection of future non-default schema controls. - `pre-commit run --all-files`, diff checks, and Fern passed (0 errors, 1 warning). New commit is DCO-signed. - Fresh real-model public SWE-Pro HTTP smoke using `nvidia/nemotron-3-super-120b-a12b`: **reward 1.0**, **18/18 verifier tests passed**, 11,273-byte patch. Verified prompt ordering and temperature at the Model Server, agent close before verification, Resources close, and no remaining owned containers/network. Inspected the patch and verifier results. Agent reached the 20-turn smoke limit and returned `incomplete`, with the separate summary-call 422 described above. - This is one functional smoke, not an accuracy/baseline comparison. Reused isolated dependency environments; full repository CI is not claimed. The inherited DCO author/sign-off mismatch in `07ca331ca1e12d234f1a3a60bb12e1651e496786` is unchanged. <details> <summary>Focused commands</summary> ```bash python -m pytest responses_api_agents/hermes_agent/tests \ tests/unit_tests/test_agent_session_retries.py \ tests/unit_tests/test_agent_sessions.py tests/unit_tests/test_base_responses_api_agent.py \ tests/unit_tests/test_process_supervisor.py tests/unit_tests/test_local_provider.py \ responses_api_agents/hermes_sandboxed_agent/tests \ environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests \ resources_servers/swebench_pro/tests tests/unit_tests/test_resources_sessions.py \ tests/unit_tests/test_environment_server.py \ --import-mode=importlib -q -o addopts='' -o log_cli=false --tb=short -rs # Linux subset: python -m pytest tests/unit_tests/test_process_supervisor.py tests/unit_tests/test_local_provider.py \ tests/unit_tests/test_agent_session_retries.py responses_api_agents/hermes_agent/tests \ environment_servers/single_agent_turn/tests \ --import-mode=importlib -q -o addopts='' -o log_cli=false --tb=short -rs python -m pre_commit run --all-files cd fern && npm run check ``` </details> </details> --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> Signed-off-by: Marta Stepniewska-Dziubinska <martas@nvidia.com> Signed-off-by: Adil Asif <adasif@nvidia.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> Co-authored-by: Ananth Subramaniam <ansubramania@nvidia.com> Co-authored-by: Marta Stepniewska-Dziubinska <martas@nvidia.com> Co-authored-by: Adil Asif <adasif@nvidia.com> (cherry picked from commit b846dfa) Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ananthsub
added a commit
to ananthsub/Gym
that referenced
this pull request
Oct 6, 2026
…ons (NVIDIA-NeMo#3784) Lets Simple Agent run inside an Environment Server episode through agent sessions, gives the example single-tool-call Resources Server typed sessions, and adds a config that runs the example through the single-agent-turn Environment Server end to end. `/run` is unchanged. Simple Agent sessions: - Simple Agent implements only the base agent's session hooks, `_seed_agent_session_state` and `_close_agent_session_state`. The base agent's shared bookkeeping from [NVIDIA-NeMo#3554](NVIDIA-NeMo#3554) handles the rest: idempotent seed, binding a session to its seed, the session cookie, retries, and close receipts. The cookie key is one constant, `AGENT_SESSION_COOKIE_KEY`, in `nemo_gym.base_responses_api_agent`. - During the session, Simple Agent calls tools through the direct HTTP tool access the Environment Server grants, which carries the Resources session cookie. It accepts at most one direct HTTP grant and rejects sandbox access and required MCP access, which it does not support. A session without a grant never falls back to the configured Resources Server, which has no session for the episode: a tool call there fails the activation. - When observability is on, the session returns its agent invocation record at close, where the Environment Server stores it as the episode's agent observations. - Sessions live in the worker's memory, so seeding one requires `num_workers: 1`. `/run` keeps no session and still supports several workers. - `resources_server` is optional in the Simple Agent config, because a session gets its tools from the Environment Server. `/run` and `/aggregate_metrics` still require it. Example single-tool-call Resources Server: - Implements typed, idempotent seed and close by `resources_session_id`. Close overrides the `close_resources_session` hook; the example no longer registers its own `/close_session` route, which the base route takes precedence over. - `configs/example_single_tool_call_single_agent_turn.yaml` fronts Simple Agent with `single_agent_turn_legacy` and grants it direct HTTP access to the example's tools. The existing config, which the quickstart and training docs use, still runs through `/run`. `single_agent_turn_legacy` accepts a `task_source` that names its Resources Server, its agent, or itself. Collation sets `task_source` to the instance that declares the dataset, usually the agent, so the adapter rejected collated rows before. The `close_resources_session` docstring states what a close override must do: succeed for a session that is already closed or was never seeded, keep a later seed from recreating a closed session, and raise when releasing fails so the caller can retry. It also notes that the default seed does not tie the session cookie to `resources_session_id`. Contract checks: - `nemo_gym.testing.session_conformance.check_resources_session_contract` is an integration test of a server's session lifecycle: it seeds, re-seeds and closes a session through the server's real routes and middleware, in process, and checks the close rules, so it also catches a shadowed route. The base default and the example run it; a migrated server calls it from its own tests. `nemo_gym.testing` holds helpers that tests import. - `tests/unit_tests/test_session_contract_repo.py` reads every agent and Resources Server's source, since servers run in their own environments. It fails when an agent overrides the public seed or close instead of the hooks, when an agent implements the seed hook without the close hook, when a Resources Server registers its own `/close_session`, or when a typed seed has no `close_resources_session`. No issue is linked; this is part of the Environment Server work. - `pytest responses_api_agents/simple_agent/tests -q`: 26 passed. A seed after close now returns 409 from the base bookkeeping instead of a 500. A session tool call without a grant fails and reaches no Resources Server; that test fails without the fix. - `pytest resources_servers/example_single_tool_call/tests -q`: 5 passed, including the session contract check. - `pytest environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests -q`: 30 and 12 passed, including `task_source` naming the agent or the Resources Server, and rejecting another instance. - `pytest resources_servers/swebench_pro/tests -q`: 96 passed. `pytest responses_api_agents/hermes_agent/tests -q` (its own venv): 219 passed. - `pytest tests/unit_tests/test_session_contract_repo.py tests/unit_tests/test_base_resources_server.py tests/unit_tests/test_agent_sessions.py tests/unit_tests/test_agent_session_retries.py tests/unit_tests/test_base_responses_api_agent.py -q`: all passed. The repo-wide checks and the contract check each fail when the example registers its own `/close_session` again; the repo-wide agent check fails when Simple Agent overrides `close_agent_session`. - `pytest tests/unit_tests -q`: all passed except tests that need packages this environment lacks (`opensandbox`, `tenacity`, `mlflow`, `wandb`) and a Slurm test that calls the system pip. - `pre-commit run` on the changed files. `gym dataset collate` on the new config stamped `task_source` with the agent's name, and the five collated `example_single_tool_call` tasks ran with `gpt-5.6-sol` through `single_agent_turn_legacy` with no failures. Three called `get_weather` and scored `1.0`; the other two prompts are ambiguous ("get the weather for 3 cities in the us", "how's the outside?"), and the model asked which city was meant, so they scored `0.0` as the verifier intends. A stateful check, run locally with `example_session_state_mgmt` given a typed seed and close (that change is a separate PR) and a `single_agent_turn` config like the one here: its counter starts at the task's `initial_count`, so a reward of `1.0` needs the seed, the agent's tool calls, and verification to reach the same Resources session. With the direct HTTP grant, all five collated tasks scored `1.0` (for example 3 seeded + 1 + 2 = 6), which confirms the grant's cookie reaches the seeded session without forwarding cookies on the agent seed request. With no grant, the earlier code scored `0.0` on all five because tool calls landed in fresh sessions; with the fix, those episodes fail with "Model called tool 'increment_counter', but this agent session has no direct HTTP tool access". `/run`, its request and result shapes, and existing Simple Agent configurations are unchanged. `resources_server` becomes optional in the Simple Agent config, and configurations that set it behave as before. --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> (cherry picked from commit e0e63e5) Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ananthsub
added a commit
that referenced
this pull request
Oct 6, 2026
…ions (#3554, #3784) into `r0.7.0` (#4113) ## What This PR only cherry-picks two already-merged `main` PRs into `r0.7.0`. It adds no new code. Each commit is applied with `git cherry-pick -x`, so its message records the source commit. | Original PR | Commit on `main` | Change | |---|---|---| | #3554 | `b846dfaa5` | fix(hermes): harden sandbox sessions | | #3784 | `e0e63e526` | feat(simple-agent): run simple agent through environment server sessions | This branch is based on the current `r0.7.0`, which already includes #4107 (#3818, #4070, #3929, #4053) and #4078. ## Why #3784 has the `r0.7.0` label, but the automatic cherry-pick workflow could not apply it: it hit a merge conflict in `nemo_gym/base_responses_api_agent.py` and reported success anyway, so nothing was opened. #3554 is included because #3784 builds on it. #3554 added the agent session handling (seed and close retries, session locks, close receipts) in `nemo_gym/base_responses_api_agent.py` that #3784 extends. With #3554 applied first, the conflict in that file goes away. #3554 has no `r0.7.0` label; it is included on purpose. ### Behavior changes from #3554 #3554's own description lists these, and they now apply on `r0.7.0` too: - Hermes host execution now combines the config system prompt, request instructions, and input system message instead of letting the config prompt replace the dataset prompt. It honors the request temperature, including zero, and rejects `developer` messages with 422. These changes can change benchmark scores for Hermes on the host path. - Hermes rejects unsupported non-default controls with a permanent 422, including `top_p=1.0`, `store`, `service_tier`, metadata overrides, `max_output_tokens`, and a `model` that does not match the configured model. ## Conflict resolution - **#3554**: applied cleanly. - **#3784**: one conflict in `environment_servers/single_agent_turn_legacy/tests/test_app.py`. #3784 adds two `task_source` tests next to a test that #4070 (already on `r0.7.0` through #4107) renamed. The resolution keeps both, and the new tests call the adapter method by its post-#4070 name, `_episode_request_from_row`. The resulting file is identical to the file on `main`. ## Validation - `pytest tests/unit_tests -n 8`: same failures as unmodified `r0.7.0` and no new ones. The existing failures are local-environment tests in `test_opensandbox_compose.py`, `test_sandbox_stop_retry.py`, and `test_slurm_script.py`. - #3554's focused suite, each path run separately with `--import-mode=importlib`: `environment_servers/single_agent_turn/tests`, `environment_servers/single_agent_turn_legacy/tests`, `environment_servers/legacy_agent/tests`, `responses_api_agents/hermes_sandboxed_agent/tests`, `resources_servers/swebench_pro/tests`, and `tests/unit_tests/test_{episode_types,environment_server,rollout_outcomes,failure_kinds,agent_session_retries,agent_sessions,base_responses_api_agent,process_supervisor,local_provider}.py`: all pass. - `responses_api_agents/hermes_agent/tests` in its own venv from `requirements.txt`: all pass. - `responses_api_agents/simple_agent/tests` and `resources_servers/example_single_tool_call/tests`: all pass. - `pre-commit run --files` on all files changed by the two commits: pass. ## Rollout evidence N/A for the cherry-pick itself. Both changes are applied unchanged from `main`, except for the one test-file conflict described above. #3554 includes a real-model Hermes with SWE-bench Pro lifecycle smoke rollout. --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> Signed-off-by: Marta Stepniewska-Dziubinska <martas@nvidia.com> Signed-off-by: Adil Asif <adasif@nvidia.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> Co-authored-by: Yu Yao <54727607+yaoyu-33@users.noreply.github.com> Co-authored-by: Marta Stepniewska-Dziubinska <martas@nvidia.com> Co-authored-by: Adil Asif <adasif@nvidia.com>
kajalj22
pushed a commit
to kajalj22/Gym
that referenced
this pull request
Oct 6, 2026
…on (NVIDIA-NeMo#4114) ## What does this PR do? Makes a SimpleAgent agent session return the observations of every activation at close, not only the last one. During a session, each `/v1/responses` call replaced `state.observations` with that call's invocation. An environment server that drives several activations through one session, such as the user simulation stack (NVIDIA-NeMo#3800, NVIDIA-NeMo#4080), would receive only the final activation's invocation at close, and `ng_agent_observations` would show one model call where there were several. `single_agent_turn` makes one call per session, so it was unaffected. The fix: - Records and gaps now accumulate across a session's activations. The bundle is rebuilt each time, so its unique-ID and parent-graph checks still run. - Every activation used the invocation ID `"root"`, so accumulating them would fail the bundle's unique-ID check. Later activations in a session now use `activation-<n>`. The first keeps `"root"`, so single-activation sessions report exactly what they did before. No issue is linked; this is a follow-up to NVIDIA-NeMo#3784, found while reviewing the SimpleAgent changes in the user simulation stack. ## Validation - New test `test_agent_session_returns_observations_from_every_activation`: two activations in one session return two invocations, `root` and `activation-2`, each linked to its own model response. It fails without the fix. - `pytest responses_api_agents/simple_agent/tests environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests`: all pass. - `pytest tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_base_responses_api_agent.py tests/unit_tests/test_rollout_observability.py`: all pass. - `pre-commit run` on the changed files: all pass. ## Rollouts N/A. Single-activation sessions, which every current configuration uses, produce the same observations as before. The change affects only sessions with several activations, which the user simulation environment server introduces; that stack carries its own rollout evidence. ## Compatibility No configuration change. For sessions with more than one activation, `ng_agent_observations` now contains one invocation per activation instead of only the last. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
adil-a
added a commit
that referenced
this pull request
Oct 9, 2026
…ugh the base hooks) main's session contract (#3784) makes the base agent own seed and close bookkeeping; an agent implements only _seed_agent_session_state and _close_agent_session_state. The oracle agent keeps its behavior (sandbox connect at seed, disconnect at close, 404 for an unseeded /v1/responses) but no longer overrides the routes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Adil Asif <adasif@nvidia.com>
adil-a
added a commit
that referenced
this pull request
Oct 9, 2026
…through the base hooks) main's session contract (#3784) makes the base agent own seed and close bookkeeping; an agent implements only _seed_agent_session_state and _close_agent_session_state. The Terminus 2 agent keeps its behavior (borrow the resources server's sandbox at seed, disconnect at close) but no longer overrides the routes. The multi-worker refusal now comes from the base as a ValueError before any Terminus state exists. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Adil Asif <adasif@nvidia.com>
adil-a
added a commit
that referenced
this pull request
Oct 9, 2026
… the base hooks, unbound template) main's session contract (#3784) makes the base agent own seed and close bookkeeping; the mini-SWE agent now implements _seed_agent_session_state and _close_agent_session_state. Episode state still lives in _sessions so the harness and legacy /run paths are unchanged; the base agent's session cookie names the seeded state, so the owner half of the episode key is derived from the agent session id instead of the resources cookie. main's add_legacy_agent_environment_servers check wants every dispatchable agent config fronted by an environment server; the mini-SWE episode-session config is an unbound template that prepare binds, so it declares resources_server.name as ??? like the Terminus 2 and oracle templates. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Adil Asif <adasif@nvidia.com>
adil-a
added a commit
that referenced
this pull request
Oct 9, 2026
…ugh the base hooks) main's session contract (#3784) makes the base agent own seed and close bookkeeping; an agent implements only _seed_agent_session_state and _close_agent_session_state. The oracle agent keeps its behavior (sandbox connect at seed, disconnect at close, 404 for an unseeded /v1/responses) but no longer overrides the routes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Adil Asif <adasif@nvidia.com>
adil-a
added a commit
that referenced
this pull request
Oct 9, 2026
…through the base hooks) main's session contract (#3784) makes the base agent own seed and close bookkeeping; an agent implements only _seed_agent_session_state and _close_agent_session_state. The Terminus 2 agent keeps its behavior (borrow the resources server's sandbox at seed, disconnect at close) but no longer overrides the routes. The multi-worker refusal now comes from the base as a ValueError before any Terminus state exists. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Adil Asif <adasif@nvidia.com>
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Lets Simple Agent run inside an Environment Server episode through agent sessions, gives the example single-tool-call Resources Server typed sessions, and adds a config that runs the example through the single-agent-turn Environment Server end to end.
/runis unchanged.Simple Agent sessions:
_seed_agent_session_stateand_close_agent_session_state. The base agent's shared bookkeeping from #3554 handles the rest: idempotent seed, binding a session to its seed, the session cookie, retries, and close receipts. The cookie key is one constant,AGENT_SESSION_COOKIE_KEY, innemo_gym.base_responses_api_agent.num_workers: 1./runkeeps no session and still supports several workers.resources_serveris optional in the Simple Agent config, because a session gets its tools from the Environment Server./runand/aggregate_metricsstill require it.Example single-tool-call Resources Server:
resources_session_id. Close overrides theclose_resources_sessionhook; the example no longer registers its own/close_sessionroute, which the base route takes precedence over.configs/example_single_tool_call_single_agent_turn.yamlfronts Simple Agent withsingle_agent_turn_legacyand grants it direct HTTP access to the example's tools. The existing config, which the quickstart and training docs use, still runs through/run.single_agent_turn_legacyaccepts atask_sourcethat names its Resources Server, its agent, or itself. Collation setstask_sourceto the instance that declares the dataset, usually the agent, so the adapter rejected collated rows before.The
close_resources_sessiondocstring states what a close override must do: succeed for a session that is already closed or was never seeded, keep a later seed from recreating a closed session, and raise when releasing fails so the caller can retry. It also notes that the default seed does not tie the session cookie toresources_session_id.Contract checks:
nemo_gym.testing.session_conformance.check_resources_session_contractis an integration test of a server's session lifecycle: it seeds, re-seeds and closes a session through the server's real routes and middleware, in process, and checks the close rules, so it also catches a shadowed route. The base default and the example run it; a migrated server calls it from its own tests.nemo_gym.testingholds helpers that tests import.tests/unit_tests/test_session_contract_repo.pyreads every agent and Resources Server's source, since servers run in their own environments. It fails when an agent overrides the public seed or close instead of the hooks, when an agent implements the seed hook without the close hook, when a Resources Server registers its own/close_session, or when a typed seed has noclose_resources_session.Issue
No issue is linked; this is part of the Environment Server work.
Validation
pytest responses_api_agents/simple_agent/tests -q: 26 passed. A seed after close now returns 409 from the base bookkeeping instead of a 500. A session tool call without a grant fails and reaches no Resources Server; that test fails without the fix.pytest resources_servers/example_single_tool_call/tests -q: 5 passed, including the session contract check.pytest environment_servers/single_agent_turn/tests environment_servers/single_agent_turn_legacy/tests -q: 30 and 12 passed, includingtask_sourcenaming the agent or the Resources Server, and rejecting another instance.pytest resources_servers/swebench_pro/tests -q: 96 passed.pytest responses_api_agents/hermes_agent/tests -q(its own venv): 219 passed.pytest tests/unit_tests/test_session_contract_repo.py tests/unit_tests/test_base_resources_server.py tests/unit_tests/test_agent_sessions.py tests/unit_tests/test_agent_session_retries.py tests/unit_tests/test_base_responses_api_agent.py -q: all passed. The repo-wide checks and the contract check each fail when the example registers its own/close_sessionagain; the repo-wide agent check fails when Simple Agent overridesclose_agent_session.pytest tests/unit_tests -q: all passed except tests that need packages this environment lacks (opensandbox,tenacity,mlflow,wandb) and a Slurm test that calls the system pip.pre-commit runon the changed files.Rollouts
gym dataset collateon the new config stampedtask_sourcewith the agent's name, and the five collatedexample_single_tool_calltasks ran withgpt-5.6-solthroughsingle_agent_turn_legacywith no failures. Three calledget_weatherand scored1.0; the other two prompts are ambiguous ("get the weather for 3 cities in the us", "how's the outside?"), and the model asked which city was meant, so they scored0.0as the verifier intends.A stateful check, run locally with
example_session_state_mgmtgiven a typed seed and close (that change is a separate PR) and asingle_agent_turnconfig like the one here: its counter starts at the task'sinitial_count, so a reward of1.0needs the seed, the agent's tool calls, and verification to reach the same Resources session. With the direct HTTP grant, all five collated tasks scored1.0(for example 3 seeded + 1 + 2 = 6), which confirms the grant's cookie reaches the seeded session without forwarding cookies on the agent seed request. With no grant, the earlier code scored0.0on all five because tool calls landed in fresh sessions; with the fix, those episodes fail with "Model called tool 'increment_counter', but this agent session has no direct HTTP tool access".Compatibility
/run, its request and result shapes, and existing Simple Agent configurations are unchanged.resources_serverbecomes optional in the Simple Agent config, and configurations that set it behave as before.