Repository navigation
feat(cli): make built-in assets work from a wheel install / external cwd - #1805
Merged
Merged
Conversation
This was referenced Jun 29, 2026
…rk from any cwd A wheel install shipped almost no built-in assets and resolved component names only against the cwd, so `gym list environments` was empty and `gym env start --resources-server <name>` failed from any external directory (epic #1205, criteria C2/C5; nvbug 6383277). Packaging (pyproject.toml): - add `environments` to packages.find - ship every config.yaml, README, prepare.py, and small data/example*.jsonl across the asset trees via package-data globs; bulk train/validation JSONL is still fetched on demand via a dataset `source:` / `--download` - require setuptools>=64 for recursive `**` package-data globs Resolution (nemo_gym/cli/main.py): - `_asset_config_path` now searches the install root (PARENT_DIR) in addition to the cwd / --search-dir roots, deduped by resolved path. PARENT_DIR is the install root in both editable and wheel layouts, so built-ins resolve by name regardless of cwd. Editable-from-repo dedupes to a single root (no change). Verified: wheel ships 12/12 environments + configs + example data (0 bulk JSONL); from an external cwd a wheel install discovers all 12 environments and resolves `--environment blackjack` to the install-root config. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
wprazuch
force-pushed
the
wprazuch/gym-install-packaging
branch
from
June 29, 2026 10:10
9b9dba8 to
29c8452
Compare
…ackage-data globs - explain why the floor is bumped: recursive `**` package-data globs need setuptools>=62.3 (pinned 64 for margin); build-time only, not a runtime constraint - collapse the per-tree package-data entries (which repeated the same `**/*.yaml`/`**/*.yml`/`**/README.md` globs) into a single "*" all-packages key Wheel contents verified unchanged: 12 environments, 175 resources-server configs, 254 example JSONL, 0 train/validation JSONL. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
…le-finder The package-data globs added earlier were a no-op: built-in assets ship into the wheel via the setuptools-scm VCS file-finder (git-tracked files), not package-data. Verified by building with the globs removed — the wheel still ships all 12 environment configs, 254 example JSONL, and 175 resources-server configs, and a clean-venv wheel install runs `gym list environments` (12) and `gym env validate` from an unrelated cwd. - remove the package-data globs (incl. the "*" key, which setuptools does not honor as an all-packages key anyway — that is the empty-string key) and the `setuptools>=64` bump they needed for recursive `**`; revert floor to >=61 - the real fix remains adding `environments` to packages.find - document that assets ship via packages.find + the VCS file-finder, and that bulk train/validation JSONL is excluded because it is not committed Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
wprazuch
added a commit
that referenced
this pull request
Jun 29, 2026
Follow-up to the packaging/install-root work (#1805). Relative dataset and prompt-config paths now resolve against the cwd first, then the Gym install root, so built-in files referenced by their repo-relative path (e.g. `resources_servers/<env>/data/example.jsonl`) work from any cwd, including a wheel install (epic #1205, criterion C5; nvbug 6383277). - add `nemo_gym.resolve_under_cwd_or_install(path)`: a shared read-path resolver (absolute as-is; else cwd, then install root), mirroring config_paths resolution - prompt.py: `--prompt-config` now resolves cwd-first then install root (was install-root only, so a user's own relative prompt config was never found) - train_data_utils: dataset existence-check and collation reads use the resolver so a bundled example dataset resolves from an external cwd; metrics-write paths intentionally stay cwd-relative (never write into the install root) Tests: TestResolveUnderCwdOrInstall (absolute / cwd-preferred / install-fallback / missing) plus a cwd-first prompt-config test. Deferred to a follow-up (gym env test from an external cwd: test_all globs + setup_command PYTHONPATH) — that path is # pragma: no cover and needs reworking the test-execution resolution, so it rides with the gym env test/validate work. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
marta-sd
approved these changes
Jun 29, 2026
ko3n1g
approved these changes
Jun 29, 2026
wprazuch
added a commit
that referenced
this pull request
Jun 29, 2026
Follow-up to the packaging/install-root work (#1805). Relative dataset and prompt-config paths now resolve against the cwd first, then the Gym install root, so built-in files referenced by their repo-relative path (e.g. `resources_servers/<env>/data/example.jsonl`) work from any cwd, including a wheel install (epic #1205, criterion C5; nvbug 6383277). - add `nemo_gym.resolve_under_cwd_or_install(path)`: a shared read-path resolver (absolute as-is; else cwd, then install root), mirroring config_paths resolution - prompt.py: `--prompt-config` now resolves cwd-first then install root (was install-root only, so a user's own relative prompt config was never found) - train_data_utils: dataset existence-check and collation reads use the resolver so a bundled example dataset resolves from an external cwd; metrics-write paths intentionally stay cwd-relative (never write into the install root) Tests: TestResolveUnderCwdOrInstall (absolute / cwd-preferred / install-fallback / missing) plus a cwd-first prompt-config test. Deferred to a follow-up (gym env test from an external cwd: test_all globs + setup_command PYTHONPATH) — that path is # pragma: no cover and needs reworking the test-execution resolution, so it rides with the gym env test/validate work. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
wprazuch
added a commit
that referenced
this pull request
Jun 29, 2026
## Problem Follow-up to #1805. Relative dataset and prompt-config paths were resolved only against the current working directory (dataset reads) or only against the install root (prompt configs). So built-in files referenced by their repo-relative path didn't resolve from an arbitrary cwd, and a user's own relative `--prompt-config` was never found. Part of epic #1205 (criterion **C5**, external-dependency/path-safety). ## Changes - **`nemo_gym/__init__.py`** — add `resolve_under_cwd_or_install(path)`: a shared **read-path** resolver. Absolute → unchanged; relative → tried under the cwd (the user's project) first, then the Gym install root (`PARENT_DIR`, where built-ins live in both editable and wheel installs). Mirrors `config_paths` resolution. Documented as read-only — never for write targets. - **`nemo_gym/prompt.py`** — `_resolve_path` now resolves **cwd-first then install root** (was install-root only). A user can pass a relative `--prompt-config` from their own project, and built-in prompt YAMLs still resolve by repo-relative path from any cwd. - **`nemo_gym/train_data_utils.py`** — the dataset **existence-check** and **collation read** use the resolver, so a bundled `data/example.jsonl` resolves from an external cwd. The **metrics-write** paths intentionally stay cwd-relative — writing `_metrics.json` into the install root (site-packages) would be wrong. ## Tests - `TestResolveUnderCwdOrInstall`: absolute returned unchanged; cwd preferred over install root; falls back to install root; missing returns the cwd candidate. - `test_relative_path_resolves_to_cwd_first` for prompt configs. - Updated one `train_data_utils` mock that matched the old relative-string `open()` argument. ruff clean; prompt + train_data_utils + cli_main + registry suites pass (only the 3 pre-existing `TestDidYouMean` argparse failures remain, unrelated). ## Scope / follow-up Complements #1805 (which packages built-in assets incl. example data into the wheel). The `gym env test`-from-an-external-cwd plumbing (`test_all` globs + `setup_command` PYTHONPATH) is **intentionally deferred** — that path is `# pragma: no cover` and requires reworking the test-execution resolution (`TestConfig` asserts a relative entrypoint; `_test_single` does `cd {dir_path}` relative to cwd), so it rides with the `gym env test`/validate hardening work. --------- Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
wprazuch
added a commit
that referenced
this pull request
Jun 29, 2026
Resolve PR #1673 conflicts with main (now includes #1804/#1805/#1806). The gym env compose work coexists with #1805's install-root _asset_config_path and the agent asset selector in nemo_gym/cli/main.py; both `env compose` and `env validate` remain registered. Auto-merge verified: no conflict markers, 167 unit tests pass (only the pre-existing TestDidYouMean argparse failures), and `gym env compose --benchmark gsm8k --agent simple_agent` + `gym env validate` both work. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
wprazuch
added a commit
that referenced
this pull request
Jun 29, 2026
…ition (RFC M3) Compose a benchmark with a swapped agent harness and/or edited benchmark-dataset params, without writing a bespoke per-combination config (epic #1205, criterion C3): - nemo_gym/config_composer.py: compose(), substitute_agent() (wires the env's resources_server/model_server/datasets into the swapped agent, filling a generic agent's mandatory resources_server.name), find_agent_block_key, substitute_dataset_params, _validate_no_mandatory_placeholders (scans for remaining ??? WITHOUT resolving interpolations), and a composability guard (self-contained Pattern B agents rejected). - gym env compose command + an agent asset selector in the unified CLI. - docs: document gym env compose in the CLI reference. Rebased onto current main (composes with #1805/#1806/#1807): #1807's env.py install-root resolution + validate are preserved; gym env compose and gym env validate coexist as separate commands. Verified: compose --benchmark gsm8k --agent simple_agent wires resources_server; validate works; env-test resolution intact. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
wprazuch
added a commit
that referenced
this pull request
Jun 29, 2026
…ition (RFC M3) Compose a benchmark with a swapped agent harness and/or edited benchmark-dataset params, without writing a bespoke per-combination config (epic #1205, criterion C3): - nemo_gym/config_composer.py: compose(), substitute_agent() (wires the env's resources_server/model_server/datasets into the swapped agent, filling a generic agent's mandatory resources_server.name), find_agent_block_key, substitute_dataset_params, _validate_no_mandatory_placeholders (scans for remaining ??? WITHOUT resolving interpolations), and a composability guard (Pattern B rejected). - gym env compose command + an agent asset selector in the unified CLI. - docs: document gym env compose in the CLI reference. Rebased onto current main (composes with #1805/#1806/#1807): #1807's env.py install-root resolution + validate preserved; gym env compose and gym env validate coexist. Verified: compose --benchmark gsm8k --agent simple_agent wires resources_server; validate works; env-test resolution intact. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
wprazuch
added a commit
that referenced
this pull request
Jun 29, 2026
…ition (RFC M3) Compose a benchmark with a swapped agent harness and/or edited benchmark-dataset params, without writing a bespoke per-combination config (epic #1205, criterion C3): - nemo_gym/config_composer.py: compose(), substitute_agent() (wires the env's resources_server/model_server/datasets into the swapped agent, filling a generic agent's mandatory resources_server.name), find_agent_block_key, substitute_dataset_params, _validate_no_mandatory_placeholders (scans for remaining ??? WITHOUT resolving interpolations), and a composability guard (Pattern B rejected). - gym env compose command + an agent asset selector in the unified CLI. - docs: document gym env compose in the CLI reference. Rebased onto current main (composes with #1805/#1806/#1807): #1807's env.py install-root resolution + validate preserved; gym env compose and gym env validate coexist. Verified: compose --benchmark gsm8k --agent simple_agent wires resources_server; validate works; env-test resolution intact. Signed-off-by: Wojciech Prazuch <wprazuch@nvidia.com>
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.
Problem
A wheel/PyPI install of NeMo Gym shipped almost no built-in assets and resolved component names only against the current working directory. So from any directory other than a repo checkout:
gym list environmentsreturned nothing (theenvironments/tree wasn't packaged at all).gym env start --resources-server <name>/--environment <name>/--benchmark <name>failed (the install root was never searched).This is the packaging / install-root slice of epic #1205 (criteria C2 run-by-name/no-internal-paths, C5 external-dependency/path-safety).
Root cause
pyproject.toml:packages.findomittedenvironments, and there was nopackage-data/MANIFEST, so the 388config.yaml+ 254data/*.jsonl+ READMEs +prepare.pynever shipped in the wheel (onlynemo_gym/resources/*.pydid).nemo_gym/cli/main.py:_asset_config_pathsearched[WORKING_DIR, *--search-dir], andWORKING_DIR == cwdfor wheel installs — so the install root (PARENT_DIR, i.e. site-packages) was never searched.Changes
Packaging (
pyproject.toml)environments/environments.*topackages.find.package-dataglobs: every*.yaml,README.md,prepare.py, and smalldata/example*.jsonlacrossresources_servers,responses_api_agents,responses_api_models,environments,benchmarks. Bulktrain/validationJSONL is intentionally excluded — those are fetched on demand via a datasetsource:/--download, keeping the wheel lean.setuptools>=64for reliable recursive**package-data globs.Resolution (
nemo_gym/cli/main.py)_asset_config_pathnow searches the install root (PARENT_DIR) in addition to the cwd /--search-dirroots, deduped by resolved path.PARENT_DIRis the install root in both editable (repo) and wheel (site-packages) layouts, so built-ins resolve by name regardless of cwd. Editable-from-repo still dedupes to a single root (no behavior change). A user asset that shadows a built-in of the same name remains an explicit "ambiguous" error (use--config).Tests (
tests/unit_tests/test_cli_main.py)TestInstallRootResolution: built-in resolves from the install root when cwd differs; user-cwd asset resolves; same-name shadow is ambiguous; editable single-root isn't self-ambiguous.Verification
/tmp→discover_environments()returns 12 (was 0) and--environment blackjackresolves tosite-packages/environments/blackjack/config.yaml.ruff check/formatclean;test_cli_main.py/test_registry.py/test_cli.py/test_cli_agents.pypass.Follow-ups (out of scope here)
Other cwd-anchored sites from the audit remain for a follow-up PR:
train_data_utils(jsonl_fpath),prompt.py,cli/env.pytest globs,cli/setup_command.pyPYTHONPATH.