Repository navigation
feat(config): deprecate agents without an environment server and fix the migration script - #3929
Merged
Merged
Conversation
…y relay Rollout collection reaches agents only through an environment server, and config validation has rejected any agent that no environment server names since #3724. That breaks every configuration written before environment servers, including out-of-tree training configs, on upgrade. Instead of failing, generate the `legacy_agent` relay that scripts/add_legacy_agent_environment_servers.py would declare, named `<agent>_environment_server`, and log one deprecation warning listing every generated relay. Only agents with no environment server get one, so a generated relay never makes routing ambiguous or rewrites a reference. Setting `error_on_agent_without_environment_server: true` restores the error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Contributor
|
🌿 Preview your docs: https://nvidia-preview-pr-3929-2a1177468973.docs.buildwithfern.com/nemo/gym |
yaoyu-33
previously approved these changes
Oct 2, 2026
yaoyu-33
left a comment
Contributor
There was a problem hiding this comment.
LGTM. The compatibility relay preserves environment-server routing, and the warning encourages users to declare it explicitly in their config.
This was referenced Oct 2, 2026
Resolve the import conflict in tests/unit_tests/test_global_config.py by keeping both upstream's hide_secrets_in_overrides import (#3679) and this branch's rollout_collection import. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
…the migration script Make the generated legacy_agent relay an explicit deprecation: the warning says a future release will reject these configs, prints the exact block to add for each agent, and explains how to run the migration script or make the error take effect now. Generated relays are named the way the migration script declares them, so the printed block matches what the script writes. Fix scripts/add_legacy_agent_environment_servers.py so users can migrate their own configs: - Accept config files and directories outside the repository; it could only rewrite the repository's own configs. - Skip an agent that any environment server already names. It used to add a second server for such agents (for example the one benchmarks/swebench/pro/hermes.yaml fronts with single_agent_turn_legacy), which makes agent routing ambiguous. - Handle an agent renamed only through `_inherit_from` by inheriting the source agent's server; such renames were skipped. - Report configs that cannot be parsed (exit 2) instead of skipping them. A dangling environment server agent reference, left when an agent is renamed with `_inherit_from`, now points at the script. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
… text Comments, docstrings, the deprecation warning, the strict-mode error, and the troubleshooting page now keep each sentence on its own line instead of reflowing clauses across lines. Long sentences are split. The warning quoted in the docs matches the new text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
yaoyu-33
approved these changes
Oct 5, 2026
Contributor
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/37255919835 |
This was referenced Oct 5, 2026
7 tasks done
ananthsub
added a commit
to ananthsub/Gym
that referenced
this pull request
Oct 5, 2026
…the migration script (NVIDIA-NeMo#3929) Since NVIDIA-NeMo#3724, config validation rejects any agent that no environment server names, with `AgentWithoutEnvironmentServerError`. A config written before environment servers therefore stops loading when Gym is upgraded. This also affects configs in this repository: `resources_servers/wordle/configs/wordle.yaml` and `environments/wordle/config.yaml` fail to parse on `main`. This PR turns that error into a deprecation, so these configs keep running while users migrate. It also fixes the migration script so it can update configs outside this repository. During config parsing, Gym finds each agent that no environment server names. For each one, it adds a `legacy_agent` environment server in front of the agent. A `legacy_agent` server relays `/run` to its agent unchanged, so rollouts and results are the same as before. Gym then logs one deprecation warning: ``` DEPRECATED: agents without an environment server: `workplace_assistant_simple_agent`. Rollout collection reaches an agent only through an environment server. This run generated a legacy_agent environment server for each agent above. Each one relays /run to its agent unchanged, so results are not affected. A future release will reject these configs with AgentWithoutEnvironmentServerError. To migrate, declare the environment servers in your config. Either: 1. Run the migration script from a NeMo Gym checkout on your config files or directories: python scripts/add_legacy_agent_environment_servers.py path/to/config.yaml [more paths] [--check] --check reports what would change without writing. 2. Or add these blocks yourself, at the same level as the agents: workplace_assistant_environment_server: environment_servers: legacy_agent: entrypoint: app.py agent_server: type: responses_api_agents name: workplace_assistant_simple_agent To make this an error now, set error_on_agent_without_environment_server: true in your config. You can also pass +error_on_agent_without_environment_server=true on the command line. ``` - The generated server is named the way the migration script names it: the agent type is stripped from the agent name. For example, `workplace_assistant_simple_agent` gets `workplace_assistant_environment_server`. The block in the warning is therefore the block the script writes. If that name is already taken, the server is named `<agent>_environment_server` instead. - Gym only adds a server for an agent that has none. An agent with an existing environment server is left alone, so the new server can never make agent routing ambiguous. - Setting `error_on_agent_without_environment_server: true` restores the error. It follows the existing `error_on_almost_servers` switch. The error message now also points at the migration script. - Renaming an agent with `_inherit_from` moves it to the new name. An environment server that named the old agent is then left pointing at an agent that no longer exists, and parsing fails with `ServerRefNotFoundError`. That error now says the environment server must be renamed with the agent, and points at the script. The test this PR replaces explained why the error existed: without it, a config written before environment servers "would otherwise run, silently dispatching straight to the agent." The generated server keeps that guarantee. Rollout collection still reaches the agent only through an environment server, and the warning names every server Gym added. - The script now accepts config files and directories as arguments. Before, it could only update this repository's configs, so users had no way to migrate their own. Run without arguments, it still updates this repository's configs. - The script now skips an agent that any environment server already names. Before, it only checked whether a server with the generated name existed. In `benchmarks/swebench/pro/hermes.yaml`, a `single_agent_turn_legacy` server already fronts the agent under a different name, so the script added a second server. Two servers in front of one agent make agent routing ambiguous, and rollout collection then fails. - The script now handles an agent that only renames another one, such as `renamed_agent: {_inherit_from: some_agent}`. Before, such configs were skipped because the new instance has no agent body of its own. The script now writes a server for the new name that inherits the original agent's server. Because `_inherit_from` moves the original server, the renamed agent ends up with exactly one server. - A config that cannot be read or parsed is now reported, and the script exits with 2. Before, such files were skipped without any message. `test_langchain_deepagents_configs_have_environment_servers` now parses with `error_on_agent_without_environment_server: true`. The test checks that in-repo configs declare their environment server. A generated server would have the same name, so without strict mode the test would pass even when the declaration is missing. The script's `--check` mode reports six configs on `main` that declare no environment server: two `wordle` configs, two `swemer_oml` configs, and two `swe_internal_v1` configs. With this PR they load with the deprecation warning. Migrating them is left to their owners. No issue is linked. This is a compatibility follow-up to NVIDIA-NeMo#3724. - `pytest tests/unit_tests/test_global_config.py`, covering the generated server, its name, the warning text, strict mode, an agent that already has a server, a name collision, a full `parse()` of a config without environment servers, and the rename hint on `ServerRefNotFoundError`. - `pytest tests/unit_tests/test_add_legacy_agent_environment_servers.py` (new), covering configs outside the repository, `--check` not writing, an agent that already has a server, a rename through `_inherit_from`, an unreadable config, and that the script and Gym name servers the same way. The behavior tests fail against the script on `main`. - Six configs outside this repository that failed validation were migrated with the script, covering both added agents and `_inherit_from` renames. Each migrated config was then parsed with `GlobalConfigDictParser` and `error_on_agent_without_environment_server: true`. An unmigrated copy, parsed the same way, raised `AgentWithoutEnvironmentServerError`. - `pytest tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_scaffold.py`. - `pytest tests/unit_tests`. The tests that fail in this environment fail identically on `main`. They need the `sandbox` extra, or `mlflow` and `wandb`, which NVIDIA-NeMo#3547 moved into extras. - `pre-commit run --all-files`. `example_single_tool_call` was run with its environment server block removed from the config, against `Qwen/Qwen3-0.6B` served by a local vLLM with the hermes tool parser: - `gym env start` logged the deprecation warning and started the generated environment server alongside the agent, the resources server, and the model server. - `gym eval run --no-serve` completed every requested rollout without failures. Each record has the generated server in `_ng_environment_server` and `legacy_agent` in `_ng_result_type`. - When the model called `get_weather`, the verifier scored the rollout 1.0. When it answered in prose without calling the tool, the verifier scored it 0.0. Dispatch through the generated server has not changed since that run. Only the server's name and the warning text have. - Configs that failed with `AgentWithoutEnvironmentServerError` now load and log a deprecation warning. Results do not change, because `legacy_agent` relays `/run` to the agent unchanged. - Users who want the error back can set `error_on_agent_without_environment_server: true`. The warning tells users that a future release will make this the default. - No benchmark impact. Proposed labels: `feature`, `area:config`. - [x] I have read the [contributing guidelines](https://github.com/NVIDIA-NeMo/Gym/blob/main/CONTRIBUTING.md). - [x] The change is focused; unrelated "drive-by" edits are tracked as separate issues/PRs. - [x] Tests added or updated and pass locally, or N/A for docs-only / non-code changes (so CI unit/server checks pass when applicable). - [x] Documentation added or updated, or N/A with justification. - [x] Pre-commit checks pass locally (`pre-commit run --all-files`) (so CI lint/format checks pass). - [x] New source files include the required Apache-2.0 SPDX header. - [x] All commits have DCO sign-off (`git commit -s`) (so the DCO check passes). --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> (cherry picked from commit 1f2446c) Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ananthsub
added a commit
to ananthsub/Gym
that referenced
this pull request
Oct 6, 2026
…the migration script (NVIDIA-NeMo#3929) Since NVIDIA-NeMo#3724, config validation rejects any agent that no environment server names, with `AgentWithoutEnvironmentServerError`. A config written before environment servers therefore stops loading when Gym is upgraded. This also affects configs in this repository: `resources_servers/wordle/configs/wordle.yaml` and `environments/wordle/config.yaml` fail to parse on `main`. This PR turns that error into a deprecation, so these configs keep running while users migrate. It also fixes the migration script so it can update configs outside this repository. During config parsing, Gym finds each agent that no environment server names. For each one, it adds a `legacy_agent` environment server in front of the agent. A `legacy_agent` server relays `/run` to its agent unchanged, so rollouts and results are the same as before. Gym then logs one deprecation warning: ``` DEPRECATED: agents without an environment server: `workplace_assistant_simple_agent`. Rollout collection reaches an agent only through an environment server. This run generated a legacy_agent environment server for each agent above. Each one relays /run to its agent unchanged, so results are not affected. A future release will reject these configs with AgentWithoutEnvironmentServerError. To migrate, declare the environment servers in your config. Either: 1. Run the migration script from a NeMo Gym checkout on your config files or directories: python scripts/add_legacy_agent_environment_servers.py path/to/config.yaml [more paths] [--check] --check reports what would change without writing. 2. Or add these blocks yourself, at the same level as the agents: workplace_assistant_environment_server: environment_servers: legacy_agent: entrypoint: app.py agent_server: type: responses_api_agents name: workplace_assistant_simple_agent To make this an error now, set error_on_agent_without_environment_server: true in your config. You can also pass +error_on_agent_without_environment_server=true on the command line. ``` - The generated server is named the way the migration script names it: the agent type is stripped from the agent name. For example, `workplace_assistant_simple_agent` gets `workplace_assistant_environment_server`. The block in the warning is therefore the block the script writes. If that name is already taken, the server is named `<agent>_environment_server` instead. - Gym only adds a server for an agent that has none. An agent with an existing environment server is left alone, so the new server can never make agent routing ambiguous. - Setting `error_on_agent_without_environment_server: true` restores the error. It follows the existing `error_on_almost_servers` switch. The error message now also points at the migration script. - Renaming an agent with `_inherit_from` moves it to the new name. An environment server that named the old agent is then left pointing at an agent that no longer exists, and parsing fails with `ServerRefNotFoundError`. That error now says the environment server must be renamed with the agent, and points at the script. The test this PR replaces explained why the error existed: without it, a config written before environment servers "would otherwise run, silently dispatching straight to the agent." The generated server keeps that guarantee. Rollout collection still reaches the agent only through an environment server, and the warning names every server Gym added. - The script now accepts config files and directories as arguments. Before, it could only update this repository's configs, so users had no way to migrate their own. Run without arguments, it still updates this repository's configs. - The script now skips an agent that any environment server already names. Before, it only checked whether a server with the generated name existed. In `benchmarks/swebench/pro/hermes.yaml`, a `single_agent_turn_legacy` server already fronts the agent under a different name, so the script added a second server. Two servers in front of one agent make agent routing ambiguous, and rollout collection then fails. - The script now handles an agent that only renames another one, such as `renamed_agent: {_inherit_from: some_agent}`. Before, such configs were skipped because the new instance has no agent body of its own. The script now writes a server for the new name that inherits the original agent's server. Because `_inherit_from` moves the original server, the renamed agent ends up with exactly one server. - A config that cannot be read or parsed is now reported, and the script exits with 2. Before, such files were skipped without any message. `test_langchain_deepagents_configs_have_environment_servers` now parses with `error_on_agent_without_environment_server: true`. The test checks that in-repo configs declare their environment server. A generated server would have the same name, so without strict mode the test would pass even when the declaration is missing. The script's `--check` mode reports six configs on `main` that declare no environment server: two `wordle` configs, two `swemer_oml` configs, and two `swe_internal_v1` configs. With this PR they load with the deprecation warning. Migrating them is left to their owners. No issue is linked. This is a compatibility follow-up to NVIDIA-NeMo#3724. - `pytest tests/unit_tests/test_global_config.py`, covering the generated server, its name, the warning text, strict mode, an agent that already has a server, a name collision, a full `parse()` of a config without environment servers, and the rename hint on `ServerRefNotFoundError`. - `pytest tests/unit_tests/test_add_legacy_agent_environment_servers.py` (new), covering configs outside the repository, `--check` not writing, an agent that already has a server, a rename through `_inherit_from`, an unreadable config, and that the script and Gym name servers the same way. The behavior tests fail against the script on `main`. - Six configs outside this repository that failed validation were migrated with the script, covering both added agents and `_inherit_from` renames. Each migrated config was then parsed with `GlobalConfigDictParser` and `error_on_agent_without_environment_server: true`. An unmigrated copy, parsed the same way, raised `AgentWithoutEnvironmentServerError`. - `pytest tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_scaffold.py`. - `pytest tests/unit_tests`. The tests that fail in this environment fail identically on `main`. They need the `sandbox` extra, or `mlflow` and `wandb`, which NVIDIA-NeMo#3547 moved into extras. - `pre-commit run --all-files`. `example_single_tool_call` was run with its environment server block removed from the config, against `Qwen/Qwen3-0.6B` served by a local vLLM with the hermes tool parser: - `gym env start` logged the deprecation warning and started the generated environment server alongside the agent, the resources server, and the model server. - `gym eval run --no-serve` completed every requested rollout without failures. Each record has the generated server in `_ng_environment_server` and `legacy_agent` in `_ng_result_type`. - When the model called `get_weather`, the verifier scored the rollout 1.0. When it answered in prose without calling the tool, the verifier scored it 0.0. Dispatch through the generated server has not changed since that run. Only the server's name and the warning text have. - Configs that failed with `AgentWithoutEnvironmentServerError` now load and log a deprecation warning. Results do not change, because `legacy_agent` relays `/run` to the agent unchanged. - Users who want the error back can set `error_on_agent_without_environment_server: true`. The warning tells users that a future release will make this the default. - No benchmark impact. Proposed labels: `feature`, `area:config`. - [x] I have read the [contributing guidelines](https://github.com/NVIDIA-NeMo/Gym/blob/main/CONTRIBUTING.md). - [x] The change is focused; unrelated "drive-by" edits are tracked as separate issues/PRs. - [x] Tests added or updated and pass locally, or N/A for docs-only / non-code changes (so CI unit/server checks pass when applicable). - [x] Documentation added or updated, or N/A with justification. - [x] Pre-commit checks pass locally (`pre-commit run --all-files`) (so CI lint/format checks pass). - [x] New source files include the required Apache-2.0 SPDX header. - [x] All commits have DCO sign-off (`git commit -s`) (so the DCO check passes). --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> (cherry picked from commit 1f2446c) Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ananthsub
added a commit
to ananthsub/Gym
that referenced
this pull request
Oct 6, 2026
…VIDIA-NeMo#4053) ## What does this PR do? Makes Gym and its migration tooling agree on which agents an environment server fronts, including servers that front several agents. It combines the runtime change from NVIDIA-NeMo#4059 with fixes to `scripts/add_legacy_agent_environment_servers.py`, the `ServerRefNotFoundError` rename hint, and the troubleshooting docs, so the environment server and agent server changes land together. The same commits are in the `r0.7.0` cherry-pick of NVIDIA-NeMo#3929 (NVIDIA-NeMo#4050). ## Where this fits Datasets reach an environment server in one of two ways today: run request rows route by their agent, and datasets that declare `taskset:` are materialized during collation and route by `environment_server_routes`. The goal is one path, where every dataset declares a taskset and routes to an environment server: ```mermaid flowchart LR P["Preparation script<br/>task rows with a stable task_id"] --> C["Collation<br/>validates, computes metrics,<br/>materializes"] Y["Dataset YAML<br/>file + taskset"] --> C S["task_data.py schemas<br/>environment server + resources server"] -.-> C C --> R["Rollout collection<br/>routes by taskset"] T["environment_server_routes"] --> R R --> E["Environment server<br/>single-agent, multi-agent,<br/>or legacy_agent relay"] ``` This PR is one step on the way there. Each step is additive, and datasets without `taskset:` keep working until the last one: ```mermaid flowchart TB S3818["PR 3818 (merged)<br/>collation materializes<br/>declared tasksets"] S4053["PR 4053<br/>tasksets route to servers<br/>with several agents"] S4096["PR 4096<br/>stable, unique<br/>task IDs"] S4097["PR 4097<br/>metrics for<br/>every row"] S4098["PR 4098<br/>schema per<br/>environment server"] S4099["PR 4099<br/>legacy_agent relays<br/>serve tasksets"] SNEXT["Later<br/>configs declare taskset:,<br/>then agent routing retires"] S3818 --> S4053 --> S4096 --> S4097 --> S4098 --> S4099 --> SNEXT classDef done fill:#F1EFE8,stroke:#5F5E5A,color:#444441 classDef this fill:#EEEDFE,stroke:#534AB7,color:#26215C,stroke-width:2px classDef next fill:#E1F5EE,stroke:#0F6E56,color:#085041 class S3818 done class S4053 this class S4096,S4097,S4098,S4099,SNEXT next ``` Gray: merged. Purple: this PR. Green: other steps. ### Environment servers that front several agents An environment server can front several agents, such as the user and assistant agents of the user-simulation server in NVIDIA-NeMo#3800. It references them through fields such as `user_agent` and `assistant_agent`. Gym only recognized the `agent_server` field. For such a server: - The deprecation check from NVIDIA-NeMo#3929 treated its agents as unfronted and generated a `legacy_agent` relay in front of each. - Agent-routed rows could not find the server, because `_environment_servers_by_agent` only read `agent_server`. The first commit from NVIDIA-NeMo#4059 (authored by Felipe Vieira Frujeri) adds `environment_server_agent_refs` to `nemo_gym/global_config.py`. Any top-level field whose value has `type: responses_api_agents` references an agent, and so does `agent_server`, whose type may be left out. The deprecation check, agent routing, and `_retarget_environment_servers` (which follows an agent swapped during composition) use it. Two places in `nemo_gym/rollout_collection.py` still read only `agent_server`: - `_agent_name_for_row` names the agent a row's results are attributed to. It decides whether token IDs are captured and labels aggregate metrics. Results are now attributed to the server's `agent_server`, or to the only agent it references. A server that fronts several agents without an `agent_server` has no single agent to attribute, as before. - `_validate_environment_servers` rejects a row whose `agent_ref` names an agent the server does not front. It skipped the check for any server without `agent_server`. A row may now name any agent its server references. ### The migration script The script assumed an environment server references one agent, through `agent_server`. It now finds agent references with the same rule as `environment_server_agent_refs`. The script depends only on PyYAML, so it keeps its own copy of the rule, and a test checks that both agree. Each referenced agent counts as fronted, so the script no longer adds relays in front of agents a multi-agent server already fronts, such as those in `resources_servers/usersim/configs/usersim.yaml` from NVIDIA-NeMo#3800. For an agent renamed with `_inherit_from`, the renamed agent's server used to inherit from the name the script would generate for the source, always as a `legacy_agent` relay. That failed in three ways: - The source's server has a custom name: the inherited name does not exist, raising `InheritPathNotFoundError`. - The source's server is `single_agent_turn_legacy`: the inherited server gains a second `legacy_agent` block without an entrypoint, raising `AlmostServerError`. - Inheriting retires the source server's name, which `environment_server_routes` may still use. For example, usersim routes its tasksets to `usersim_environment`. The script now handles a rename this way: - Where a declared server references the source, the script points that reference at the new name, on the existing server. The server keeps its name, so routes and the server's other agents are unaffected. Renaming several agents of one server writes one block for that server. - Where the source has only the server the script generates, the renamed agent's server inherits it, as before. - A rename in the same file as a server that references its source cannot be migrated by appending, because the server's key would repeat. The script reports the field to update by hand and exits with 2. ### The renamed-agent hint When an environment server references an agent that no longer exists, `ServerRefNotFoundError` suggests the agent may have been renamed with `_inherit_from`. The hint said the environment server "must be renamed with it", which no longer matches the script. - The hint now says the environment server must reference the agent's new name, and names the field to update instead of always `agent_server.name`. - It is shown for any agent reference on an environment server, not only `agent_server`. - `fern/versions/latest/pages/troubleshooting/configuration.mdx` describes the same rename behavior, and the script's exit code 2 for a rename that needs a manual update. ### Declared tasksets routed to a multi-agent server NVIDIA-NeMo#3818 added `resolve_dataset_agent`'s taskset path, used by benchmark discovery, `gym eval prepare`, manifest validation, and collation. It read only the routed environment server's `agent_server`, so a taskset routed to a server that fronts several agents failed with "must bind a valid agent_server". A taskset routes to an environment server, not to an agent: rollout collection sends materialized rows to the server that `environment_server_routes` names and never uses an agent. So resolution now checks the route instead of choosing an agent: - Every agent the server references, through `environment_server_agent_refs`, must exist. - A dataset declared on an agent must be one of those agents. - A dataset declared on a resources server must be the one the server binds. - A dataset with `taskset:` cannot set `agent:`. Only benchmark datasets accept `agent:`, so requiring a pin would have left example and train datasets for a server with several agents unable to collate, and would have made an environment server that owns its dataset name one of its own agents. It returns the declaring agent, or the server's attributed agent (its `agent_server`, or its only agent), or none when the server fronts several agents without an `agent_server`. `environment_server_agent_names` and `environment_server_attributed_agent` move to `global_config.py` so resolution and rollout attribution share that rule. Benchmarks record the environment server, and `gym eval list` shows it when there is no single agent. Environment manifest validation reports that a manifest, which describes one agent, cannot describe such a taskset. `resources-server-apis.mdx` describes the route check. This supersedes NVIDIA-NeMo#4059. No issue is linked. It follows up NVIDIA-NeMo#3929, from review on NVIDIA-NeMo#4050, and provides the runtime support NVIDIA-NeMo#3800 needs. ## Validation - `tests/unit_tests/test_global_config.py`: a multi-agent server fronts all of its agents and needs no relays, removing one agent's reference makes that agent unfronted, and a composition swap retargets only the swapped agent's reference. The rename hint test runs for an `agent_server` and a `user_agent` reference. - `tests/unit_tests/test_rollout_collection.py`: attribution to `agent_server`, to a server's only agent, and to no agent for a server with several, and row validation accepting any agent of the server and rejecting others. - `tests/unit_tests/test_add_legacy_agent_environment_servers.py`: migrate-then-parse tests, which run the script and parse the result with `GlobalConfigDictParser`. They cover a rename whose source server is generated, custom-named, or `single_agent_turn_legacy`, a two-agent server the script must leave alone, renaming both agents of that server (parsed with `error_on_agent_without_environment_server: true`), and a rename in the same file as the server it would update. A test checks that the script and `environment_server_agent_refs` find the same references. - `tests/unit_tests/test_benchmarks.py`: a multi-agent taskset resolves without a pin, to none or to the server's `agent_server`; any pin on a taskset dataset is rejected; a reference to a non-agent is rejected; a declaring participant is accepted; benchmark, example, and train datasets collate and route end to end; the benchmark listing names the environment server; and manifest validation reports a multi-agent taskset. These tests fail without the change. - `pytest tests/unit_tests/test_global_config.py tests/unit_tests/test_add_legacy_agent_environment_servers.py tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_scaffold.py tests/unit_tests/test_benchmarks.py tests/unit_tests/test_task_materialization.py`: all pass. - `pytest tests/unit_tests`: the tests that fail in this environment fail identically on `main`. They need the `sandbox` and telemetry extras, or Slurm. - With NVIDIA-NeMo#3800 merged locally: the script leaves `resources_servers/usersim/configs/usersim.yaml` unchanged. After renaming `usersim_assistant`, the usersim config parses with `error_on_agent_without_environment_server: true`, `usersim_environment` fronts both agents, and both usersim routes still name an existing server. - `python scripts/add_legacy_agent_environment_servers.py --check` on this repository reports the same files and blocks before and after this change. - `pre-commit run` on the changed files: all pass. ## Rollouts N/A. Single-agent servers resolve, route, and attribute exactly as before, and no configuration in this repository changes. The new behavior applies only to servers that reference agents through fields other than `agent_server`. None exist on `main` until NVIDIA-NeMo#3800, which carries its own rollout evidence. The migration script runs offline. ## Compatibility and benchmark impact - Environment servers that front several agents no longer get generated relays, and their agents route to them. - The error for a row that names the wrong agent for its environment server now lists the server's agents. - A dataset that declares `taskset:` can no longer set `agent:`. NVIDIA-NeMo#3818 accepted a pin that matched the routed agent; no configuration in this repository declares a taskset. - `gym eval list --json` adds an `environment_server` field, and `agent_name` is null for a taskset routed to a server with several agents. - The migration script updates a declared server in place when an agent is renamed, so server names and routes no longer change. - No benchmark impact. --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> Signed-off-by: Felipe Vieira Frujeri <ffrujeri@nvidia.com> Co-authored-by: Felipe Vieira Frujeri <ffrujeri@nvidia.com> (cherry picked from commit 89c065b) Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ehosseiniasl
pushed a commit
to ehosseiniasl/Gym
that referenced
this pull request
Oct 6, 2026
…VIDIA-NeMo#4053) ## What does this PR do? Makes Gym and its migration tooling agree on which agents an environment server fronts, including servers that front several agents. It combines the runtime change from NVIDIA-NeMo#4059 with fixes to `scripts/add_legacy_agent_environment_servers.py`, the `ServerRefNotFoundError` rename hint, and the troubleshooting docs, so the environment server and agent server changes land together. The same commits are in the `r0.7.0` cherry-pick of NVIDIA-NeMo#3929 (NVIDIA-NeMo#4050). ## Where this fits Datasets reach an environment server in one of two ways today: run request rows route by their agent, and datasets that declare `taskset:` are materialized during collation and route by `environment_server_routes`. The goal is one path, where every dataset declares a taskset and routes to an environment server: ```mermaid flowchart LR P["Preparation script<br/>task rows with a stable task_id"] --> C["Collation<br/>validates, computes metrics,<br/>materializes"] Y["Dataset YAML<br/>file + taskset"] --> C S["task_data.py schemas<br/>environment server + resources server"] -.-> C C --> R["Rollout collection<br/>routes by taskset"] T["environment_server_routes"] --> R R --> E["Environment server<br/>single-agent, multi-agent,<br/>or legacy_agent relay"] ``` This PR is one step on the way there. Each step is additive, and datasets without `taskset:` keep working until the last one: ```mermaid flowchart TB S3818["PR 3818 (merged)<br/>collation materializes<br/>declared tasksets"] S4053["PR 4053<br/>tasksets route to servers<br/>with several agents"] S4096["PR 4096<br/>stable, unique<br/>task IDs"] S4097["PR 4097<br/>metrics for<br/>every row"] S4098["PR 4098<br/>schema per<br/>environment server"] S4099["PR 4099<br/>legacy_agent relays<br/>serve tasksets"] SNEXT["Later<br/>configs declare taskset:,<br/>then agent routing retires"] S3818 --> S4053 --> S4096 --> S4097 --> S4098 --> S4099 --> SNEXT classDef done fill:#F1EFE8,stroke:#5F5E5A,color:#444441 classDef this fill:#EEEDFE,stroke:#534AB7,color:#26215C,stroke-width:2px classDef next fill:#E1F5EE,stroke:#0F6E56,color:#085041 class S3818 done class S4053 this class S4096,S4097,S4098,S4099,SNEXT next ``` Gray: merged. Purple: this PR. Green: other steps. ### Environment servers that front several agents An environment server can front several agents, such as the user and assistant agents of the user-simulation server in NVIDIA-NeMo#3800. It references them through fields such as `user_agent` and `assistant_agent`. Gym only recognized the `agent_server` field. For such a server: - The deprecation check from NVIDIA-NeMo#3929 treated its agents as unfronted and generated a `legacy_agent` relay in front of each. - Agent-routed rows could not find the server, because `_environment_servers_by_agent` only read `agent_server`. The first commit from NVIDIA-NeMo#4059 (authored by Felipe Vieira Frujeri) adds `environment_server_agent_refs` to `nemo_gym/global_config.py`. Any top-level field whose value has `type: responses_api_agents` references an agent, and so does `agent_server`, whose type may be left out. The deprecation check, agent routing, and `_retarget_environment_servers` (which follows an agent swapped during composition) use it. Two places in `nemo_gym/rollout_collection.py` still read only `agent_server`: - `_agent_name_for_row` names the agent a row's results are attributed to. It decides whether token IDs are captured and labels aggregate metrics. Results are now attributed to the server's `agent_server`, or to the only agent it references. A server that fronts several agents without an `agent_server` has no single agent to attribute, as before. - `_validate_environment_servers` rejects a row whose `agent_ref` names an agent the server does not front. It skipped the check for any server without `agent_server`. A row may now name any agent its server references. ### The migration script The script assumed an environment server references one agent, through `agent_server`. It now finds agent references with the same rule as `environment_server_agent_refs`. The script depends only on PyYAML, so it keeps its own copy of the rule, and a test checks that both agree. Each referenced agent counts as fronted, so the script no longer adds relays in front of agents a multi-agent server already fronts, such as those in `resources_servers/usersim/configs/usersim.yaml` from NVIDIA-NeMo#3800. For an agent renamed with `_inherit_from`, the renamed agent's server used to inherit from the name the script would generate for the source, always as a `legacy_agent` relay. That failed in three ways: - The source's server has a custom name: the inherited name does not exist, raising `InheritPathNotFoundError`. - The source's server is `single_agent_turn_legacy`: the inherited server gains a second `legacy_agent` block without an entrypoint, raising `AlmostServerError`. - Inheriting retires the source server's name, which `environment_server_routes` may still use. For example, usersim routes its tasksets to `usersim_environment`. The script now handles a rename this way: - Where a declared server references the source, the script points that reference at the new name, on the existing server. The server keeps its name, so routes and the server's other agents are unaffected. Renaming several agents of one server writes one block for that server. - Where the source has only the server the script generates, the renamed agent's server inherits it, as before. - A rename in the same file as a server that references its source cannot be migrated by appending, because the server's key would repeat. The script reports the field to update by hand and exits with 2. ### The renamed-agent hint When an environment server references an agent that no longer exists, `ServerRefNotFoundError` suggests the agent may have been renamed with `_inherit_from`. The hint said the environment server "must be renamed with it", which no longer matches the script. - The hint now says the environment server must reference the agent's new name, and names the field to update instead of always `agent_server.name`. - It is shown for any agent reference on an environment server, not only `agent_server`. - `fern/versions/latest/pages/troubleshooting/configuration.mdx` describes the same rename behavior, and the script's exit code 2 for a rename that needs a manual update. ### Declared tasksets routed to a multi-agent server NVIDIA-NeMo#3818 added `resolve_dataset_agent`'s taskset path, used by benchmark discovery, `gym eval prepare`, manifest validation, and collation. It read only the routed environment server's `agent_server`, so a taskset routed to a server that fronts several agents failed with "must bind a valid agent_server". A taskset routes to an environment server, not to an agent: rollout collection sends materialized rows to the server that `environment_server_routes` names and never uses an agent. So resolution now checks the route instead of choosing an agent: - Every agent the server references, through `environment_server_agent_refs`, must exist. - A dataset declared on an agent must be one of those agents. - A dataset declared on a resources server must be the one the server binds. - A dataset with `taskset:` cannot set `agent:`. Only benchmark datasets accept `agent:`, so requiring a pin would have left example and train datasets for a server with several agents unable to collate, and would have made an environment server that owns its dataset name one of its own agents. It returns the declaring agent, or the server's attributed agent (its `agent_server`, or its only agent), or none when the server fronts several agents without an `agent_server`. `environment_server_agent_names` and `environment_server_attributed_agent` move to `global_config.py` so resolution and rollout attribution share that rule. Benchmarks record the environment server, and `gym eval list` shows it when there is no single agent. Environment manifest validation reports that a manifest, which describes one agent, cannot describe such a taskset. `resources-server-apis.mdx` describes the route check. This supersedes NVIDIA-NeMo#4059. No issue is linked. It follows up NVIDIA-NeMo#3929, from review on NVIDIA-NeMo#4050, and provides the runtime support NVIDIA-NeMo#3800 needs. ## Validation - `tests/unit_tests/test_global_config.py`: a multi-agent server fronts all of its agents and needs no relays, removing one agent's reference makes that agent unfronted, and a composition swap retargets only the swapped agent's reference. The rename hint test runs for an `agent_server` and a `user_agent` reference. - `tests/unit_tests/test_rollout_collection.py`: attribution to `agent_server`, to a server's only agent, and to no agent for a server with several, and row validation accepting any agent of the server and rejecting others. - `tests/unit_tests/test_add_legacy_agent_environment_servers.py`: migrate-then-parse tests, which run the script and parse the result with `GlobalConfigDictParser`. They cover a rename whose source server is generated, custom-named, or `single_agent_turn_legacy`, a two-agent server the script must leave alone, renaming both agents of that server (parsed with `error_on_agent_without_environment_server: true`), and a rename in the same file as the server it would update. A test checks that the script and `environment_server_agent_refs` find the same references. - `tests/unit_tests/test_benchmarks.py`: a multi-agent taskset resolves without a pin, to none or to the server's `agent_server`; any pin on a taskset dataset is rejected; a reference to a non-agent is rejected; a declaring participant is accepted; benchmark, example, and train datasets collate and route end to end; the benchmark listing names the environment server; and manifest validation reports a multi-agent taskset. These tests fail without the change. - `pytest tests/unit_tests/test_global_config.py tests/unit_tests/test_add_legacy_agent_environment_servers.py tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_scaffold.py tests/unit_tests/test_benchmarks.py tests/unit_tests/test_task_materialization.py`: all pass. - `pytest tests/unit_tests`: the tests that fail in this environment fail identically on `main`. They need the `sandbox` and telemetry extras, or Slurm. - With NVIDIA-NeMo#3800 merged locally: the script leaves `resources_servers/usersim/configs/usersim.yaml` unchanged. After renaming `usersim_assistant`, the usersim config parses with `error_on_agent_without_environment_server: true`, `usersim_environment` fronts both agents, and both usersim routes still name an existing server. - `python scripts/add_legacy_agent_environment_servers.py --check` on this repository reports the same files and blocks before and after this change. - `pre-commit run` on the changed files: all pass. ## Rollouts N/A. Single-agent servers resolve, route, and attribute exactly as before, and no configuration in this repository changes. The new behavior applies only to servers that reference agents through fields other than `agent_server`. None exist on `main` until NVIDIA-NeMo#3800, which carries its own rollout evidence. The migration script runs offline. ## Compatibility and benchmark impact - Environment servers that front several agents no longer get generated relays, and their agents route to them. - The error for a row that names the wrong agent for its environment server now lists the server's agents. - A dataset that declares `taskset:` can no longer set `agent:`. NVIDIA-NeMo#3818 accepted a pin that matched the routed agent; no configuration in this repository declares a taskset. - `gym eval list --json` adds an `environment_server` field, and `agent_name` is null for a taskset routed to a server with several agents. - The migration script updates a declared server in place when an agent is renamed, so server names and routes no longer change. - No benchmark impact. --------- Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> Signed-off-by: Felipe Vieira Frujeri <ffrujeri@nvidia.com> Co-authored-by: Felipe Vieira Frujeri <ffrujeri@nvidia.com>
ananthsub
added a commit
to ananthsub/Gym
that referenced
this pull request
Oct 6, 2026
…igration Six configs added after NVIDIA-NeMo#3929 (wordle, swemer_oml, swe_internal_v1) declare agents without an environment server, so every run generates legacy_agent relays with a deprecation warning, and runs with error_on_agent_without_environment_server fail. Add the relays with scripts/add_legacy_agent_environment_servers.py. Nothing checked repository configs, so add a test that runs the script's --check over them. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ananthsub
added a commit
that referenced
this pull request
Oct 6, 2026
…vers (#3818, #4070, #3929, #4053) into `r0.7.0` (#4107) ## What This PR only cherry-picks four already-merged `main` PRs into `r0.7.0`. It adds no new code. The commits are applied in their `main` merge order with `git cherry-pick -x`, so each commit message records its source commit. | Original PR | Commit on `main` | Change | |---|---|---| | #3818 | `325fb49d6` | feat(data): collate declared tasksets | | #4070 | `412efc730` | refactor: name episode requests and sessions without "native" | | #3929 | `1f2446c17` | feat(config): deprecate agents without an environment server and fix the migration script | | #4053 | `89c065b0e` | feat(config): support environment servers that front several agents | ## Why #4070 and #4053 carry the `r0.7.0` label, but the automatic cherry-pick workflow could not apply them: both hit merge conflicts on `r0.7.0`. The workflow still reports success, so the failure was silent. #3818 was intended for `r0.7.0` but never had the label. #3929 is included because #4053 builds on it. Every #4053 conflict came from #3929 changes to `nemo_gym/global_config.py`, `scripts/add_legacy_agent_environment_servers.py`, and their tests. With #3929 applied first, #4053 applies cleanly. ## Conflict resolution Picking #3818 first removed one #4070 conflict. The remaining conflicts were in tests that other PRs added on `main` after the branch cut: - **#4070**: renames identifiers, comments, and test names; no behavior change. Where the conflict was a test that exists only on `main` (`single_agent_turn`, SWE-bench Pro, and Hermes `test_agent_sessions.py`), the `r0.7.0` version is kept and the `main`-only test is not brought in. In `single_agent_turn_legacy`, the existing test is renamed to `test_flat_rows_and_episode_requests_project_the_same_result` to match `main`, and the `task_source` tests from a separate PR are left out. - **#3929**: one import line in `tests/unit_tests/test_global_config.py`. `main` also imports `hide_secrets_in_overrides`, added by #3679 and unused in this file, so the `r0.7.0` import is kept. - **#3818** and **#4053**: applied cleanly. ## Validation - `pytest tests/unit_tests/test_global_config.py tests/unit_tests/test_add_legacy_agent_environment_servers.py tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_server.py tests/unit_tests/test_task_materialization.py tests/unit_tests/test_benchmarks.py`: pass. - `pytest environment_servers/single_agent_turn/tests` and `pytest environment_servers/single_agent_turn_legacy/tests` (run separately): pass. - `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`. - `pre-commit run --files` on all changed files: pass. - Checked for old names #4070 renames (`_native_request`, `_native_episode_request_body`, `_native_session_locks`, `_closed_native_sessions`): none remain. The few remaining "native row" comments are also on `main`. - SWE-bench Pro and Hermes server tests were not run locally (they need per-server venvs); release-branch CI covers them. ## Rollout evidence N/A. Each change was reviewed and validated on `main`. This PR only carries them to the release branch: #4070 is a rename with no behavior change, and the config changes are covered by the unit tests above. --------- Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com> Signed-off-by: Felipe Vieira Frujeri <ffrujeri@nvidia.com> Co-authored-by: Yu Yao <54727607+yaoyu-33@users.noreply.github.com> Co-authored-by: Felipe Vieira Frujeri <ffrujeri@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>
katherineh123
pushed a commit
to katherineh123/Gym
that referenced
this pull request
Oct 6, 2026
…igration (NVIDIA-NeMo#4084) ## What does this PR do? Declares `legacy_agent` environment servers for the six configs added after NVIDIA-NeMo#3929 that have agents with no environment server: - `environments/wordle/config.yaml` - `resources_servers/wordle/configs/wordle.yaml` - `resources_servers/swemer_oml/configs/swemer_oml_example.yaml` - `resources_servers/swemer_oml/configs/swemer_oml_opencode.yaml` - `resources_servers/swe_internal_v1/configs/swe_internal_v1_example.yaml` - `resources_servers/swe_internal_v1/configs/swe_internal_v1_opencode.yaml` Without these, every run of these configs generates the relays at startup and logs the deprecation warning, and a run with `error_on_agent_without_environment_server: true` fails with `AgentWithoutEnvironmentServerError`. The blocks were written by `scripts/add_legacy_agent_environment_servers.py`, so they match what the runtime generates today. Nothing checked the repository's configs, which is how these slipped in. This PR adds a unit test that runs the script's `--check` over the repository and fails if any agent still lacks an environment server. ## Issue No issue; follow-up to NVIDIA-NeMo#3929. ## Validation - The new test, `test_repository_configs_declare_every_environment_server`, fails on `main`'s configs and passes with this change. - `gym env resolve --config <file> +error_on_agent_without_environment_server=true` on each of the six configs: before this change, all six are rejected for having no environment server; after it, all six pass that check. Resolution then stops at the unset `model_server` values, which need a local `env.yaml`. - `pytest tests/unit_tests/test_add_legacy_agent_environment_servers.py tests/unit_tests/test_global_config.py`: all pass. - `pre-commit run` on the changed files: all pass. ## Rollouts N/A. The added blocks are the relays the runtime already generates for these agents at startup, so a run's servers and routing don't change; only the deprecation warning goes away. ## Compatibility No behavior change for existing runs. Runs with `error_on_agent_without_environment_server: true` now accept these configs. Signed-off-by: Ananth Subramaniam <ansubramania@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?
Since #3724, config validation rejects any agent that no environment server names, with
AgentWithoutEnvironmentServerError. A config written before environment servers therefore stops loading when Gym is upgraded. This also affects configs in this repository:resources_servers/wordle/configs/wordle.yamlandenvironments/wordle/config.yamlfail to parse onmain.This PR turns that error into a deprecation, so these configs keep running while users migrate. It also fixes the migration script so it can update configs outside this repository.
What happens to an agent without an environment server
During config parsing, Gym finds each agent that no environment server names. For each one, it adds a
legacy_agentenvironment server in front of the agent. Alegacy_agentserver relays/runto its agent unchanged, so rollouts and results are the same as before. Gym then logs one deprecation warning:The generated server is named the way the migration script names it: the agent type is stripped from the agent name. For example,
workplace_assistant_simple_agentgetsworkplace_assistant_environment_server. The block in the warning is therefore the block the script writes. If that name is already taken, the server is named<agent>_environment_serverinstead.Gym only adds a server for an agent that has none. An agent with an existing environment server is left alone, so the new server can never make agent routing ambiguous.
Setting
error_on_agent_without_environment_server: truerestores the error. It follows the existingerror_on_almost_serversswitch. The error message now also points at the migration script.Renaming an agent with
_inherit_frommoves it to the new name. An environment server that named the old agent is then left pointing at an agent that no longer exists, and parsing fails withServerRefNotFoundError. That error now says the environment server must be renamed with the agent, and points at the script.Follow-up: feat(config): support environment servers that front several agents #4053 changes this hint to say the environment server must reference the agent's new name, and shows it for any agent field of an environment server.
The test this PR replaces explained why the error existed: without it, a config written before environment servers "would otherwise run, silently dispatching straight to the agent." The generated server keeps that guarantee. Rollout collection still reaches the agent only through an environment server, and the warning names every server Gym added.
Fixes to
scripts/add_legacy_agent_environment_servers.pyThe script now accepts config files and directories as arguments. Before, it could only update this repository's configs, so users had no way to migrate their own. Run without arguments, it still updates this repository's configs.
The script now skips an agent that any environment server already names. Before, it only checked whether a server with the generated name existed. In
benchmarks/swebench/pro/hermes.yaml, asingle_agent_turn_legacyserver already fronts the agent under a different name, so the script added a second server. Two servers in front of one agent make agent routing ambiguous, and rollout collection then fails.The script now handles an agent that only renames another one, such as
renamed_agent: {_inherit_from: some_agent}. Before, such configs were skipped because the new instance has no agent body of its own. The script now writes a server for the new name that inherits the original agent's server. Because_inherit_frommoves the original server, the renamed agent ends up with exactly one server.Follow-up: feat(config): support environment servers that front several agents #4053 replaces this. When the source agent's server is declared, the script now points that server at the new name in place, so server names and
environment_server_routesdo not change. feat(config): support environment servers that front several agents #4053 also recognizes environment servers that front several agents, from feat(nemo-user-sim): add materialized-row runtime #3800. cp:feat(config): deprecate agents without an environment server and fix the migration script (3929)intor0.7.0#4050 carries this PR and feat(config): support environment servers that front several agents #4053 tor0.7.0.A config that cannot be read or parsed is now reported, and the script exits with 2. Before, such files were skipped without any message.
test_langchain_deepagents_configs_have_environment_serversnow parses witherror_on_agent_without_environment_server: true. The test checks that in-repo configs declare their environment server. A generated server would have the same name, so without strict mode the test would pass even when the declaration is missing.The script's
--checkmode reports six configs onmainthat declare no environment server: twowordleconfigs, twoswemer_omlconfigs, and twoswe_internal_v1configs. With this PR they load with the deprecation warning. Migrating them is left to their owners.No issue is linked. This is a compatibility follow-up to #3724.
Validation
pytest tests/unit_tests/test_global_config.py, covering the generated server, its name, the warning text, strict mode, an agent that already has a server, a name collision, a fullparse()of a config without environment servers, and the rename hint onServerRefNotFoundError.pytest tests/unit_tests/test_add_legacy_agent_environment_servers.py(new), covering configs outside the repository,--checknot writing, an agent that already has a server, a rename through_inherit_from, an unreadable config, and that the script and Gym name servers the same way. The behavior tests fail against the script onmain._inherit_fromrenames. Each migrated config was then parsed withGlobalConfigDictParseranderror_on_agent_without_environment_server: true. An unmigrated copy, parsed the same way, raisedAgentWithoutEnvironmentServerError.pytest tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_environment_scaffold.py.pytest tests/unit_tests. The tests that fail in this environment fail identically onmain. They need thesandboxextra, ormlflowandwandb, which (fix) Move mlflow/wandb out of core deps and into extras #3547 moved into extras.pre-commit run --all-files.Rollouts
example_single_tool_callwas run with its environment server block removed from the config, againstQwen/Qwen3-0.6Bserved by a local vLLM with the hermes tool parser:gym env startlogged the deprecation warning and started the generated environment server alongside the agent, the resources server, and the model server.gym eval run --no-servecompleted every requested rollout without failures. Each record has the generated server in_ng_environment_serverandlegacy_agentin_ng_result_type.get_weather, the verifier scored the rollout 1.0. When it answered in prose without calling the tool, the verifier scored it 0.0.Dispatch through the generated server has not changed since that run. Only the server's name and the warning text have.
Compatibility and benchmark impact
AgentWithoutEnvironmentServerErrornow load and log a deprecation warning. Results do not change, becauselegacy_agentrelays/runto the agent unchanged.error_on_agent_without_environment_server: true. The warning tells users that a future release will make this the default.Proposed labels:
feature,area:config.Checklist
pre-commit run --all-files) (so CI lint/format checks pass).git commit -s) (so the DCO check passes).